commit dc8ea308503b8d7a0335e12ee813867791e64ee9 Author: wangchunxiang Date: Wed Jan 21 20:50:35 2026 +0800 feat(jsqlparser): 首次提交 diff --git a/.codacy.yml b/.codacy.yml new file mode 100644 index 0000000..d98077f --- /dev/null +++ b/.codacy.yml @@ -0,0 +1,3 @@ +--- +exclude_paths: + - "site/**" diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..5926aa2 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: https://paypal.me/wumpz diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..e79696e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,41 @@ +--- +name: @ SQL Parser Error +about: Create a report to help us improve +title: '[BUG] JSQLParser Version : RDBMS : failing feature description' +labels: 'Parser Error', 'Feature Request', 'Documentation', 'Java API', 'RDBMS support' +assignees: '' + +--- + + + +### Failing SQL Feature: + + +### SQL Example: + + +### Software Information: + + +### Tips: + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..9003b51 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,31 @@ +--- +name: Feature request +about: Suggest an unsupported Statement or Expression +title: "[FEATURE] missing feature description" +labels: '' +assignees: '' + +--- + +### Grammar or Syntax Description +- Brief description of the failing SQL feature and the EBNF +- Example: `WITH ROLLUP` clause is not supported yet + +### SQL Example +- Simplified Query Example, focusing on the failing feature + ```sql + -- Replace with your ACTUAL example + select 1 + from dual + ``` +- Please don't send screen shots + +### Additional context +The used JSQLParser Version (please test the latest SNAPSHOT version before submitting). +State the applicable RDBMS and version +Links to the reference documentation + +### Tips: + diff --git a/.github/ISSUE_TEMPLATE/sql-parser-error.md b/.github/ISSUE_TEMPLATE/sql-parser-error.md new file mode 100644 index 0000000..2a5eaf2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/sql-parser-error.md @@ -0,0 +1,31 @@ +--- +name: SQL Parser Error +about: Report a Parser Error +title: "[BUG] JSQLParser Version : RDBMS : failing feature description" +labels: '' +assignees: '' + +--- + +Always check against the **Latest SNAPSHOT of JSQLParser** and the [Syntax Diagram](https://jsqlparser.github.io/JSqlParser/syntax_snapshot.html) + +### Failing SQL Feature: +- Brief description of the failing SQL feature +- Example: `WITH ROLLUP` can't be parsed + +### SQL Example: +- Simplified Query Example, focusing on the failing feature + ```sql + -- Replace with your ACTUAL example + select 1 + from dual + ``` + +### Software Information: +- JSqlParser version +- Database (e. g. Oracle, MS SQL Server, H2, PostgreSQL, IBM DB2 ) + +### Tips: +Please write in English and avoid Screenshots (as we can't copy and paste content from it). +[Try your example online with the latest JSQLParser](http://jsqlformatter.manticore-projects.com) and share the link in the error report. +Do provide Links or References to the specific Grammar and Syntax you are trying to use. diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 0000000..b9134ea --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,8 @@ +changelog: + categories: + - title: Bugs solved + labels: + - "bug" + - title: Changes and new Features + labels: + - "*" \ No newline at end of file diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml new file mode 100644 index 0000000..67ccc31 --- /dev/null +++ b/.github/workflows/gradle.yml @@ -0,0 +1,35 @@ +# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle + +name: Gradle CI + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +permissions: + contents: read + +jobs: + check: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'temurin' + - name: Build with Gradle + uses: gradle/gradle-build-action@v2.6.0 + with: + arguments: check + env: + ossrhUsername: ${{ secrets.OSSRHPASSWORD }} + ossrhPassword: ${{ secrets.OSSRHUSERNAME }} diff --git a/.github/workflows/gradle_publish.yml b/.github/workflows/gradle_publish.yml new file mode 100644 index 0000000..4e8cbdd --- /dev/null +++ b/.github/workflows/gradle_publish.yml @@ -0,0 +1,37 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle + +name: Gradle publish Snapshot + +on: + workflow_dispatch: + +permissions: + contents: read + +jobs: + publish: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'temurin' + - name: Build with Gradle + uses: gradle/gradle-build-action@v2.6.0 + with: + arguments: publish + # arguments: build check publish + env: + ossrhUsername: ${{ secrets.OSSRHPASSWORD }} + ossrhPassword: ${{ secrets.OSSRHUSERNAME }} diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 0000000..3c265b6 --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,36 @@ +# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: Maven CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + package: + + runs-on: ubuntu-latest + strategy: + matrix: + java: [11] + name: Java ${{ matrix.java }} building ... + + steps: + - uses: actions/checkout@v3 + - name: Set up Java ${{ matrix.java }} + uses: actions/setup-java@v3 + with: + java-version: ${{ matrix.java }} + distribution: 'temurin' + cache: maven + server-id: sonatype-nexus-snapshots + server-username: MAVEN_USERNAME + server-password: MAVEN_PASSWORD + - name: Build with Maven + run: mvn -B package --file pom.xml -DdisableXmlReport=true -Djacoco.skip=true -Dpmd.skip=true + env: + MAVEN_USERNAME: ${{ secrets.OSSRHUSERNAME }} + MAVEN_PASSWORD: ${{ secrets.OSSRHPASSWORD }} diff --git a/.github/workflows/maven_deploy.yml b/.github/workflows/maven_deploy.yml new file mode 100644 index 0000000..740e80c --- /dev/null +++ b/.github/workflows/maven_deploy.yml @@ -0,0 +1,31 @@ +name: Maven deploy snapshot + +on: + push: + branches: [ "master" ] + +jobs: + deploy: + + runs-on: ubuntu-latest + strategy: + matrix: + java: [11] + name: Java ${{ matrix.java }} building ... + + steps: + - uses: actions/checkout@v3 + - name: Set up Java ${{ matrix.java }} + uses: actions/setup-java@v3 + with: + java-version: ${{ matrix.java }} + distribution: 'temurin' + cache: maven + server-id: sonatype-nexus-snapshots + server-username: MAVEN_USERNAME + server-password: MAVEN_PASSWORD + - name: Build with Maven + run: mvn -B deploy --file pom.xml -DdisableXmlReport=true -Djacoco.skip=true -Dpmd.skip=true + env: + MAVEN_USERNAME: ${{ secrets.OSSRHUSERNAME }} + MAVEN_PASSWORD: ${{ secrets.OSSRHPASSWORD }} diff --git a/.github/workflows/sphinx.yml b/.github/workflows/sphinx.yml new file mode 100644 index 0000000..ad8bde1 --- /dev/null +++ b/.github/workflows/sphinx.yml @@ -0,0 +1,34 @@ +name: Sphinx Pages +on: + push: + branches: [ "master" ] + +permissions: write-all +jobs: + docs: + runs-on: ubuntu-latest + steps: + - uses: actions/setup-python@v4 + - name: Install XSLT Processor + run: sudo apt-get install xsltproc sphinx-common + - name: Install dependencies + run: pip install furo myst_parser sphinx-prompt sphinx_substitution_extensions sphinx_issues sphinx_inline_tabs pygments + - name: Checkout project sources + uses: actions/checkout@v2 + with: + ref: master + fetch-depth: 0 + - name: Setup Gradle + uses: gradle/gradle-build-action@v2.4.2 + - name: Run build with Gradle Wrapper + run: FLOATING_TOC=false gradle --no-build-cache clean xmldoc sphinx + - name: Deploy + uses: actions/configure-pages@v2 + - name: Upload artifact + uses: actions/upload-pages-artifact@v1 + with: + # Upload entire repository + path: 'build/sphinx' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..20db77d --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +# Generated by maven +/target +/build + +# Sphinx Theme related stuff, which shall be downloaded separately +/src/site/sphinx/_themes + +# Exclude the Auto-generated Changelog +/src/site/sphinx/changelog.rst +/src/site/sphinx/javadoc_stable.rst +/src/site/sphinx/syntax_stable.rst +/src/site/sphinx/javadoc_snapshot.rst +/src/site/sphinx/syntax_snapshot.rst + +# Generated by javacc-maven-plugin +/bin +/src/net + +# Eclipse configuration files +/.project +/.settings +/.classpath + +#IntelliJ +/.idea +*.iml + +*.jj~ +*.java~ +*.yml~ +/nbproject/ + +/.gradle diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..5819030 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,13 @@ +language: java +jdk: + - openjdk8 + - openjdk11 + +after_success: + - mvn clean cobertura:cobertura coveralls:report + +cache: + directories: + - $HOME/.m2 + - $HOME/.gradle/caches/ + - $HOME/.gradle/wrapper/ \ No newline at end of file diff --git a/LICENSE_APACHEV2 b/LICENSE_APACHEV2 new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/LICENSE_APACHEV2 @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/LICENSE_LGPLV21 b/LICENSE_LGPLV21 new file mode 100644 index 0000000..19e3071 --- /dev/null +++ b/LICENSE_LGPLV21 @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +(This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.) + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random + Hacker. + + {signature of Ty Coon}, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/README.md b/README.md new file mode 100644 index 0000000..d3606bf --- /dev/null +++ b/README.md @@ -0,0 +1,76 @@ +# [JSqlParser 4.9 Website](https://jsqlparser.github.io/JSqlParser) drawing + +![Build Status](https://github.com/JSQLParser/JSqlParser/actions/workflows/maven.yml/badge.svg) +[![Build Status (Legacy)](https://travis-ci.com/JSQLParser/JSqlParser.svg?branch=master)](https://travis-ci.com/JSQLParser/JSqlParser) [![Coverage Status](https://coveralls.io/repos/JSQLParser/JSqlParser/badge.svg?branch=master)](https://coveralls.io/r/JSQLParser/JSqlParser?branch=master) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/6f9a2d7eb98f45969749e101322634a1)](https://www.codacy.com/gh/JSQLParser/JSqlParser/dashboard?utm_source=github.com&utm_medium=referral&utm_content=JSQLParser/JSqlParser&utm_campaign=Badge_Grade) +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.jsqlparser/jsqlparser/badge.svg)](http://maven-badges.herokuapp.com/maven-central/com.github.jsqlparser/jsqlparser) [![Javadocs](https://www.javadoc.io/badge/com.github.jsqlparser/jsqlparser.svg)](https://www.javadoc.io/doc/com.github.jsqlparser/jsqlparser) +[![Gitter](https://badges.gitter.im/JSQLParser/JSqlParser.svg)](https://gitter.im/JSQLParser/JSqlParser?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +## Summary + +Please visit the [WebSite](https://jsqlparser.github.io/JSqlParser). **JSqlParser** is a RDBMS agnostic SQL statement parser. It translates SQL statements into a traversable hierarchy of Java classes (see [Samples](https://jsqlparser.github.io/JSqlParser/usage.html#parse-a-sql-statements)): + +```sql +SELECT 1 FROM dual WHERE a = b +``` + +```text +SQL Text + └─Statements: statement.select.PlainSelect + ├─selectItems: statement.select.SelectItem + │ └─LongValue: 1 + ├─Table: dual + └─where: expression.operators.relational.EqualsTo + ├─Column: a + └─Column: b +``` + +```java +String sqlStr = "select 1 from dual where a=b"; + +PlainSelect select = (PlainSelect) CCJSqlParserUtil.parse(sqlStr); + +SelectItem selectItem = + select.getSelectItems().get(0); +Assertions.assertEquals( + new LongValue(1) + , selectItem.getExpression()); + +Table table = (Table) select.getFromItem(); +Assertions.assertEquals("dual", table.getName()); + +EqualsTo equalsTo = (EqualsTo) select.getWhere(); +Column a = (Column) equalsTo.getLeftExpression(); +Column b = (Column) equalsTo.getRightExpression(); +Assertions.assertEquals("a", a.getColumnName()); +Assertions.assertEquals("b", b.getColumnName()); +} +``` + +JSQLParser-4.9 is the last JDK8 compatible version. The upcoming JSQLParser-5.0 will depend on JDK11 and introduces API breaking changes to the AST Visitors. Please see the Migration Guide for the details. + +## [Supported Grammar and Syntax](https://jsqlparser.github.io/JSqlParser/syntax.html) + +**JSqlParser** aims to support the SQL standard as well as all major RDBMS. Any missing syntax or features can be added on demand. + +| RDBMS | Statements | +|-----------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------| +| Oracle
MS SQL Server and Sybase
Postgres
MySQL and MariaDB
DB2
H2 and HSQLDB and Derby
SQLite | `SELECT`
`INSERT`, `UPDATE`, `UPSERT`, `MERGE`
`DELETE`, `TRUNCATE TABLE`
`CREATE ...`, `ALTER ....`, `DROP ...`
`WITH ...` | +| Salesforce SOQL | `INCLUDES`, `EXCLUDES` | + +**JSqlParser** can also be used to create SQL Statements from Java Code with a fluent API (see [Samples](https://jsqlparser.github.io/JSqlParser/usage.html#build-a-sql-statements)). + +## Alternatives to JSqlParser? +[**General SQL Parser**](http://www.sqlparser.com/features/introduce.php?utm_source=github-jsqlparser&utm_medium=text-general) looks pretty good, with extended SQL syntax (like PL/SQL and T-SQL) and java + .NET APIs. The tool is commercial (license available online), with a free download option. + +Alternatively the dual-licensed [JOOQ](https://www.jooq.org/doc/latest/manual/sql-building/sql-parser/) provides a hand-written Parser supporting a lot of RDBMS, translation between dialects, SQL transformation, can be used as a JDBC proxy for translation and transformation purposes. + +## [Documentation](https://jsqlparser.github.io/JSqlParser) + 1. [Samples](https://jsqlparser.github.io/JSqlParser/usage.html#parse-a-sql-statements) + 2. [Build Instructions](https://jsqlparser.github.io/JSqlParser/usage.html) and [Maven Artifact](https://jsqlparser.github.io/JSqlParser/usage.html#build-dependencies) + 3. [Contribution](https://jsqlparser.github.io/JSqlParser/contribution.html) + 4. [Change Log](https://jsqlparser.github.io/JSqlParser/changelog.html#latest-changes-since-jsqlparser-version) + 5. [Issues](https://github.com/JSQLParser/JSqlParser/issues) + +## License + +**JSqlParser** is dual licensed under **LGPL V2.1** or **Apache Software License, Version 2.0**. diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..1444799 --- /dev/null +++ b/build.gradle @@ -0,0 +1,610 @@ +import se.bjurr.gitchangelog.plugin.gradle.GitChangelogTask + +plugins { + id 'java' + id 'maven-publish' + id 'signing' + + id "org.javacc.javacc" version "latest.release" + id 'jacoco' + id 'com.github.kt3k.coveralls' version "latest.release" + id "com.github.spotbugs" version "latest.release" + id "com.diffplug.spotless" version "latest.release" + id 'pmd' + id 'checkstyle' + + // download the RR tools which have no Maven Repository + id "de.undercouch.download" version "latest.release" + id 'org.hidetake.ssh' version "latest.release" + + id "se.bjurr.gitchangelog.git-changelog-gradle-plugin" version "latest.release" + id "me.champeau.jmh" version "latest.release" + id "com.nwalsh.gradle.saxon.saxon-gradle" version "0.9.6" +} + +def getVersion = { boolean considerSnapshot -> + Integer major = 0 + Integer minor = 0 + Integer patch = null + Integer build = null + def commit = null + def snapshot = "" + new ByteArrayOutputStream().withStream { os -> + exec { + args = [ + "--no-pager" + , "describe" + , "--tags" + , "--always" + , "--dirty=-SNAPSHOT" + ] + executable "git" + standardOutput = os + } + def versionStr = os.toString().trim() + def pattern = /(?\d*)\.(?\d*)(\.(?\d*))?(-(?\d*)-(?[a-zA-Z\d]*))?/ + def matcher = versionStr =~ pattern + if (matcher.find()) { + major = matcher.group('major') as Integer + minor = matcher.group('minor') as Integer + patch = matcher.group('patch') as Integer + build = matcher.group('build') as Integer + commit = matcher.group('commit') + } + + if (considerSnapshot && ( versionStr.endsWith('SNAPSHOT') || build!=null) ) { + minor++ + if (patch!=null) patch = 0 + snapshot = "-SNAPSHOT" + } + } + return patch!=null + ? "${major}.${minor}.${patch}${snapshot}" + : "${major}.${minor}${snapshot}" +} + +// for publishing a release, call Gradle with Environment Variable RELEASE: +// RELEASE=true gradle JSQLParser:publish +version = getVersion( !System.getenv("RELEASE") ) +group = 'com.github.jsqlparser' +description = 'JSQLParser library' +archivesBaseName = "JSQLParser" + +repositories { + gradlePluginPortal() + mavenLocal() + mavenCentral() + + // Sonatype OSSRH + maven { + url = uri('https://s01.oss.sonatype.org/content/repositories/snapshots/') + } +} + +configurations { + xmlDoclet +} + +dependencies { + testImplementation 'commons-io:commons-io:2.+' + testImplementation 'org.apache.commons:commons-text:+' + testImplementation 'org.mockito:mockito-core:+' + testImplementation 'org.assertj:assertj-core:+' + testImplementation 'org.hamcrest:hamcrest-core:+' + testImplementation 'org.apache.commons:commons-lang3:+' + testImplementation 'com.h2database:h2:+' + + // for JaCoCo Reports + testImplementation 'org.junit.jupiter:junit-jupiter-api:+' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:+' + testImplementation 'org.junit.jupiter:junit-jupiter-params:+' + + // https://mvnrepository.com/artifact/org.mockito/mockito-junit-jupiter + testImplementation 'org.mockito:mockito-junit-jupiter:+' + + // Performance Benchmark + testImplementation 'org.openjdk.jmh:jmh-core:+' + testImplementation 'org.openjdk.jmh:jmh-generator-annprocess:+' + + // Java Doc in XML Format + xmlDoclet 'com.manticore-projects.tools:xml-doclet:+' + + // enforce latest version of JavaCC + testImplementation 'net.java.dev.javacc:javacc:+' + javacc 'net.java.dev.javacc:javacc:+' +} + +compileJavacc { + arguments = [grammar_encoding: 'UTF-8', static: 'false', java_template_type: 'modern'] +} + +java { + withSourcesJar() + withJavadocJar() + + sourceCompatibility = '11' + targetCompatibility = '11' + // needed for XML-Doclet to work (since Doclet changed again with Java 13) + toolchain { + languageVersion.set(JavaLanguageVersion.of(11)) + } +} + +compileJava { + options.release = 11 +} + +javadoc { + include("build/generated/javacc/net/sf/jsqlparser/parser/*.java" ) + if(JavaVersion.current().isJava9Compatible()) { + options.addBooleanOption('html5', true) + } + options.addBooleanOption("Xdoclint:none", true) +} + +jar { + manifest { + attributes ( + "Automatic-Module-Name": "net.sf.jsqlparser" + ) + } +} + +tasks.register('xmldoc', Javadoc) { + def outFile = reporting.file( + version.endsWith("-SNAPSHOT") + ? "xmlDoclet/javadoc_snapshot.xml" + : "xmlDoclet/javadoc_stable.xml" + ) + + def rstFile = reporting.file( + version.endsWith("-SNAPSHOT") + ? "xmlDoclet/javadoc_snapshot.rst" + : "xmlDoclet/javadoc_stable.rst" + ) + + source = sourceSets.main.allJava + // beware: Gradle deletes this folder automatically and there is no switch-off + destinationDir = reporting.file("xmlDoclet") + options.docletpath = configurations.xmlDoclet.files as List + options.doclet = "com.github.markusbernhardt.xmldoclet.XmlDoclet" + title = "API $version" + options.addBooleanOption("rst", true) + options.addBooleanOption("withFloatingToc", Boolean.parseBoolean(System.getenv().getOrDefault("FLOATING_TOC", "true"))) + options.addStringOption("basePackage", "net.sf.jsqlparser") + options.addStringOption("filename", outFile.getName()) + + dependsOn(compileJava) + doLast { + copy { + from rstFile + into "${projectDir}/src/site/sphinx/" + } + } +} + +test { + environment = [ 'EXPORT_TEST_TO_FILE': 'True' ] + useJUnitPlatform() + + // set heap size for the test JVM(s) + minHeapSize = "128m" + maxHeapSize = "1G" +} + +coveralls { + jacocoReportPath 'build/reports/jacoco/test/jacocoTestReport.xml' +} + +jacocoTestReport { + // Jacoco can't handle the TokenManager class + afterEvaluate { + classDirectories.setFrom(files(classDirectories.files.collect { + fileTree(dir: it, exclude: [ + "**CCJSqlParserTokenManager**" + ]) + })) + } + dependsOn test // tests are required to run before generating the report + reports { + xml.required = false + csv.required = false + html.outputLocation = layout.buildDirectory.dir('reports/jacoco') + } +} +jacocoTestCoverageVerification { + // Jacoco can't handle the TokenManager class + afterEvaluate { + classDirectories.setFrom(files(classDirectories.files.collect { + fileTree(dir: it, exclude: [ + "**CCJSqlParserTokenManager**" + ]) + })) + } + violationRules { + rule { + //element = 'CLASS' + limit { + //@todo: temporarily reduced it 80%, we need to bring that back to 84% accepting the Keywords PR + minimum = 0.50 + } + excludes = [ + 'net.sf.jsqlparser.util.validation.*', + 'net.sf.jsqlparser.**.*Adapter', + 'net.sf.jsqlparser.parser.**' + ] + } + rule { + //element = 'CLASS' + limit { + counter = 'LINE' + value = 'MISSEDCOUNT' + + //@todo: temporarily increased to 7000, we need to bring that down to 5500 after accepting the Keywords PR + maximum = 20000 + } + excludes = [ + 'net.sf.jsqlparser.util.validation.*', + 'net.sf.jsqlparser.**.*Adapter', + 'net.sf.jsqlparser.parser.**' + ] + } +// rule { +// element = 'CLASS' +// limit { +// counter = 'LINE' +// value = 'MISSEDRATIO' +// maximum = 0.3 +// } +// excludes = [ +// 'net.sf.jsqlparser.util.validation.*', +// 'net.sf.jsqlparser.**.*Adapter', +// 'net.sf.jsqlparser.parser.**' +// ] +// } + } +} + +spotbugsMain { + reports { + html { + enabled = true + destination = file("build/reports/spotbugs/main/spotbugs.html") + stylesheet = 'fancy-hist.xsl' + } + } +} + +spotbugs { + // fail only on P1 and without the net.sf.jsqlparser.parser.* + excludeFilter = file("config/spotbugs/spotBugsExcludeFilter.xml") + + // do not run over the test, although we should do that eventually + spotbugsTest.enabled = false +} + +pmd { + consoleOutput = true + sourceSets = [sourceSets.main] + + // clear the ruleset in order to use configured rules only + ruleSets = [] + + //rulesMinimumPriority = 1 + ruleSetFiles = files("config/pmd/ruleset.xml") + + pmdMain { + excludes = [ + "build/generated/*" + ] + } +} + +checkstyle { + sourceSets = [sourceSets.main, sourceSets.test] + configFile = rootProject.file('config/checkstyle/checkstyle.xml') +} + +spotless { + // optional: limit format enforcement to just the files changed by this feature branch + ratchetFrom 'origin/master' + + format 'misc', { + // define the files to apply `misc` to + target '*.rst', '*.md', '.gitignore' + + // define the steps to apply to those files + trimTrailingWhitespace() + indentWithSpaces(4) // or spaces. Takes an integer argument if you don't like 4 + endWithNewline() + } + java { + indentWithSpaces(4) + eclipse().configFile('config/formatter/eclipse-java-google-style.xml') + } +} + +tasks.withType(Checkstyle).configureEach { + reports { + xml.required = false + html.required = true + } + excludes = [ "**/module-info.java" ] +} + +tasks.register('renderRR') { + dependsOn(compileJavacc) + doLast { + // these WAR files have been provided as a courtesy by Gunther Rademacher + // and belong to the RR - Railroad Diagram Generator Project + // https://github.com/GuntherRademacher/rr + // + // Hosting at manticore-projects.com is temporary until a better solution is found + // Please do not use these files without Gunther's permission + download.run { + src 'http://manticore-projects.com/download/convert.war' + dest "$buildDir/rr/convert.war" + overwrite false + onlyIfModified true + } + + download.run { + src 'http://manticore-projects.com/download/rr.war' + dest "$buildDir/rr/rr.war" + overwrite false + onlyIfModified true + tempAndMove true + } + + javaexec { + standardOutput = new FileOutputStream("${buildDir}/rr/JSqlParserCC.ebnf") + main = "-jar" + args = [ + "$buildDir/rr/convert.war", + "$buildDir/generated/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jj" + ] + } + + javaexec { + main = "-jar" + args = [ + "$buildDir/rr/rr.war", + "-noepsilon", + "-color:#4D88FF", + "-offset:0", + "-width:800", + //"-png", + //"-out:${buildDir}/rr/JSqlParserCC.zip", + "-out:${buildDir}/rr/JSqlParserCC.xhtml", + "${buildDir}/rr/JSqlParserCC.ebnf" + ] + } + } +} + +tasks.register('gitChangelogTask', GitChangelogTask) { + fromRepo = file("$projectDir") + file = new File("${projectDir}/src/site/sphinx/changelog.rst") + fromRef = "4.0" + //toRef = "1.1"; + + // switch off the formatter since the indentation matters for Mark-down + // @formatter:off + templateContent =""" +************************ +Changelog +************************ + + +{{#tags}} +{{#ifMatches name "^Unreleased.*"}} +Latest Changes since |JSQLPARSER_VERSION| +{{/ifMatches}} +{{#ifMatches name "^(?!Unreleased).*"}} +Version {{name}} +{{/ifMatches}} +============================================================= + + {{#issues}} + + {{#commits}} + {{#ifMatches messageTitle "^(?!Merge).*"}} + * **{{{messageTitle}}}** + + {{authorName}}, {{commitDate}} + {{/ifMatches}} + {{/commits}} + + {{/issues}} +{{/tags}} +""" + // @formatter:on +} + +tasks.register('updateKeywords', JavaExec) { + group = "Execution" + description = "Run the main class with JavaExecTask" + classpath = sourceSets.main.runtimeClasspath + args = [ + file('src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt').absolutePath + , file('src/site/sphinx/keywords.rst').absolutePath + ] + main("net.sf.jsqlparser.parser.ParserKeywordsUtils") + + dependsOn(compileJava) +} + +xslt { + def outFile = version.endsWith("-SNAPSHOT") + ? file("src/site/sphinx/syntax_snapshot.rst") + : file("src/site/sphinx/syntax_stable.rst") + + dependsOn(renderRR) + stylesheet 'src/main/resources/rr/xhtml2rst.xsl' + + parameters ( + "withFloatingToc": System.getenv().getOrDefault("FLOATING_TOC", "true"), + "isSnapshot": Boolean.toString(version.endsWith("-SNAPSHOT")) + ) + + // Transform every .xml file in the "input" directory. + input "$buildDir/rr/JSqlParserCC.xhtml" + output outFile +} + +tasks.register('sphinx', Exec) { + dependsOn(gitChangelogTask, renderRR, xslt, xmldoc) + + String PROLOG = """ +.. |_| unicode:: U+00A0 + :trim: + +.. |JSQLPARSER_EMAIL| replace:: support@manticore-projects.com +.. |JSQLPARSER_VERSION| replace:: ${getVersion(false)} +.. |JSQLPARSER_SNAPSHOT_VERSION| replace:: ${getVersion(true)} +.. |JSQLPARSER_STABLE_VERSION_LINK| raw:: html + + ${project.name}-${getVersion(false)}.jar + +.. |JSQLPARSER_SNAPSHOT_VERSION_LINK| raw:: html + + ${project.name}-${getVersion(true)}.jar + +""" + + args = [ + "-Dproject=JSQLParser" + , "-Dcopyright=Tobias Warneke, 2022" + , "-Dauthor=Tobias Warneke" + , "-Drelease=${getVersion(false)}" + , "-Drst_prolog=$PROLOG" + , "${projectDir}/src/site/sphinx" + , "${project.buildDir}/sphinx" + ] + + executable "sphinx-build" + + //store the output instead of printing to the console: + standardOutput = new ByteArrayOutputStream() + + //extension method stopTomcat.output() can be used to obtain the output: + ext.output = { + return standardOutput.toString() + } +} + +publish { + dependsOn(check, gitChangelogTask, renderRR, xslt, xmldoc) +} + +publishing { + publications { + mavenJava(MavenPublication) { + artifactId = 'jsqlparser' + + from components.java + versionMapping { + usage('java-api') { + fromResolutionOf('runtimeClasspath') + } + usage('java-runtime') { + fromResolutionResult() + } + } + pom { + name = 'JSQLParser library' + description = 'Parse SQL Statements into Abstract Syntax Trees (AST)' + url = 'https://github.com/JSQLParser/JSqlParser' + licenses { + license { + name = 'GNU Library or Lesser General Public License (LGPL) V2.1' + url = 'http://www.gnu.org/licenses/lgpl-2.1.html' + } + license { + name = 'The Apache Software License, Version 2.0' + url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + developers { + developer { + id = 'twa' + name = 'Tobias Warneke' + email = 't.warneke@gmx.net' + } + developer { + id = 'are' + name = 'Andreas Reichel' + email = 'andreas@manticore-projects.com' + } + } + scm { + connection = 'scm:git:https://github.com/JSQLParser/JSqlParser.git' + developerConnection = 'scm:git:ssh://git@github.com:JSQLParser/JSqlParser.git' + url = 'https://github.com/JSQLParser/JSqlParser.git' + } + } + } + } + repositories { + maven { + name "ossrh" + def releasesRepoUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/" + def snapshotsRepoUrl= "https://oss.sonatype.org/content/repositories/snapshots/" + url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl + credentials { + username = System.getenv("ossrhUsername") + password = System.getenv("ossrhPassword") + } + } + } +} + +signing { + //def signingKey = findProperty("signingKey") + //def signingPassword = findProperty("signingPassword") + //useInMemoryPgpKeys(signingKey, signingPassword) + + // don't sign SNAPSHOTS + if (!version.endsWith('SNAPSHOT')) { + sign publishing.publications.mavenJava + } +} + +tasks.withType(JavaCompile).configureEach { + options.encoding = 'UTF-8' +} + +remotes { + webServer { + host = findProperty("${project.name}.host") + user = findProperty("${project.name}.username") + identity = new File("${System.properties['user.home']}/.ssh/id_rsa") + } +} + +tasks.register('upload') { + doFirst { + if (findProperty("${project.name}.host") == null) { + println( + """ + Property \"${project.name}.host\' not found. + Please define \"${project.name}.host\" in the Gradle configuration (e. g. \$HOME/.gradle/gradle.properties. + """ + ) + } + } + doLast { + ssh.run { + session(remotes.webServer) { + def versionStable = getVersion(false) + execute "mkdir -p download/${project.name}-${versionStable}" + for (File file: fileTree(include:['*.jar'], dir:"${project.buildDir}/libs").collect()) { + put from: file, into: "download/${project.name}-${versionStable}" + } + } + } + } +} +check.dependsOn jacocoTestCoverageVerification +upload.dependsOn(check, assemble, gitChangelogTask, renderRR, xslt, xmldoc) + diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml new file mode 100644 index 0000000..c49a705 --- /dev/null +++ b/config/checkstyle/checkstyle.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/checkstyle/checkstyle_checks.xml b/config/checkstyle/checkstyle_checks.xml new file mode 100644 index 0000000..708f387 --- /dev/null +++ b/config/checkstyle/checkstyle_checks.xml @@ -0,0 +1,879 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/checkstyle/google_checks.xml b/config/checkstyle/google_checks.xml new file mode 100644 index 0000000..153a045 --- /dev/null +++ b/config/checkstyle/google_checks.xml @@ -0,0 +1,358 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/checkstyle/sun_checks.xml b/config/checkstyle/sun_checks.xml new file mode 100644 index 0000000..e5f5367 --- /dev/null +++ b/config/checkstyle/sun_checks.xml @@ -0,0 +1,198 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml new file mode 100644 index 0000000..b5ddce5 --- /dev/null +++ b/config/checkstyle/suppressions.xml @@ -0,0 +1,659 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/formatter/eclipse-java-google-style.xml b/config/formatter/eclipse-java-google-style.xml new file mode 100644 index 0000000..5f9965d --- /dev/null +++ b/config/formatter/eclipse-java-google-style.xml @@ -0,0 +1,337 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/pmd/ruleset.xml b/config/pmd/ruleset.xml new file mode 100644 index 0000000..acca2d6 --- /dev/null +++ b/config/pmd/ruleset.xml @@ -0,0 +1,111 @@ + + + + + + The default ruleset used by the Maven PMD Plugin, when no other ruleset is specified. + It contains the rules of the old (pre PMD 6.0.0) rulesets java-basic, java-empty, java-imports, + java-unnecessary, java-unusedcode. + + This ruleset might be used as a starting point for an own customized ruleset [0]. + + [0] https://pmd.github.io/latest/pmd_userdocs_making_rulesets.html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/spotbugs/spotBugsExcludeFilter.xml b/config/spotbugs/spotBugsExcludeFilter.xml new file mode 100644 index 0000000..14af0c1 --- /dev/null +++ b/config/spotbugs/spotBugsExcludeFilter.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..522c755 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,17 @@ +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1G -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError + +org.gradle.caching=true + +# Modularise your project and enable parallel build +org.gradle.parallel=true + +# Enable configure on demand. +org.gradle.configureondemand=true + +# see https://docs.gradle.org/current/userguide/upgrading_version_8.html#xml_parsing_now_requires_recent_parsers +systemProp.javax.xml.parsers.SAXParserFactory=com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl +systemProp.javax.xml.transform.TransformerFactory=com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl +systemProp.javax.xml.parsers.DocumentBuilderFactory=com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl + diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e644113 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..a441313 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..b740cf1 --- /dev/null +++ b/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..25da30d --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/nb-configuration.xml b/nb-configuration.xml new file mode 100644 index 0000000..2c751ca --- /dev/null +++ b/nb-configuration.xml @@ -0,0 +1,32 @@ + + + + + + none + false + true + LF + false + true + JDK_11 + false + none + 4 + 4 + 4 + 120 + true + project + + diff --git a/nbactions.xml b/nbactions.xml new file mode 100644 index 0000000..6522476 --- /dev/null +++ b/nbactions.xml @@ -0,0 +1,23 @@ + + + + CUSTOM-check-sign + check-sign + + clean + install + + + true + true + + + + CUSTOM-clean deploy + clean deploy + + clean + deploy + + + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..988d59b --- /dev/null +++ b/pom.xml @@ -0,0 +1,665 @@ + + 4.0.0 + com.fantaibao.jsqlparser + jsqlparser + 5.0 + JSQLParser library + 2004 + + JSQLParser + + bundle + https://github.com/JSQLParser/JSqlParser + + + + GNU Library or Lesser General Public License (LGPL) V2.1 + http://www.gnu.org/licenses/lgpl-2.1.html + repo + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + + + net.java.dev.javacc + javacc + [7.0.13,) + test + + + + commons-io + commons-io + [2.15.1,) + test + + + org.junit.jupiter + junit-jupiter + [5.10.2,) + test + + + org.mockito + mockito-core + [5.11.0,) + test + + + org.mockito + mockito-junit-jupiter + [5.11.0,) + test + + + org.assertj + assertj-core + [3.25.3,) + test + + + org.apache.commons + commons-lang3 + [3.14.0,) + test + + + com.h2database + h2 + [2.2.224,) + test + + + + + org.hamcrest + hamcrest-all + 1.3 + test + + + + + + Tobias Warneke + t.warneke@gmx.net + + + + + + + + + + + + + + + + scm:git:https://github.com/JSQLParser/JSqlParser.git + scm:git:ssh://git@github.com:JSQLParser/JSqlParser.git + https://github.com/JSQLParser/JSqlParser.git + jsqlparser-5.0 + + + + GitHub Issues + https://github.com/JSQLParser/JSqlParser/issues + + + + + + + org.codehaus.mojo + exec-maven-plugin + 3.1.0 + + net.sf.jsqlparser.parser.ParserKeywordsUtils + + src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt + src/site/sphinx/keywords.rst + + + + + org.apache.maven.plugins + maven-pmd-plugin + 3.21.2 + + + + ${project.basedir}/config/pmd/ruleset.xml + + + **/*Bean.java + **/generated/*.java + + + target/generated-sources + target/generated-test-sources + + true + + + + pmd + + check + + process-sources + + + + + net.sourceforge.pmd + pmd-core + ${pmdVersion} + + + net.sourceforge.pmd + pmd-java + ${pmdVersion} + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.2.0 + + + add-source + generate-sources + + add-source + + + + ${project.build.directory}/generated-sources/javacc/ + ${project.build.directory}/generated-sources/jjtree/ + + + + + + + maven-compiler-plugin + 3.10.1 + + 11 + 11 + true + ${project.build.sourceEncoding} + true + + + + org.javacc.plugin + javacc-maven-plugin + 3.0.3 + + + + javacc + generate-sources + + jjtree-javacc + + + + + + net.java.dev.javacc + javacc + 7.0.13 + + + + + org.apache.maven.plugins + maven-eclipse-plugin + 2.9 + + + /target/generated-sources/javacc + + + + + org.apache.maven.plugins + maven-resources-plugin + 3.3.0 + + ${project.build.sourceEncoding} + + + + org.codehaus.mojo + license-maven-plugin + 2.0.0 + + false + false + false + dual_lgpl_ap2 + ${project.baseUri}/src/license + + site/sphinx/** + + + + + first + + update-file-header + + process-sources + + + + + org.apache.maven.plugins + maven-release-plugin + + 3.0.0-M7 + + true + false + forked-path + sign-release-artifacts + + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.1 + + + attach-sources + + jar + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.4.1 + + + attach-javadocs + + ${javadoc.opts} + net.sf.jsqlparser.parser + none + + + jar + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.3.0 + + + + net.sf.jsqlparser + + + + + + maven-site-plugin + 3.12.1 + + + attach-descriptor + + attach-descriptor + + + + + en + + + + org.eluder.coveralls + coveralls-maven-plugin + 4.3.0 + + + org.codehaus.mojo + cobertura-maven-plugin + 2.7 + + xml + + + net/sf/jsqlparser/parser/*.class + net/sf/jsqlparser/JSQLParserException.class + + + + + + + org.apache.felix + maven-bundle-plugin + 5.1.8 + true + + + org.apache.maven.plugins + maven-surefire-plugin + 3.2.5 + + false + + false + + --add-opens=java.base/java.lang=ALL-UNNAMED + --add-opens=java.base/java.util=ALL-UNNAMED + + + + + org.jacoco + jacoco-maven-plugin + 0.8.10 + + + + prepare-agent + + + + report + prepare-package + + report + + + + + + com.diffplug.spotless + spotless-maven-plugin + 2.28.0 + + + origin/master + + + + + + *.md + .gitignore + + + + + + true + 4 + + + + + + + + src/main/java/**/*.java + src/test/java/**/*.java + + + + + + + + config/formatter/eclipse-java-google-style.xml + + + + + + + + + org.apache.maven.plugins + maven-shade-plugin + + + package + + shade + + + + + net.sf.jsqlparser + com.fantaibao.jsqlparser + + + + + + + + + + + + + + org.apache.maven.plugins + maven-surefire-report-plugin + 3.0.0-M7 + + ${project.reporting.outputDirectory}/testresults + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.4.1 + + true + 800m + none + + + + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.4.1 + + + org.apache.maven.plugins + maven-jxr-plugin + 3.3.0 + + + + org.codehaus.mojo + findbugs-maven-plugin + 3.0.5 + + + + + + + sign-release-artifacts + + + performRelease + true + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + f22e0543 + + + + + + + + + + check.sources + + + !skipCheckSources + + [11,) + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.3.1 + + + verify-style + process-classes + + check + + + + + true + true + ${project.build.sourceDirectory} + **/module-info.java + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + com.puppycrawl.tools + checkstyle + ${checkStyleVersion} + + + + + + + + + skip.all + + false + + + true + true + true + true + + + + + + UTF-8 + 6.55.0 + 10.14.0 + + + + + + + fantaibao-fantaibao-maven-repository + maven-repository + http://192.168.3.25:18081/nexus/repository/maven-releases/ + + + + JSqlParser parses an SQL statement and translate it into a hierarchy of Java classes. + The generated hierarchy can be navigated using the Visitor Pattern. + diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..78feb00 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,5 @@ +/* + * This file was generated by the Gradle 'init' task. + */ + +rootProject.name = 'JSQLFormatter' diff --git a/src/license/dual_lgpl_ap2/header.txt b/src/license/dual_lgpl_ap2/header.txt new file mode 100644 index 0000000..0dab259 --- /dev/null +++ b/src/license/dual_lgpl_ap2/header.txt @@ -0,0 +1 @@ +Dual licensed under GNU LGPL 2.1 or Apache License 2.0 \ No newline at end of file diff --git a/src/license/dual_lgpl_ap2/license.txt b/src/license/dual_lgpl_ap2/license.txt new file mode 100644 index 0000000..8a2d35b --- /dev/null +++ b/src/license/dual_lgpl_ap2/license.txt @@ -0,0 +1,710 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +(This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.) + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random + Hacker. + + {signature of Ty Coon}, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + +********************************************************************************************* + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/src/license/licenses.properties b/src/license/licenses.properties new file mode 100644 index 0000000..dfdce95 --- /dev/null +++ b/src/license/licenses.properties @@ -0,0 +1,10 @@ +### +# #%L +# JSQLParser library +# %% +# Copyright (C) 2004 - 2019 JSQLParser +# %% +# Dual licensed under GNU LGPL 2.1 or Apache License 2.0 +# #L% +### +dual_lgpl_ap2=Dual License GNU LGPL 2.1 or Apache License 2.0 diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java new file mode 100644 index 0000000..66fe1cb --- /dev/null +++ b/src/main/java/module-info.java @@ -0,0 +1,55 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2024 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +module net.sf.jsqlparser { + requires java.sql; + requires java.logging; + + exports net.sf.jsqlparser; + exports net.sf.jsqlparser.expression; + exports net.sf.jsqlparser.expression.operators.arithmetic; + exports net.sf.jsqlparser.expression.operators.conditional; + exports net.sf.jsqlparser.expression.operators.relational; + exports net.sf.jsqlparser.parser; + exports net.sf.jsqlparser.parser.feature; + exports net.sf.jsqlparser.schema; + exports net.sf.jsqlparser.statement; + exports net.sf.jsqlparser.statement.alter; + exports net.sf.jsqlparser.statement.alter.sequence; + exports net.sf.jsqlparser.statement.analyze; + exports net.sf.jsqlparser.statement.comment; + exports net.sf.jsqlparser.statement.create.function; + exports net.sf.jsqlparser.statement.create.index; + exports net.sf.jsqlparser.statement.create.procedure; + exports net.sf.jsqlparser.statement.create.schema; + exports net.sf.jsqlparser.statement.create.sequence; + exports net.sf.jsqlparser.statement.create.synonym; + exports net.sf.jsqlparser.statement.create.table; + exports net.sf.jsqlparser.statement.create.view; + exports net.sf.jsqlparser.statement.delete; + exports net.sf.jsqlparser.statement.drop; + exports net.sf.jsqlparser.statement.execute; + exports net.sf.jsqlparser.statement.grant; + exports net.sf.jsqlparser.statement.insert; + exports net.sf.jsqlparser.statement.merge; + exports net.sf.jsqlparser.statement.refresh; + exports net.sf.jsqlparser.statement.select; + exports net.sf.jsqlparser.statement.show; + exports net.sf.jsqlparser.statement.truncate; + exports net.sf.jsqlparser.statement.update; + exports net.sf.jsqlparser.statement.upsert; + exports net.sf.jsqlparser.util; + exports net.sf.jsqlparser.util.cnfexpression; + exports net.sf.jsqlparser.util.deparser; + exports net.sf.jsqlparser.util.validation; + exports net.sf.jsqlparser.util.validation.allowedtypes; + exports net.sf.jsqlparser.util.validation.feature; + exports net.sf.jsqlparser.util.validation.metadata; + exports net.sf.jsqlparser.util.validation.validator; +} diff --git a/src/main/java/net/sf/jsqlparser/JSQLParserException.java b/src/main/java/net/sf/jsqlparser/JSQLParserException.java new file mode 100644 index 0000000..d5ac694 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/JSQLParserException.java @@ -0,0 +1,32 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser; + +public class JSQLParserException extends Exception { + + private static final long serialVersionUID = -4200894355696788796L; + + public JSQLParserException() { + super(); + } + + public JSQLParserException(String message, Throwable cause) { + super(message, cause); + } + + public JSQLParserException(String message) { + super(message); + } + + public JSQLParserException(Throwable cause) { + super(cause == null ? null : cause.getMessage(), cause); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/Model.java b/src/main/java/net/sf/jsqlparser/Model.java new file mode 100644 index 0000000..4ad52f4 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/Model.java @@ -0,0 +1,24 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser; + +import java.io.Serializable; + +/** + *

+ * A marker interface for jsqlparser-model-classes. + *

+ *

+ * The datastructure where the sql syntax is represented by a tree consists of {@link Model}'s + *

+ */ +public interface Model extends Serializable { + +} diff --git a/src/main/java/net/sf/jsqlparser/expression/Alias.java b/src/main/java/net/sf/jsqlparser/expression/Alias.java new file mode 100644 index 0000000..cd50e12 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/Alias.java @@ -0,0 +1,126 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import net.sf.jsqlparser.statement.create.table.ColDataType; + +public class Alias implements Serializable { + + private String name; + private boolean useAs = true; + private List aliasColumns; + + public Alias(String name) { + this.name = name; + } + + public Alias(String name, boolean useAs) { + this.name = name; + this.useAs = useAs; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean isUseAs() { + return useAs; + } + + public void setUseAs(boolean useAs) { + this.useAs = useAs; + } + + public List getAliasColumns() { + return aliasColumns; + } + + public void setAliasColumns(List aliasColumns) { + this.aliasColumns = aliasColumns; + } + + @Override + public String toString() { + String alias = (useAs ? " AS " : " ") + name; + + if (aliasColumns != null && !aliasColumns.isEmpty()) { + StringBuilder ac = new StringBuilder(); + for (AliasColumn col : aliasColumns) { + if (ac.length() > 0) { + ac.append(", "); + } + ac.append(col.name); + if (col.colDataType != null) { + ac.append(" ").append(col.colDataType.toString()); + } + } + alias += "(" + ac + ")"; + } + + return alias; + } + + public Alias withName(String name) { + this.setName(name); + return this; + } + + public Alias withUseAs(boolean useAs) { + this.setUseAs(useAs); + return this; + } + + public Alias withAliasColumns(List aliasColumns) { + this.setAliasColumns(aliasColumns); + return this; + } + + public Alias addAliasColumns(AliasColumn... aliasColumns) { + List collection = + Optional.ofNullable(getAliasColumns()).orElseGet(ArrayList::new); + Collections.addAll(collection, aliasColumns); + return this.withAliasColumns(collection); + } + + public Alias addAliasColumns(Collection aliasColumns) { + List collection = + Optional.ofNullable(getAliasColumns()).orElseGet(ArrayList::new); + collection.addAll(aliasColumns); + return this.withAliasColumns(collection); + } + + public static class AliasColumn implements Serializable { + + public final String name; + public final ColDataType colDataType; + + public AliasColumn(String name, ColDataType colDataType) { + Objects.requireNonNull(name); + this.name = name; + this.colDataType = colDataType; + } + + public AliasColumn(String name) { + this(name, null); + } + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/AllValue.java b/src/main/java/net/sf/jsqlparser/expression/AllValue.java new file mode 100644 index 0000000..14f924a --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/AllValue.java @@ -0,0 +1,25 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +public class AllValue extends ASTNodeAccessImpl implements Expression { + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String toString() { + return "ALL"; + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/AnalyticExpression.java b/src/main/java/net/sf/jsqlparser/expression/AnalyticExpression.java new file mode 100644 index 0000000..4cbb4e1 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/AnalyticExpression.java @@ -0,0 +1,452 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; +import net.sf.jsqlparser.statement.select.Limit; +import net.sf.jsqlparser.statement.select.OrderByElement; + +import java.util.List; + +import static java.util.stream.Collectors.joining; + +/** + * Analytic function. The name of the function is variable but the parameters following the special + * analytic function path. e.g. row_number() over (order by test). Additionally, there can be an + * expression for an analytical aggregate like sum(col) or the "all columns" wildcard like count(*). + * + * @author tw + */ +public class AnalyticExpression extends ASTNodeAccessImpl implements Expression { + + + private String name; + private Expression expression; + private Expression offset; + private Expression defaultValue; + private boolean allColumns = false; + private KeepExpression keep = null; + private AnalyticType type = AnalyticType.OVER; + private boolean distinct = false; + private boolean unique = false; + private boolean ignoreNullsOutside = false; // IGNORE NULLS outside function parameters + private Expression filterExpression = null; + private List funcOrderBy = null; + private String windowName = null; // refers to an external window definition (paritionBy, + // orderBy, windowElement) + private WindowDefinition windowDef = new WindowDefinition(); + + private Function.HavingClause havingClause; + + private Function.NullHandling nullHandling = null; + + private Limit limit = null; + + public AnalyticExpression() {} + + public AnalyticExpression(Function function) { + this.name = String.join(" ", function.getMultipartName()); + this.allColumns = function.isAllColumns(); + this.distinct = function.isDistinct(); + this.unique = function.isUnique(); + + + ExpressionList list = function.getParameters(); + if (list != null) { + if (list.size() > 3) { + throw new IllegalArgumentException( + "function object not valid to initialize analytic expression"); + } + + expression = list.get(0); + if (list.size() > 1) { + offset = list.get(1); + } + if (list.size() > 2) { + defaultValue = list.get(2); + } + } + this.havingClause = function.getHavingClause(); + this.ignoreNullsOutside = function.isIgnoreNullsOutside(); + this.nullHandling = function.getNullHandling(); + this.funcOrderBy = function.getOrderByElements(); + this.limit = function.getLimit(); + this.keep = function.getKeep(); + } + + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + public List getOrderByElements() { + return windowDef.orderBy.getOrderByElements(); + } + + public void setOrderByElements(List orderByElements) { + windowDef.orderBy.setOrderByElements(orderByElements); + } + + public KeepExpression getKeep() { + return keep; + } + + public void setKeep(KeepExpression keep) { + this.keep = keep; + } + + public ExpressionList getPartitionExpressionList() { + return windowDef.partitionBy.getPartitionExpressionList(); + } + + public void setPartitionExpressionList(ExpressionList partitionExpressionList) { + setPartitionExpressionList(partitionExpressionList, false); + } + + public void setPartitionExpressionList(ExpressionList partitionExpressionList, + boolean brackets) { + windowDef.partitionBy.setPartitionExpressionList(partitionExpressionList, brackets); + } + + public boolean isPartitionByBrackets() { + return windowDef.partitionBy.isBrackets(); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Expression getExpression() { + return expression; + } + + public void setExpression(Expression expression) { + this.expression = expression; + } + + public Expression getOffset() { + return offset; + } + + public void setOffset(Expression offset) { + this.offset = offset; + } + + public Expression getDefaultValue() { + return defaultValue; + } + + public void setDefaultValue(Expression defaultValue) { + this.defaultValue = defaultValue; + } + + public WindowElement getWindowElement() { + return windowDef.windowElement; + } + + public void setWindowElement(WindowElement windowElement) { + windowDef.windowElement = windowElement; + } + + public AnalyticType getType() { + return type; + } + + public void setType(AnalyticType type) { + this.type = type; + } + + public boolean isDistinct() { + return distinct; + } + + public void setDistinct(boolean distinct) { + this.distinct = distinct; + } + + public boolean isUnique() { + return unique; + } + + public void setUnique(boolean unique) { + this.unique = unique; + } + + public boolean isIgnoreNulls() { + return this.nullHandling == Function.NullHandling.IGNORE_NULLS; + } + + public void setIgnoreNulls(boolean ignoreNulls) { + this.nullHandling = ignoreNulls ? Function.NullHandling.IGNORE_NULLS : null; + } + + public boolean isIgnoreNullsOutside() { + return ignoreNullsOutside; + } + + public void setIgnoreNullsOutside(boolean ignoreNullsOutside) { + this.ignoreNullsOutside = ignoreNullsOutside; + } + + public String getWindowName() { + return windowName; + } + + public void setWindowName(String windowName) { + this.windowName = windowName; + } + + public WindowDefinition getWindowDefinition() { + return windowDef; + } + + public void setWindowDefinition(WindowDefinition windowDef) { + this.windowDef = windowDef; + } + + + public Function.HavingClause getHavingClause() { + return havingClause; + } + + public AnalyticExpression setHavingClause(Function.HavingClause havingClause) { + this.havingClause = havingClause; + return this; + } + + public AnalyticExpression setHavingClause(String havingType, Expression expression) { + this.havingClause = new Function.HavingClause( + Function.HavingClause.HavingType.valueOf(havingType.trim().toUpperCase()), + expression); + return this; + } + + public Function.NullHandling getNullHandling() { + return nullHandling; + } + + public AnalyticExpression setNullHandling(Function.NullHandling nullHandling) { + this.nullHandling = nullHandling; + return this; + } + + public Limit getLimit() { + return limit; + } + + public AnalyticExpression setLimit(Limit limit) { + this.limit = limit; + return this; + } + + @Override + @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity", + "PMD.MissingBreakInSwitch"}) + public String toString() { + StringBuilder b = new StringBuilder(); + + b.append(name).append("("); + if (isDistinct()) { + b.append("DISTINCT "); + } + if (expression != null) { + b.append(expression); + if (offset != null) { + b.append(", ").append(offset); + if (defaultValue != null) { + b.append(", ").append(defaultValue); + } + } + } else if (isAllColumns()) { + b.append("*"); + } + + if (havingClause != null) { + havingClause.appendTo(b); + } + + if (nullHandling != null && !ignoreNullsOutside) { + switch (nullHandling) { + case IGNORE_NULLS: + b.append(" IGNORE NULLS"); + break; + case RESPECT_NULLS: + b.append(" RESPECT NULLS"); + break; + } + } + + if (funcOrderBy != null) { + b.append(" ORDER BY "); + b.append(funcOrderBy.stream().map(OrderByElement::toString).collect(joining(", "))); + } + + if (limit != null) { + b.append(limit); + } + + b.append(") "); + if (keep != null) { + b.append(keep).append(" "); + } + + if (filterExpression != null) { + b.append("FILTER (WHERE "); + b.append(filterExpression); + b.append(")"); + if (type != AnalyticType.FILTER_ONLY) { + b.append(" "); + } + } + + if (nullHandling != null && ignoreNullsOutside) { + switch (nullHandling) { + case IGNORE_NULLS: + b.append(" IGNORE NULLS "); + break; + case RESPECT_NULLS: + b.append(" RESPECT NULLS "); + break; + } + } + + switch (type) { + case FILTER_ONLY: + return b.toString(); + case WITHIN_GROUP: + b.append("WITHIN GROUP"); + break; + case WITHIN_GROUP_OVER: + b.append("WITHIN GROUP ("); + windowDef.orderBy.toStringOrderByElements(b); + b.append(") OVER ("); + windowDef.partitionBy.toStringPartitionBy(b); + b.append(")"); + break; + default: + b.append("OVER"); + } + + if (windowName != null) { + b.append(" ").append(windowName); + } else if (type != AnalyticType.WITHIN_GROUP_OVER) { + b.append(" "); + b.append(windowDef.toString()); + } + + return b.toString(); + } + + public boolean isAllColumns() { + return allColumns; + } + + public void setAllColumns(boolean allColumns) { + this.allColumns = allColumns; + } + + public Expression getFilterExpression() { + return filterExpression; + } + + public void setFilterExpression(Expression filterExpression) { + this.filterExpression = filterExpression; + } + + public AnalyticExpression withName(String name) { + this.setName(name); + return this; + } + + public AnalyticExpression withExpression(Expression expression) { + this.setExpression(expression); + return this; + } + + public AnalyticExpression withOffset(Expression offset) { + this.setOffset(offset); + return this; + } + + public AnalyticExpression withDefaultValue(Expression defaultValue) { + this.setDefaultValue(defaultValue); + return this; + } + + public AnalyticExpression withAllColumns(boolean allColumns) { + this.setAllColumns(allColumns); + return this; + } + + public AnalyticExpression withKeep(KeepExpression keep) { + this.setKeep(keep); + return this; + } + + public AnalyticExpression withType(AnalyticType type) { + this.setType(type); + return this; + } + + public AnalyticExpression withDistinct(boolean distinct) { + this.setDistinct(distinct); + return this; + } + + public AnalyticExpression withUnique(boolean unique) { + this.setUnique(unique); + return this; + } + + public AnalyticExpression withIgnoreNulls(boolean ignoreNulls) { + this.setIgnoreNulls(ignoreNulls); + return this; + } + + public AnalyticExpression withFilterExpression(Expression filterExpression) { + this.setFilterExpression(filterExpression); + return this; + } + + public AnalyticExpression withWindowElement(WindowElement windowElement) { + this.setWindowElement(windowElement); + return this; + } + + public E getExpression(Class type) { + return type.cast(getExpression()); + } + + public E getOffset(Class type) { + return type.cast(getOffset()); + } + + public E getDefaultValue(Class type) { + return type.cast(getDefaultValue()); + } + + public E getFilterExpression(Class type) { + return type.cast(getFilterExpression()); + } + + public List getFuncOrderBy() { + return funcOrderBy; + } + + public void setFuncOrderBy(List funcOrderBy) { + this.funcOrderBy = funcOrderBy; + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/AnalyticType.java b/src/main/java/net/sf/jsqlparser/expression/AnalyticType.java new file mode 100644 index 0000000..2738849 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/AnalyticType.java @@ -0,0 +1,18 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +public enum AnalyticType { + OVER, WITHIN_GROUP, WITHIN_GROUP_OVER, FILTER_ONLY; + + public static AnalyticType from(String type) { + return Enum.valueOf(AnalyticType.class, type.toUpperCase()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/AnyComparisonExpression.java b/src/main/java/net/sf/jsqlparser/expression/AnyComparisonExpression.java new file mode 100644 index 0000000..cf3ba46 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/AnyComparisonExpression.java @@ -0,0 +1,48 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; +import net.sf.jsqlparser.statement.select.Select; + +/** + * Combines ANY and SOME expressions. + * + * @author toben + */ +public class AnyComparisonExpression extends ASTNodeAccessImpl implements Expression { + private final Select select; + private final AnyType anyType; + + public AnyComparisonExpression(AnyType anyType, Select select) { + this.anyType = anyType; + this.select = select; + } + + public Select getSelect() { + return select; + } + + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + public AnyType getAnyType() { + return anyType; + } + + @Override + public String toString() { + String s = anyType.name() + select; + return s; + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/AnyType.java b/src/main/java/net/sf/jsqlparser/expression/AnyType.java new file mode 100644 index 0000000..fec6fb0 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/AnyType.java @@ -0,0 +1,18 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +public enum AnyType { + ANY, SOME, ALL; + + public static AnyType from(String type) { + return Enum.valueOf(AnyType.class, type.toUpperCase()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/ArrayConstructor.java b/src/main/java/net/sf/jsqlparser/expression/ArrayConstructor.java new file mode 100644 index 0000000..c7fe031 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/ArrayConstructor.java @@ -0,0 +1,60 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2021 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +public class ArrayConstructor extends ASTNodeAccessImpl implements Expression { + private ExpressionList expressions; + private boolean arrayKeyword; + + public ArrayConstructor(ExpressionList expressions, boolean arrayKeyword) { + this.expressions = expressions; + this.arrayKeyword = arrayKeyword; + } + + public ArrayConstructor(Expression... expressions) { + this(new ExpressionList(expressions), false); + } + + public ExpressionList getExpressions() { + return expressions; + } + + public void setExpressions(ExpressionList expressions) { + this.expressions = expressions; + } + + public boolean isArrayKeyword() { + return arrayKeyword; + } + + public void setArrayKeyword(boolean arrayKeyword) { + this.arrayKeyword = arrayKeyword; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + if (arrayKeyword) { + sb.append("ARRAY"); + } + sb.append("["); + sb.append(expressions.toString()); + sb.append("]"); + return sb.toString(); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/ArrayExpression.java b/src/main/java/net/sf/jsqlparser/expression/ArrayExpression.java new file mode 100644 index 0000000..e86f34c --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/ArrayExpression.java @@ -0,0 +1,117 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +public class ArrayExpression extends ASTNodeAccessImpl implements Expression { + + private Expression objExpression; + private Expression indexExpression; + private Expression startIndexExpression; + private Expression stopIndexExpression; + + + public ArrayExpression() { + // empty constructor + } + + public ArrayExpression(Expression objExpression, Expression indexExpression, + Expression startIndexExpression, Expression stopIndexExpression) { + this.objExpression = objExpression; + this.indexExpression = indexExpression; + this.startIndexExpression = startIndexExpression; + this.stopIndexExpression = stopIndexExpression; + } + + public ArrayExpression(Expression objExpression, Expression indexExpression) { + this(objExpression, indexExpression, null, null); + } + + public ArrayExpression(Expression objExpression, Expression startIndexExpression, + Expression stopIndexExpression) { + this(objExpression, null, startIndexExpression, stopIndexExpression); + } + + public Expression getObjExpression() { + return objExpression; + } + + public void setObjExpression(Expression objExpression) { + this.objExpression = objExpression; + } + + public Expression getIndexExpression() { + return indexExpression; + } + + public void setIndexExpression(Expression indexExpression) { + this.indexExpression = indexExpression; + } + + public Expression getStartIndexExpression() { + return startIndexExpression; + } + + public void setStartIndexExpression(Expression startIndexExpression) { + this.startIndexExpression = startIndexExpression; + } + + public Expression getStopIndexExpression() { + return stopIndexExpression; + } + + public void setStopIndexExpression(Expression stopIndexExpression) { + this.stopIndexExpression = stopIndexExpression; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String toString() { + if (indexExpression != null) { + return objExpression.toString() + "[" + indexExpression.toString() + "]"; + } else { + return objExpression.toString() + "[" + + (startIndexExpression == null ? "" : startIndexExpression.toString()) + + ":" + + (stopIndexExpression == null ? "" : stopIndexExpression.toString()) + + "]"; + } + } + + public ArrayExpression withObjExpression(Expression objExpression) { + this.setObjExpression(objExpression); + return this; + } + + public ArrayExpression withIndexExpression(Expression indexExpression) { + this.setIndexExpression(indexExpression); + return this; + } + + public ArrayExpression withRangeExpression(Expression startIndexExpression, + Expression stopIndexExpression) { + this.setStartIndexExpression(startIndexExpression); + this.setStopIndexExpression(stopIndexExpression); + return this; + } + + public E getObjExpression(Class type) { + return type.cast(getObjExpression()); + } + + public E getIndexExpression(Class type) { + return type.cast(getIndexExpression()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/BinaryExpression.java b/src/main/java/net/sf/jsqlparser/expression/BinaryExpression.java new file mode 100644 index 0000000..ffd9c2d --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/BinaryExpression.java @@ -0,0 +1,266 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.expression.operators.arithmetic.Addition; +import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseAnd; +import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseLeftShift; +import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseOr; +import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseRightShift; +import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseXor; +import net.sf.jsqlparser.expression.operators.arithmetic.Concat; +import net.sf.jsqlparser.expression.operators.arithmetic.Division; +import net.sf.jsqlparser.expression.operators.arithmetic.IntegerDivision; +import net.sf.jsqlparser.expression.operators.arithmetic.Modulo; +import net.sf.jsqlparser.expression.operators.arithmetic.Multiplication; +import net.sf.jsqlparser.expression.operators.arithmetic.Subtraction; +import net.sf.jsqlparser.expression.operators.conditional.AndExpression; +import net.sf.jsqlparser.expression.operators.conditional.OrExpression; +import net.sf.jsqlparser.expression.operators.conditional.XorExpression; +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.Iterator; + +/** + * A basic class for binary expressions, that is expressions having a left member and a right member + * which are in turn expressions. + */ +public abstract class BinaryExpression extends ASTNodeAccessImpl implements Expression { + + private Expression leftExpression; + private Expression rightExpression; + + public BinaryExpression() {} + + public BinaryExpression(Expression leftExpression, Expression rightExpression) { + this.leftExpression = leftExpression; + this.rightExpression = rightExpression; + } + + public static Expression build(Class clz, Expression... expressions) + throws NoSuchMethodException, InvocationTargetException, InstantiationException, + IllegalAccessException { + switch (expressions.length) { + case 0: + return new NullValue(); + case 1: + return expressions[0]; + default: + Iterator it = Arrays.stream(expressions).iterator(); + + Expression leftExpression = it.next(); + Expression rightExpression = it.next(); + BinaryExpression binaryExpression = + clz.getConstructor(Expression.class, Expression.class) + .newInstance(leftExpression, rightExpression); + + while (it.hasNext()) { + rightExpression = it.next(); + binaryExpression = clz.getConstructor(Expression.class, Expression.class) + .newInstance(binaryExpression, rightExpression); + } + return binaryExpression; + } + } + + public static Expression add(Expression... expressions) { + try { + return build(Addition.class, expressions); + } catch (NoSuchMethodException | InvocationTargetException | InstantiationException + | IllegalAccessException e) { + // this should never happen, at least I don't see how + throw new RuntimeException(e); + } + } + + public static Expression bitAnd(Expression... expressions) { + try { + return build(BitwiseAnd.class, expressions); + } catch (NoSuchMethodException | InvocationTargetException | InstantiationException + | IllegalAccessException e) { + // this should never happen, at least I don't see how + throw new RuntimeException(e); + } + } + + public static Expression bitShiftLeft(Expression... expressions) { + try { + return build(BitwiseLeftShift.class, expressions); + } catch (NoSuchMethodException | InvocationTargetException | InstantiationException + | IllegalAccessException e) { + // this should never happen, at least I don't see how + throw new RuntimeException(e); + } + } + + public static Expression multiply(Expression... expressions) { + try { + return build(Multiplication.class, expressions); + } catch (NoSuchMethodException | InvocationTargetException | InstantiationException + | IllegalAccessException e) { + // this should never happen, at least I don't see how + throw new RuntimeException(e); + } + } + + public static Expression bitOr(Expression... expressions) { + try { + return build(BitwiseOr.class, expressions); + } catch (NoSuchMethodException | InvocationTargetException | InstantiationException + | IllegalAccessException e) { + // this should never happen, at least I don't see how + throw new RuntimeException(e); + } + } + + public static Expression bitShiftRight(Expression... expressions) { + try { + return build(BitwiseRightShift.class, expressions); + } catch (NoSuchMethodException | InvocationTargetException | InstantiationException + | IllegalAccessException e) { + // this should never happen, at least I don't see how + throw new RuntimeException(e); + } + } + + public static Expression bitXor(Expression... expressions) { + try { + return build(BitwiseXor.class, expressions); + } catch (NoSuchMethodException | InvocationTargetException | InstantiationException + | IllegalAccessException e) { + // this should never happen, at least I don't see how + throw new RuntimeException(e); + } + } + + public static Expression concat(Expression... expressions) { + try { + return build(Concat.class, expressions); + } catch (NoSuchMethodException | InvocationTargetException | InstantiationException + | IllegalAccessException e) { + // this should never happen, at least I don't see how + throw new RuntimeException(e); + } + } + + public static Expression divide(Expression... expressions) { + try { + return build(Division.class, expressions); + } catch (NoSuchMethodException | InvocationTargetException | InstantiationException + | IllegalAccessException e) { + // this should never happen, at least I don't see how + throw new RuntimeException(e); + } + } + + public static Expression divideInt(Expression... expressions) { + try { + return build(IntegerDivision.class, expressions); + } catch (NoSuchMethodException | InvocationTargetException | InstantiationException + | IllegalAccessException e) { + // this should never happen, at least I don't see how + throw new RuntimeException(e); + } + } + + public static Expression modulo(Expression... expressions) { + try { + return build(Modulo.class, expressions); + } catch (NoSuchMethodException | InvocationTargetException | InstantiationException + | IllegalAccessException e) { + // this should never happen, at least I don't see how + throw new RuntimeException(e); + } + } + + public static Expression subtract(Expression... expressions) { + try { + return build(Subtraction.class, expressions); + } catch (NoSuchMethodException | InvocationTargetException | InstantiationException + | IllegalAccessException e) { + // this should never happen, at least I don't see how + throw new RuntimeException(e); + } + } + + public static Expression or(Expression... expressions) { + try { + return build(OrExpression.class, expressions); + } catch (NoSuchMethodException | InvocationTargetException | InstantiationException + | IllegalAccessException e) { + // this should never happen, at least I don't see how + throw new RuntimeException(e); + } + } + + public static Expression xor(Expression... expressions) { + try { + return build(XorExpression.class, expressions); + } catch (NoSuchMethodException | InvocationTargetException | InstantiationException + | IllegalAccessException e) { + // this should never happen, at least I don't see how + throw new RuntimeException(e); + } + } + + public static Expression and(Expression... expressions) { + try { + return build(AndExpression.class, expressions); + } catch (NoSuchMethodException | InvocationTargetException | InstantiationException + | IllegalAccessException e) { + // this should never happen, at least I don't see how + throw new RuntimeException(e); + } + } + + public Expression getLeftExpression() { + return leftExpression; + } + + public void setLeftExpression(Expression expression) { + leftExpression = expression; + } + + public Expression getRightExpression() { + return rightExpression; + } + + public void setRightExpression(Expression expression) { + rightExpression = expression; + } + + public BinaryExpression withLeftExpression(Expression expression) { + setLeftExpression(expression); + return this; + } + + public BinaryExpression withRightExpression(Expression expression) { + setRightExpression(expression); + return this; + } + + @Override + public String toString() { + return // (not ? "NOT " : "") + + getLeftExpression() + " " + getStringExpression() + " " + getRightExpression(); + } + + public abstract String getStringExpression(); + + public E getLeftExpression(Class type) { + return type.cast(getLeftExpression()); + } + + public E getRightExpression(Class type) { + return type.cast(getRightExpression()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/CaseExpression.java b/src/main/java/net/sf/jsqlparser/expression/CaseExpression.java new file mode 100644 index 0000000..10fd7d5 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/CaseExpression.java @@ -0,0 +1,182 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; +import net.sf.jsqlparser.statement.select.PlainSelect; + +/** + * CASE/WHEN expression. + *

+ * Syntax: + * + *

+ * 
+ * CASE
+ * WHEN condition THEN expression
+ * [WHEN condition THEN expression]...
+ * [ELSE expression]
+ * END
+ * 
+ * 
+ * + *
+ * or
+ *
+ * + *
+ * 
+ * CASE expression
+ * WHEN condition THEN expression
+ * [WHEN condition THEN expression]...
+ * [ELSE expression]
+ * END
+ * 
+ * 
+ */ +public class CaseExpression extends ASTNodeAccessImpl implements Expression { + + private boolean usingBrackets = false; + private Expression switchExpression; + private List whenClauses; + private Expression elseExpression; + + public CaseExpression() {} + + public CaseExpression(WhenClause... whenClauses) { + this.whenClauses = Arrays.asList(whenClauses); + } + + public CaseExpression(Expression elseExpression, WhenClause... whenClauses) { + this.elseExpression = elseExpression; + this.whenClauses = Arrays.asList(whenClauses); + } + + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + public Expression getSwitchExpression() { + return switchExpression; + } + + public void setSwitchExpression(Expression switchExpression) { + this.switchExpression = switchExpression; + } + + /** + * @return Returns the elseExpression. + */ + public Expression getElseExpression() { + return elseExpression; + } + + /** + * @param elseExpression The elseExpression to set. + */ + public void setElseExpression(Expression elseExpression) { + this.elseExpression = elseExpression; + } + + /** + * @return Returns the whenClauses. + */ + public List getWhenClauses() { + return whenClauses; + } + + /** + * @param whenClauses The whenClauses to set. + */ + public void setWhenClauses(List whenClauses) { + this.whenClauses = whenClauses; + } + + @Override + public String toString() { + return (usingBrackets ? "(" : "") + "CASE " + + ((switchExpression != null) ? switchExpression + " " : "") + + PlainSelect.getStringList(whenClauses, false, false) + " " + + ((elseExpression != null) ? "ELSE " + elseExpression + " " : "") + "END" + + (usingBrackets ? ")" : ""); + } + + public CaseExpression withSwitchExpression(Expression switchExpression) { + this.setSwitchExpression(switchExpression); + return this; + } + + public CaseExpression withWhenClauses(WhenClause... whenClauses) { + return this.withWhenClauses(Arrays.asList(whenClauses)); + } + + public CaseExpression withWhenClauses(List whenClauses) { + this.setWhenClauses(whenClauses); + return this; + } + + public CaseExpression withElseExpression(Expression elseExpression) { + this.setElseExpression(elseExpression); + return this; + } + + public CaseExpression addWhenClauses(WhenClause... whenClauses) { + List collection = + Optional.ofNullable(getWhenClauses()).orElseGet(ArrayList::new); + Collections.addAll(collection, whenClauses); + return this.withWhenClauses(collection); + } + + public CaseExpression addWhenClauses(Collection whenClauses) { + List collection = + Optional.ofNullable(getWhenClauses()).orElseGet(ArrayList::new); + collection.addAll(whenClauses); + return this.withWhenClauses(collection); + } + + public E getSwitchExpression(Class type) { + return type.cast(getSwitchExpression()); + } + + public E getElseExpression(Class type) { + return type.cast(getElseExpression()); + } + + /** + * @return the usingBrackets + */ + public boolean isUsingBrackets() { + return usingBrackets; + } + + /** + * @param usingBrackets the usingBrackets to set + */ + public void setUsingBrackets(boolean usingBrackets) { + this.usingBrackets = usingBrackets; + } + + /** + * @param usingBrackets the usingBrackets to set + */ + public CaseExpression withUsingBrackets(boolean usingBrackets) { + this.usingBrackets = usingBrackets; + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/CastExpression.java b/src/main/java/net/sf/jsqlparser/expression/CastExpression.java new file mode 100644 index 0000000..8325e49 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/CastExpression.java @@ -0,0 +1,295 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; +import net.sf.jsqlparser.statement.create.table.ColDataType; +import net.sf.jsqlparser.statement.create.table.ColumnDefinition; +import net.sf.jsqlparser.statement.select.Select; + +import java.util.ArrayList; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class CastExpression extends ASTNodeAccessImpl implements Expression { + private final static Pattern PATTERN = + Pattern.compile("(^[a-z0-9_]*){1}", Pattern.CASE_INSENSITIVE); + public String keyword; + + private Expression leftExpression; + private ColDataType colDataType = null; + private ArrayList columnDefinitions = new ArrayList<>(); + + private boolean isImplicitCast = false; + + // BigQuery specific FORMAT clause: + // https://cloud.google.com/bigquery/docs/reference/standard-sql/conversion_functions#cast_as_date + private String format = null; + + public CastExpression(String keyword, Expression leftExpression, String dataType) { + this.keyword = keyword; + this.leftExpression = leftExpression; + this.colDataType = new ColDataType(dataType); + } + + // Implicit Cast + public CastExpression(String dataType, String value) { + this.keyword = null; + this.isImplicitCast = true; + this.colDataType = new ColDataType(dataType); + this.leftExpression = new StringValue(value); + } + + public CastExpression(ColDataType colDataType, String value) { + this.keyword = null; + this.isImplicitCast = true; + this.colDataType = colDataType; + this.leftExpression = new StringValue(value); + } + + public CastExpression(ColDataType colDataType, Long value) { + this.keyword = null; + this.isImplicitCast = true; + this.colDataType = colDataType; + this.leftExpression = new LongValue(value); + } + + public CastExpression(ColDataType colDataType, Double value) { + this.keyword = null; + this.isImplicitCast = true; + this.colDataType = colDataType; + this.leftExpression = new DoubleValue(value); + } + + public CastExpression(Expression leftExpression, String dataType) { + this.keyword = null; + this.leftExpression = leftExpression; + this.colDataType = new ColDataType(dataType); + } + + + public CastExpression(String keyword) { + this.keyword = keyword; + } + + public CastExpression() { + this("CAST"); + } + + public static boolean isOf(ColDataType colDataType, DataType... types) { + return Set.of(types).contains(DataType.from(colDataType.getDataType())); + } + + public static boolean isTime(ColDataType colDataType) { + return isOf(colDataType, DataType.TIME, DataType.TIME_WITH_TIME_ZONE, + DataType.TIME_WITHOUT_TIME_ZONE); + } + + public static boolean isTimeStamp(ColDataType colDataType) { + return isOf(colDataType, DataType.TIMESTAMP_NS, DataType.TIMESTAMP, + DataType.TIMESTAMP_WITHOUT_TIME_ZONE, + DataType.DATETIME, DataType.TIMESTAMP_MS, DataType.TIMESTAMP_S, + DataType.TIMESTAMPTZ, DataType.TIMESTAMP_WITH_TIME_ZONE); + } + + public static boolean isDate(ColDataType colDataType) { + return isOf(colDataType, DataType.DATE); + } + + public static boolean isBLOB(ColDataType colDataType) { + return isOf(colDataType, DataType.BLOB, DataType.BYTEA, DataType.BINARY, DataType.VARBINARY, + DataType.BYTES, DataType.VARBYTE); + } + + public static boolean isFloat(ColDataType colDataType) { + return isOf(colDataType, DataType.REAL, DataType.FLOAT4, DataType.FLOAT, DataType.DOUBLE, + DataType.DOUBLE_PRECISION, DataType.FLOAT8); + } + + public static boolean isInteger(ColDataType colDataType) { + return isOf(colDataType, DataType.TINYINT, DataType.INT1, DataType.SMALLINT, DataType.INT2, + DataType.SHORT, DataType.INTEGER, DataType.INT4, DataType.INT, DataType.SIGNED, + DataType.BIGINT, DataType.INT8, DataType.LONG, DataType.HUGEINT, DataType.UTINYINT, + DataType.USMALLINT, DataType.UINTEGER, DataType.UBIGINT, DataType.UHUGEINT); + } + + public static boolean isDecimal(ColDataType colDataType) { + return isOf(colDataType, DataType.DECIMAL, DataType.NUMBER, DataType.NUMERIC); + } + + public static boolean isText(ColDataType colDataType) { + return isOf(colDataType, DataType.VARCHAR, DataType.NVARCHAR, DataType.CHAR, DataType.NCHAR, + DataType.BPCHAR, DataType.STRING, DataType.TEXT, DataType.CLOB); + } + + public ColDataType getColDataType() { + return colDataType; + } + + public void setColDataType(ColDataType colDataType) { + this.colDataType = colDataType; + } + + public ArrayList getColumnDefinitions() { + return columnDefinitions; + } + + public void addColumnDefinition(ColumnDefinition columnDefinition) { + this.columnDefinitions.add(columnDefinition); + } + + public Expression getLeftExpression() { + return leftExpression; + } + + public void setLeftExpression(Expression expression) { + leftExpression = expression; + } + + public boolean isImplicitCast() { + return isImplicitCast; + } + + public CastExpression setImplicitCast(boolean implicitCast) { + isImplicitCast = implicitCast; + return this; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Deprecated + public boolean isUseCastKeyword() { + return keyword != null && !keyword.isEmpty(); + } + + @Deprecated + public void setUseCastKeyword(boolean useCastKeyword) { + if (useCastKeyword) { + if (keyword == null || keyword.isEmpty()) { + keyword = "CAST"; + } + } else { + keyword = null; + } + } + + public String getFormat() { + return format; + } + + public CastExpression setFormat(String format) { + this.format = format; + return this; + } + + @Override + public String toString() { + String formatStr = format != null && !format.isEmpty() + ? " FORMAT " + format + : ""; + if (isImplicitCast) { + return colDataType + " " + leftExpression; + } else if (keyword != null && !keyword.isEmpty()) { + return columnDefinitions.size() > 1 + ? keyword + "(" + leftExpression + " AS ROW(" + + Select.getStringList(columnDefinitions) + ")" + formatStr + ")" + : keyword + "(" + leftExpression + " AS " + colDataType.toString() + formatStr + + ")"; + } else { + return leftExpression + "::" + colDataType.toString(); + } + } + + public CastExpression withType(ColDataType type) { + this.setColDataType(type); + return this; + } + + public CastExpression withUseCastKeyword(boolean useCastKeyword) { + this.setUseCastKeyword(useCastKeyword); + return this; + } + + public CastExpression withLeftExpression(Expression leftExpression) { + this.setLeftExpression(leftExpression); + return this; + } + + public E getLeftExpression(Class type) { + return type.cast(getLeftExpression()); + } + + public boolean isOf(CastExpression anotherCast) { + return this.colDataType.equals(anotherCast.colDataType); + } + + public boolean isOf(DataType... types) { + return Set.of(types).contains(DataType.from(colDataType.getDataType())); + } + + public boolean isTime() { + return isTime(this.colDataType); + } + + public boolean isTimeStamp() { + return isTimeStamp(this.colDataType); + } + + public boolean isDate() { + return isDate(this.colDataType); + } + + public boolean isBLOB() { + return isBLOB(this.colDataType); + } + + public boolean isFloat() { + return isFloat(this.colDataType); + } + + public boolean isInteger() { + return isInteger(this.colDataType); + } + + public boolean isDecimal() { + return isDecimal(this.colDataType); + } + + public boolean isText() { + return isText(this.colDataType); + } + + public enum DataType { + ARRAY, BIT, BITSTRING, BLOB, BYTEA, BINARY, VARBINARY, BYTES, BOOLEAN, BOOL, ENUM, INTERVAL, LIST, MAP, STRUCT, TINYINT, INT1, SMALLINT, INT2, SHORT, INTEGER, INT4, INT, SIGNED, BIGINT, INT8, LONG, HUGEINT, UTINYINT, USMALLINT, UINTEGER, UBIGINT, UHUGEINT, DECIMAL, NUMBER, NUMERIC, REAL, FLOAT4, FLOAT, DOUBLE, DOUBLE_PRECISION, FLOAT8, FLOAT64, UUID, VARCHAR, NVARCHAR, CHAR, NCHAR, BPCHAR, STRING, TEXT, CLOB, DATE, TIME, TIME_WITHOUT_TIME_ZONE, TIMETZ, TIME_WITH_TIME_ZONE, TIMESTAMP_NS, TIMESTAMP, TIMESTAMP_WITHOUT_TIME_ZONE, DATETIME, TIMESTAMP_MS, TIMESTAMP_S, TIMESTAMPTZ, TIMESTAMP_WITH_TIME_ZONE, UNKNOWN, VARBYTE; + + public static DataType from(String typeStr) { + Matcher matcher = PATTERN.matcher(typeStr.trim().replaceAll("\\s+", "_").toUpperCase()); + if (matcher.find()) { + try { + return Enum.valueOf(DataType.class, matcher.group(0)); + } catch (Exception ex) { + Logger.getLogger(CastExpression.class.getName()).log(Level.FINE, + "Type " + typeStr + " unknown", ex); + return DataType.UNKNOWN; + } + } else { + Logger.getLogger(CastExpression.class.getName()).log(Level.FINE, + "Type " + typeStr + " unknown"); + return DataType.UNKNOWN; + } + } + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/CollateExpression.java b/src/main/java/net/sf/jsqlparser/expression/CollateExpression.java new file mode 100644 index 0000000..8a419b2 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/CollateExpression.java @@ -0,0 +1,67 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +public class CollateExpression extends ASTNodeAccessImpl implements Expression { + + private Expression leftExpression; + private String collate; + + public CollateExpression() { + // empty constructor + } + + public CollateExpression(Expression leftExpression, String collate) { + this.leftExpression = leftExpression; + this.collate = collate; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + public Expression getLeftExpression() { + return leftExpression; + } + + public void setLeftExpression(Expression leftExpression) { + this.leftExpression = leftExpression; + } + + public String getCollate() { + return collate; + } + + public void setCollate(String collate) { + this.collate = collate; + } + + @Override + public String toString() { + return leftExpression.toString() + " COLLATE " + collate; + } + + public CollateExpression withLeftExpression(Expression leftExpression) { + this.setLeftExpression(leftExpression); + return this; + } + + public CollateExpression withCollate(String collate) { + this.setCollate(collate); + return this; + } + + public E getLeftExpression(Class type) { + return type.cast(getLeftExpression()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/ConnectByRootOperator.java b/src/main/java/net/sf/jsqlparser/expression/ConnectByRootOperator.java new file mode 100644 index 0000000..6942f07 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/ConnectByRootOperator.java @@ -0,0 +1,63 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2021 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +/* + * Copyright (C) 2021 JSQLParser. + * + * This library is free software; you can redistribute it and/or modify it under the terms of the + * GNU Lesser General Public License as published by the Free Software Foundation; either version + * 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with this library; + * if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +package net.sf.jsqlparser.expression; + +import java.util.Objects; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; +import net.sf.jsqlparser.schema.Column; + +/** + * @author are + */ +public class ConnectByRootOperator extends ASTNodeAccessImpl implements Expression { + private final Column column; + + public ConnectByRootOperator(Column column) { + this.column = Objects.requireNonNull(column, + "The COLUMN of the ConnectByRoot Operator must not be null"); + } + + public Column getColumn() { + return column; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + public StringBuilder appendTo(StringBuilder builder) { + builder.append("CONNECT_BY_ROOT ").append(column); + return builder; + } + + @Override + public String toString() { + return appendTo(new StringBuilder()).toString(); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/expression/DateTimeLiteralExpression.java b/src/main/java/net/sf/jsqlparser/expression/DateTimeLiteralExpression.java new file mode 100644 index 0000000..3510fe6 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/DateTimeLiteralExpression.java @@ -0,0 +1,62 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +public class DateTimeLiteralExpression extends ASTNodeAccessImpl implements Expression { + + private String value; + private DateTime type; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public DateTime getType() { + return type; + } + + public void setType(DateTime type) { + this.type = type; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String toString() { + return type.name() + " " + value; + } + + public DateTimeLiteralExpression withValue(String value) { + this.setValue(value); + return this; + } + + public DateTimeLiteralExpression withType(DateTime type) { + this.setType(type); + return this; + } + + public enum DateTime { + DATE, DATETIME, TIME, TIMESTAMP, TIMESTAMPTZ; + + public static DateTime from(String dateTimeStr) { + return Enum.valueOf(DateTime.class, dateTimeStr.toUpperCase()); + } + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/DateValue.java b/src/main/java/net/sf/jsqlparser/expression/DateValue.java new file mode 100644 index 0000000..d03a73c --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/DateValue.java @@ -0,0 +1,62 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +import java.sql.Date; + +/** + * A Date in the form {d 'yyyy-mm-dd'} + */ +public class DateValue extends ASTNodeAccessImpl implements Expression { + + private Date value; + + public DateValue() { + // empty constructor + } + + public DateValue(Date value) { + this.value = value; + } + + /** + * A Date in the form {d 'yyyy-mm-dd'} + * + * @param value The text presentation of the Date to be parsed. + */ + public DateValue(String value) { + this(Date.valueOf(value.substring(1, value.length() - 1))); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + public Date getValue() { + return value; + } + + public void setValue(Date d) { + value = d; + } + + @Override + public String toString() { + return "{d '" + value.toString() + "'}"; + } + + public DateValue withValue(Date value) { + this.setValue(value); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/DoubleValue.java b/src/main/java/net/sf/jsqlparser/expression/DoubleValue.java new file mode 100644 index 0000000..8d25aa6 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/DoubleValue.java @@ -0,0 +1,66 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +/** + * Every number with a point or a exponential format is a DoubleValue + */ +public class DoubleValue extends ASTNodeAccessImpl implements Expression { + + private Double value; + private String stringValue; + + public DoubleValue() { + // empty constructor + } + + public DoubleValue(final String value) { + if (value == null || value.length() == 0) { + throw new IllegalArgumentException("value can neither be null nor empty."); + } + String val = value; + if (val.charAt(0) == '+') { + val = val.substring(1); + } + this.value = Double.parseDouble(val); + this.stringValue = val; + } + + public DoubleValue(final double value) { + this.value = value; + this.stringValue = String.valueOf(value); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + public double getValue() { + return value; + } + + public void setValue(Double d) { + value = d; + stringValue = String.valueOf(value); + } + + @Override + public String toString() { + return stringValue; + } + + public DoubleValue withValue(Double value) { + this.setValue(value); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/Expression.java b/src/main/java/net/sf/jsqlparser/expression/Expression.java new file mode 100644 index 0000000..a480758 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/Expression.java @@ -0,0 +1,26 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.Model; +import net.sf.jsqlparser.parser.ASTNodeAccess; + +public interface Expression extends ASTNodeAccess, Model { + + T accept(ExpressionVisitor expressionVisitor, S context); + + default void accept(ExpressionVisitor expressionVisitor) { + this.accept(expressionVisitor, null); + } + + ; + + +} diff --git a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java new file mode 100644 index 0000000..985547f --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java @@ -0,0 +1,644 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.expression.operators.arithmetic.Addition; +import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseAnd; +import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseLeftShift; +import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseOr; +import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseRightShift; +import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseXor; +import net.sf.jsqlparser.expression.operators.arithmetic.Concat; +import net.sf.jsqlparser.expression.operators.arithmetic.Division; +import net.sf.jsqlparser.expression.operators.arithmetic.IntegerDivision; +import net.sf.jsqlparser.expression.operators.arithmetic.Modulo; +import net.sf.jsqlparser.expression.operators.arithmetic.Multiplication; +import net.sf.jsqlparser.expression.operators.arithmetic.Subtraction; +import net.sf.jsqlparser.expression.operators.conditional.AndExpression; +import net.sf.jsqlparser.expression.operators.conditional.OrExpression; +import net.sf.jsqlparser.expression.operators.conditional.XorExpression; +import net.sf.jsqlparser.expression.operators.relational.Between; +import net.sf.jsqlparser.expression.operators.relational.ContainedBy; +import net.sf.jsqlparser.expression.operators.relational.Contains; +import net.sf.jsqlparser.expression.operators.relational.DoubleAnd; +import net.sf.jsqlparser.expression.operators.relational.EqualsTo; +import net.sf.jsqlparser.expression.operators.relational.ExcludesExpression; +import net.sf.jsqlparser.expression.operators.relational.ExistsExpression; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.expression.operators.relational.FullTextSearch; +import net.sf.jsqlparser.expression.operators.relational.GeometryDistance; +import net.sf.jsqlparser.expression.operators.relational.GreaterThan; +import net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals; +import net.sf.jsqlparser.expression.operators.relational.InExpression; +import net.sf.jsqlparser.expression.operators.relational.IncludesExpression; +import net.sf.jsqlparser.expression.operators.relational.IsBooleanExpression; +import net.sf.jsqlparser.expression.operators.relational.IsDistinctExpression; +import net.sf.jsqlparser.expression.operators.relational.IsNullExpression; +import net.sf.jsqlparser.expression.operators.relational.JsonOperator; +import net.sf.jsqlparser.expression.operators.relational.LikeExpression; +import net.sf.jsqlparser.expression.operators.relational.Matches; +import net.sf.jsqlparser.expression.operators.relational.MemberOfExpression; +import net.sf.jsqlparser.expression.operators.relational.MinorThan; +import net.sf.jsqlparser.expression.operators.relational.MinorThanEquals; +import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo; +import net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator; +import net.sf.jsqlparser.expression.operators.relational.SimilarToExpression; +import net.sf.jsqlparser.expression.operators.relational.TSQLLeftJoin; +import net.sf.jsqlparser.expression.operators.relational.TSQLRightJoin; +import net.sf.jsqlparser.schema.Column; +import net.sf.jsqlparser.statement.select.AllColumns; +import net.sf.jsqlparser.statement.select.AllTableColumns; +import net.sf.jsqlparser.statement.select.ParenthesedSelect; +import net.sf.jsqlparser.statement.select.Select; + +public interface ExpressionVisitor { + + T visit(BitwiseRightShift bitwiseRightShift, S context); + + default void visit(BitwiseRightShift bitwiseRightShift) { + this.visit(bitwiseRightShift, null); + } + + T visit(BitwiseLeftShift bitwiseLeftShift, S context); + + default void visit(BitwiseLeftShift bitwiseLeftShift) { + this.visit(bitwiseLeftShift, null); + } + + T visit(NullValue nullValue, S context); + + default void visit(NullValue nullValue) { + this.visit(nullValue, null); + } + + T visit(Function function, S context); + + default void visit(Function function) { + this.visit(function, null); + } + + T visit(SignedExpression signedExpression, S context); + + default void visit(SignedExpression signedExpression) { + this.visit(signedExpression, null); + } + + T visit(JdbcParameter jdbcParameter, S context); + + default void visit(JdbcParameter jdbcParameter) { + this.visit(jdbcParameter, null); + } + + T visit(JdbcNamedParameter jdbcNamedParameter, S context); + + default void visit(JdbcNamedParameter jdbcNamedParameter) { + this.visit(jdbcNamedParameter, null); + } + + T visit(DoubleValue doubleValue, S context); + + default void visit(DoubleValue doubleValue) { + this.visit(doubleValue, null); + } + + T visit(LongValue longValue, S context); + + default void visit(LongValue longValue) { + this.visit(longValue, null); + } + + T visit(HexValue hexValue, S context); + + default void visit(HexValue hexValue) { + this.visit(hexValue, null); + } + + T visit(DateValue dateValue, S context); + + default void visit(DateValue dateValue) { + this.visit(dateValue, null); + } + + T visit(TimeValue timeValue, S context); + + default void visit(TimeValue timeValue) { + this.visit(timeValue, null); + } + + T visit(TimestampValue timestampValue, S context); + + default void visit(TimestampValue timestampValue) { + this.visit(timestampValue, null); + } + + T visit(StringValue stringValue, S context); + + default void visit(StringValue stringValue) { + this.visit(stringValue, null); + } + + T visit(Addition addition, S context); + + default void visit(Addition addition) { + this.visit(addition, null); + } + + T visit(Division division, S context); + + default void visit(Division division) { + this.visit(division, null); + } + + T visit(IntegerDivision integerDivision, S context); + + default void visit(IntegerDivision integerDivision) { + this.visit(integerDivision, null); + } + + T visit(Multiplication multiplication, S context); + + default void visit(Multiplication multiplication) { + this.visit(multiplication, null); + } + + T visit(Subtraction subtraction, S context); + + default void visit(Subtraction subtraction) { + this.visit(subtraction, null); + } + + T visit(AndExpression andExpression, S context); + + default void visit(AndExpression andExpression) { + this.visit(andExpression, null); + } + + T visit(OrExpression orExpression, S context); + + default void visit(OrExpression orExpression) { + this.visit(orExpression, null); + } + + T visit(XorExpression xorExpression, S context); + + default void visit(XorExpression xorExpression) { + this.visit(xorExpression, null); + } + + T visit(Between between, S context); + + default void visit(Between between) { + this.visit(between, null); + } + + T visit(OverlapsCondition overlapsCondition, S context); + + default void visit(OverlapsCondition overlapsCondition) { + this.visit(overlapsCondition, null); + } + + T visit(EqualsTo equalsTo, S context); + + default void visit(EqualsTo equalsTo) { + this.visit(equalsTo, null); + } + + T visit(GreaterThan greaterThan, S context); + + default void visit(GreaterThan greaterThan) { + this.visit(greaterThan, null); + } + + T visit(GreaterThanEquals greaterThanEquals, S context); + + default void visit(GreaterThanEquals greaterThanEquals) { + this.visit(greaterThanEquals, null); + } + + T visit(InExpression inExpression, S context); + + default void visit(InExpression inExpression) { + this.visit(inExpression, null); + } + + T visit(IncludesExpression includesExpression, S context); + + default void visit(IncludesExpression includesExpression) { + this.visit(includesExpression, null); + } + + T visit(ExcludesExpression excludesExpression, S context); + + default void visit(ExcludesExpression excludesExpression) { + this.visit(excludesExpression, null); + } + + T visit(FullTextSearch fullTextSearch, S context); + + default void visit(FullTextSearch fullTextSearch) { + this.visit(fullTextSearch, null); + } + + T visit(IsNullExpression isNullExpression, S context); + + default void visit(IsNullExpression isNullExpression) { + this.visit(isNullExpression, null); + } + + T visit(IsBooleanExpression isBooleanExpression, S context); + + default void visit(IsBooleanExpression isBooleanExpression) { + this.visit(isBooleanExpression, null); + } + + T visit(LikeExpression likeExpression, S context); + + default void visit(LikeExpression likeExpression) { + this.visit(likeExpression, null); + } + + T visit(MinorThan minorThan, S context); + + default void visit(MinorThan minorThan) { + this.visit(minorThan, null); + } + + T visit(MinorThanEquals minorThanEquals, S context); + + default void visit(MinorThanEquals minorThanEquals) { + this.visit(minorThanEquals, null); + } + + T visit(NotEqualsTo notEqualsTo, S context); + + default void visit(NotEqualsTo notEqualsTo) { + this.visit(notEqualsTo, null); + } + + T visit(DoubleAnd doubleAnd, S context); + + default void visit(DoubleAnd doubleAnd) { + this.visit(doubleAnd, null); + } + + T visit(Contains contains, S context); + + default void visit(Contains contains) { + this.visit(contains, null); + } + + T visit(ContainedBy containedBy, S context); + + default void visit(ContainedBy containedBy) { + this.visit(containedBy, null); + } + + T visit(ParenthesedSelect select, S context); + + default void visit(ParenthesedSelect select) { + this.visit(select, null); + } + + T visit(Column column, S context); + + default void visit(Column column) { + this.visit(column, null); + } + + T visit(CaseExpression caseExpression, S context); + + default void visit(CaseExpression caseExpression) { + this.visit(caseExpression, null); + } + + T visit(WhenClause whenClause, S context); + + default void visit(WhenClause whenClause) { + this.visit(whenClause, null); + } + + T visit(ExistsExpression existsExpression, S context); + + default void visit(ExistsExpression existsExpression) { + this.visit(existsExpression, null); + } + + T visit(MemberOfExpression memberOfExpression, S context); + + default void visit(MemberOfExpression memberOfExpression) { + this.visit(memberOfExpression, null); + } + + T visit(AnyComparisonExpression anyComparisonExpression, S context); + + default void visit(AnyComparisonExpression anyComparisonExpression) { + this.visit(anyComparisonExpression, null); + } + + T visit(Concat concat, S context); + + default void visit(Concat concat) { + this.visit(concat, null); + } + + T visit(Matches matches, S context); + + default void visit(Matches matches) { + this.visit(matches, null); + } + + T visit(BitwiseAnd bitwiseAnd, S context); + + default void visit(BitwiseAnd bitwiseAnd) { + this.visit(bitwiseAnd, null); + } + + T visit(BitwiseOr bitwiseOr, S context); + + default void visit(BitwiseOr bitwiseOr) { + this.visit(bitwiseOr, null); + } + + T visit(BitwiseXor bitwiseXor, S context); + + default void visit(BitwiseXor bitwiseXor) { + this.visit(bitwiseXor, null); + } + + T visit(CastExpression castExpression, S context); + + default void visit(CastExpression castExpression) { + this.visit(castExpression, null); + } + + T visit(Modulo modulo, S context); + + default void visit(Modulo modulo) { + this.visit(modulo, null); + } + + T visit(AnalyticExpression analyticExpression, S context); + + default void visit(AnalyticExpression analyticExpression) { + this.visit(analyticExpression, null); + } + + T visit(ExtractExpression extractExpression, S context); + + default void visit(ExtractExpression extractExpression) { + this.visit(extractExpression, null); + } + + T visit(IntervalExpression intervalExpression, S context); + + default void visit(IntervalExpression intervalExpression) { + this.visit(intervalExpression, null); + } + + T visit(OracleHierarchicalExpression hierarchicalExpression, S context); + + default void visit(OracleHierarchicalExpression hierarchicalExpression) { + this.visit(hierarchicalExpression, null); + } + + T visit(RegExpMatchOperator regExpMatchOperator, S context); + + default void visit(RegExpMatchOperator regExpMatchOperator) { + this.visit(regExpMatchOperator, null); + } + + T visit(JsonExpression jsonExpression, S context); + + default void visit(JsonExpression jsonExpression) { + this.visit(jsonExpression, null); + } + + T visit(JsonOperator jsonOperator, S context); + + default void visit(JsonOperator jsonOperator) { + this.visit(jsonOperator, null); + } + + T visit(UserVariable userVariable, S context); + + default void visit(UserVariable userVariable) { + this.visit(userVariable, null); + } + + T visit(NumericBind numericBind, S context); + + default void visit(NumericBind numericBind) { + this.visit(numericBind, null); + } + + T visit(KeepExpression keepExpression, S context); + + default void visit(KeepExpression keepExpression) { + this.visit(keepExpression, null); + } + + T visit(MySQLGroupConcat groupConcat, S context); + + default void visit(MySQLGroupConcat groupConcat) { + this.visit(groupConcat, null); + } + + T visit(ExpressionList expressionList, S context); + + default void visit(ExpressionList expressionList) { + this.visit(expressionList, null); + } + + T visit(RowConstructor rowConstructor, S context); + + default void visit(RowConstructor rowConstructor) { + this.visit(rowConstructor, null); + } + + T visit(RowGetExpression rowGetExpression, S context); + + default void visit(RowGetExpression rowGetExpression) { + this.visit(rowGetExpression, null); + } + + T visit(OracleHint hint, S context); + + default void visit(OracleHint hint) { + this.visit(hint, null); + } + + T visit(TimeKeyExpression timeKeyExpression, S context); + + default void visit(TimeKeyExpression timeKeyExpression) { + this.visit(timeKeyExpression, null); + } + + T visit(DateTimeLiteralExpression dateTimeLiteralExpression, S context); + + default void visit(DateTimeLiteralExpression dateTimeLiteralExpression) { + this.visit(dateTimeLiteralExpression, null); + } + + T visit(NotExpression notExpression, S context); + + default void visit(NotExpression notExpression) { + this.visit(notExpression, null); + } + + T visit(NextValExpression nextValExpression, S context); + + default void visit(NextValExpression nextValExpression) { + this.visit(nextValExpression, null); + } + + T visit(CollateExpression collateExpression, S context); + + default void visit(CollateExpression collateExpression) { + this.visit(collateExpression, null); + } + + T visit(SimilarToExpression similarToExpression, S context); + + default void visit(SimilarToExpression similarToExpression) { + this.visit(similarToExpression, null); + } + + T visit(ArrayExpression arrayExpression, S context); + + default void visit(ArrayExpression arrayExpression) { + this.visit(arrayExpression, null); + } + + T visit(ArrayConstructor arrayConstructor, S context); + + default void visit(ArrayConstructor arrayConstructor) { + this.visit(arrayConstructor, null); + } + + T visit(VariableAssignment variableAssignment, S context); + + default void visit(VariableAssignment variableAssignment) { + this.visit(variableAssignment, null); + } + + T visit(XMLSerializeExpr xmlSerializeExpr, S context); + + default void visit(XMLSerializeExpr xmlSerializeExpr) { + this.visit(xmlSerializeExpr, null); + } + + T visit(TimezoneExpression timezoneExpression, S context); + + default void visit(TimezoneExpression timezoneExpression) { + this.visit(timezoneExpression, null); + } + + T visit(JsonAggregateFunction jsonAggregateFunction, S context); + + default void visit(JsonAggregateFunction jsonAggregateFunction) { + this.visit(jsonAggregateFunction, null); + } + + T visit(JsonFunction jsonFunction, S context); + + default void visit(JsonFunction jsonFunction) { + this.visit(jsonFunction, null); + } + + T visit(ConnectByRootOperator connectByRootOperator, S context); + + default void visit(ConnectByRootOperator connectByRootOperator) { + this.visit(connectByRootOperator, null); + } + + T visit(OracleNamedFunctionParameter oracleNamedFunctionParameter, S context); + + default void visit(OracleNamedFunctionParameter oracleNamedFunctionParameter) { + this.visit(oracleNamedFunctionParameter, null); + } + + T visit(AllColumns allColumns, S context); + + default void visit(AllColumns allColumns) { + this.visit(allColumns, null); + } + + T visit(AllTableColumns allTableColumns, S context); + + default void visit(AllTableColumns allTableColumns) { + this.visit(allTableColumns, null); + } + + T visit(AllValue allValue, S context); + + default void visit(AllValue allValue) { + this.visit(allValue, null); + } + + T visit(IsDistinctExpression isDistinctExpression, S context); + + default void visit(IsDistinctExpression isDistinctExpression) { + this.visit(isDistinctExpression, null); + } + + T visit(GeometryDistance geometryDistance, S context); + + default void visit(GeometryDistance geometryDistance) { + this.visit(geometryDistance, null); + } + + T visit(Select select, S context); + + default void visit(Select select) { + this.visit(select, null); + } + + T visit(TranscodingFunction transcodingFunction, S context); + + default void visit(TranscodingFunction transcodingFunction) { + this.visit(transcodingFunction, null); + } + + T visit(TrimFunction trimFunction, S context); + + default void visit(TrimFunction trimFunction) { + this.visit(trimFunction, null); + } + + T visit(RangeExpression rangeExpression, S context); + + default void visit(RangeExpression rangeExpression) { + this.visit(rangeExpression, null); + } + + T visit(TSQLLeftJoin tsqlLeftJoin, S context); + + default void visit(TSQLLeftJoin tsqlLeftJoin) { + this.visit(tsqlLeftJoin, null); + } + + T visit(TSQLRightJoin tsqlRightJoin, S context); + + default void visit(TSQLRightJoin tsqlRightJoin) { + this.visit(tsqlRightJoin, null); + } + + T visit(StructType structType, S context); + + default void visit(StructType structType) { + this.visit(structType, null); + } + + T visit(LambdaExpression lambdaExpression, S context); + + default void visit(LambdaExpression lambdaExpression) { + this.visit(lambdaExpression, null); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java new file mode 100644 index 0000000..f2223f8 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java @@ -0,0 +1,827 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.expression.operators.arithmetic.Addition; +import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseAnd; +import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseLeftShift; +import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseOr; +import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseRightShift; +import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseXor; +import net.sf.jsqlparser.expression.operators.arithmetic.Concat; +import net.sf.jsqlparser.expression.operators.arithmetic.Division; +import net.sf.jsqlparser.expression.operators.arithmetic.IntegerDivision; +import net.sf.jsqlparser.expression.operators.arithmetic.Modulo; +import net.sf.jsqlparser.expression.operators.arithmetic.Multiplication; +import net.sf.jsqlparser.expression.operators.arithmetic.Subtraction; +import net.sf.jsqlparser.expression.operators.conditional.AndExpression; +import net.sf.jsqlparser.expression.operators.conditional.OrExpression; +import net.sf.jsqlparser.expression.operators.conditional.XorExpression; +import net.sf.jsqlparser.expression.operators.relational.Between; +import net.sf.jsqlparser.expression.operators.relational.ContainedBy; +import net.sf.jsqlparser.expression.operators.relational.Contains; +import net.sf.jsqlparser.expression.operators.relational.DoubleAnd; +import net.sf.jsqlparser.expression.operators.relational.EqualsTo; +import net.sf.jsqlparser.expression.operators.relational.ExistsExpression; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.expression.operators.relational.FullTextSearch; +import net.sf.jsqlparser.expression.operators.relational.GeometryDistance; +import net.sf.jsqlparser.expression.operators.relational.GreaterThan; +import net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals; +import net.sf.jsqlparser.expression.operators.relational.InExpression; +import net.sf.jsqlparser.expression.operators.relational.IsBooleanExpression; +import net.sf.jsqlparser.expression.operators.relational.IsDistinctExpression; +import net.sf.jsqlparser.expression.operators.relational.IsNullExpression; +import net.sf.jsqlparser.expression.operators.relational.JsonOperator; +import net.sf.jsqlparser.expression.operators.relational.LikeExpression; +import net.sf.jsqlparser.expression.operators.relational.Matches; +import net.sf.jsqlparser.expression.operators.relational.MemberOfExpression; +import net.sf.jsqlparser.expression.operators.relational.MinorThan; +import net.sf.jsqlparser.expression.operators.relational.MinorThanEquals; +import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo; +import net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator; +import net.sf.jsqlparser.expression.operators.relational.SimilarToExpression; +import net.sf.jsqlparser.expression.operators.relational.*; +import net.sf.jsqlparser.schema.Column; +import net.sf.jsqlparser.statement.select.AllColumns; +import net.sf.jsqlparser.statement.select.AllTableColumns; +import net.sf.jsqlparser.statement.select.OrderByElement; +import net.sf.jsqlparser.statement.select.ParenthesedSelect; +import net.sf.jsqlparser.statement.select.Pivot; +import net.sf.jsqlparser.statement.select.PivotVisitor; +import net.sf.jsqlparser.statement.select.PivotXml; +import net.sf.jsqlparser.statement.select.Select; +import net.sf.jsqlparser.statement.select.SelectItem; +import net.sf.jsqlparser.statement.select.SelectItemVisitor; +import net.sf.jsqlparser.statement.select.SelectVisitor; +import net.sf.jsqlparser.statement.select.UnPivot; +import net.sf.jsqlparser.statement.select.WithItem; + +import java.util.Optional; + +@SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.UncommentedEmptyMethodBody"}) +public class ExpressionVisitorAdapter + implements ExpressionVisitor, PivotVisitor, SelectItemVisitor { + + private SelectVisitor selectVisitor; + + public SelectVisitor getSelectVisitor() { + return selectVisitor; + } + + public void setSelectVisitor(SelectVisitor selectVisitor) { + this.selectVisitor = selectVisitor; + } + + @Override + public T visit(NullValue nullValue, S context) { + + return null; + } + + @Override + public T visit(Function function, S context) { + if (function.getParameters() != null) { + function.getParameters().accept(this, context); + } + if (function.getKeep() != null) { + function.getKeep().accept(this, context); + } + if (function.getOrderByElements() != null) { + for (OrderByElement orderByElement : function.getOrderByElements()) { + orderByElement.getExpression().accept(this, context); + } + } + return null; + } + + @Override + public T visit(SignedExpression signedExpression, S context) { + signedExpression.getExpression().accept(this, context); + return null; + } + + @Override + public T visit(JdbcParameter jdbcParameter, S context) { + + return null; + } + + @Override + public T visit(JdbcNamedParameter jdbcNamedParameter, S context) { + + return null; + } + + @Override + public T visit(DoubleValue doubleValue, S context) { + + return null; + } + + @Override + public T visit(LongValue longValue, S context) { + + return null; + } + + @Override + public T visit(DateValue dateValue, S context) { + + return null; + } + + @Override + public T visit(TimeValue timeValue, S context) { + + return null; + } + + @Override + public T visit(TimestampValue timestampValue, S context) { + + return null; + } + + @Override + public T visit(StringValue stringValue, S context) { + return null; + } + + @Override + public T visit(Addition addition, S context) { + visitBinaryExpression(addition, context); + return null; + } + + @Override + public T visit(Division division, S context) { + visitBinaryExpression(division, context); + return null; + } + + @Override + public T visit(IntegerDivision integerDivision, S context) { + visitBinaryExpression(integerDivision, context); + return null; + } + + @Override + public T visit(Multiplication multiplication, S context) { + visitBinaryExpression(multiplication, context); + return null; + } + + @Override + public T visit(Subtraction subtraction, S context) { + visitBinaryExpression(subtraction, context); + return null; + } + + @Override + public T visit(AndExpression andExpression, S context) { + visitBinaryExpression(andExpression, context); + return null; + } + + @Override + public T visit(OrExpression orExpression, S context) { + visitBinaryExpression(orExpression, context); + return null; + } + + @Override + public T visit(XorExpression xorExpression, S context) { + visitBinaryExpression(xorExpression, context); + return null; + } + + @Override + public T visit(Between between, S context) { + between.getLeftExpression().accept(this, context); + between.getBetweenExpressionStart().accept(this, context); + between.getBetweenExpressionEnd().accept(this, context); + return null; + } + + public T visit(OverlapsCondition overlapsCondition, S context) { + overlapsCondition.getLeft().accept(this, context); + overlapsCondition.getRight().accept(this, context); + return null; + } + + + @Override + public T visit(EqualsTo equalsTo, S context) { + visitBinaryExpression(equalsTo, context); + return null; + } + + @Override + public T visit(GreaterThan greaterThan, S context) { + visitBinaryExpression(greaterThan, context); + return null; + } + + @Override + public T visit(GreaterThanEquals greaterThanEquals, S context) { + visitBinaryExpression(greaterThanEquals, context); + return null; + } + + @Override + public T visit(InExpression inExpression, S context) { + inExpression.getLeftExpression().accept(this, context); + inExpression.getRightExpression().accept(this, context); + return null; + } + + @Override + public T visit(IncludesExpression includesExpression, S context) { + includesExpression.getLeftExpression().accept(this, context); + includesExpression.getRightExpression().accept(this, context); + return null; + } + + @Override + public T visit(ExcludesExpression excludesExpression, S context) { + excludesExpression.getLeftExpression().accept(this, context); + excludesExpression.getRightExpression().accept(this, context); + return null; + } + + @Override + public T visit(IsNullExpression isNullExpression, S context) { + isNullExpression.getLeftExpression().accept(this, context); + return null; + } + + @Override + public T visit(FullTextSearch fullTextSearch, S context) { + for (Column col : fullTextSearch.getMatchColumns()) { + col.accept(this, context); + } + return null; + } + + @Override + public T visit(IsBooleanExpression isBooleanExpression, S context) { + isBooleanExpression.getLeftExpression().accept(this, context); + return null; + } + + @Override + public T visit(LikeExpression likeExpression, S context) { + visitBinaryExpression(likeExpression, context); + return null; + } + + @Override + public T visit(MinorThan minorThan, S context) { + visitBinaryExpression(minorThan, context); + return null; + } + + @Override + public T visit(MinorThanEquals minorThanEquals, S context) { + visitBinaryExpression(minorThanEquals, context); + return null; + } + + @Override + public T visit(NotEqualsTo notEqualsTo, S context) { + visitBinaryExpression(notEqualsTo, context); + return null; + } + + @Override + public T visit(DoubleAnd doubleAnd, S context) { + visitBinaryExpression(doubleAnd, context); + return null; + } + + @Override + public T visit(Contains contains, S context) { + visitBinaryExpression(contains, context); + return null; + } + + @Override + public T visit(ContainedBy containedBy, S context) { + visitBinaryExpression(containedBy, context); + return null; + } + + @Override + public T visit(Column column, S context) { + + return null; + } + + @Override + public T visit(ParenthesedSelect select, S context) { + visit((Select) select, context); + if (select.getPivot() != null) { + select.getPivot().accept(this, context); + } + return null; + } + + @Override + public T visit(CaseExpression caseExpression, S context) { + if (caseExpression.getSwitchExpression() != null) { + caseExpression.getSwitchExpression().accept(this, context); + } + for (Expression x : caseExpression.getWhenClauses()) { + x.accept(this, context); + } + if (caseExpression.getElseExpression() != null) { + caseExpression.getElseExpression().accept(this, context); + } + return null; + } + + @Override + public T visit(WhenClause whenClause, S context) { + whenClause.getWhenExpression().accept(this, context); + whenClause.getThenExpression().accept(this, context); + return null; + } + + @Override + public T visit(ExistsExpression existsExpression, S context) { + existsExpression.getRightExpression().accept(this, context); + return null; + } + + @Override + public T visit(MemberOfExpression memberOfExpression, S context) { + memberOfExpression.getRightExpression().accept(this, context); + return null; + } + + @Override + public T visit(AnyComparisonExpression anyComparisonExpression, S context) { + + return null; + } + + @Override + public T visit(Concat concat, S context) { + visitBinaryExpression(concat, context); + return null; + } + + @Override + public T visit(Matches matches, S context) { + visitBinaryExpression(matches, context); + return null; + } + + @Override + public T visit(BitwiseAnd bitwiseAnd, S context) { + visitBinaryExpression(bitwiseAnd, context); + return null; + } + + @Override + public T visit(BitwiseOr bitwiseOr, S context) { + visitBinaryExpression(bitwiseOr, context); + return null; + } + + @Override + public T visit(BitwiseXor bitwiseXor, S context) { + visitBinaryExpression(bitwiseXor, context); + return null; + } + + @Override + public T visit(CastExpression castExpression, S context) { + castExpression.getLeftExpression().accept(this, context); + return null; + } + + @Override + public T visit(Modulo modulo, S context) { + visitBinaryExpression(modulo, context); + return null; + } + + @Override + public T visit(AnalyticExpression analyticExpression, S context) { + if (analyticExpression.getExpression() != null) { + analyticExpression.getExpression().accept(this, context); + } + if (analyticExpression.getDefaultValue() != null) { + analyticExpression.getDefaultValue().accept(this, context); + } + if (analyticExpression.getOffset() != null) { + analyticExpression.getOffset().accept(this, context); + } + if (analyticExpression.getKeep() != null) { + analyticExpression.getKeep().accept(this, context); + } + if (analyticExpression.getFuncOrderBy() != null) { + for (OrderByElement element : analyticExpression.getOrderByElements()) { + element.getExpression().accept(this, context); + } + } + if (analyticExpression.getWindowElement() != null) { + /* + * Visit expressions from the range and offset of the window element. Do this using + * optional chains, because several things down the tree can be null e.g. the + * expression. So, null-safe versions of e.g.: + * analyticExpression.getWindowElement().getOffset().getExpression().accept(this, + * parameters); + */ + Optional.ofNullable(analyticExpression.getWindowElement().getRange()) + .map(WindowRange::getStart) + .map(WindowOffset::getExpression).ifPresent(e -> e.accept(this, context)); + Optional.ofNullable(analyticExpression.getWindowElement().getRange()) + .map(WindowRange::getEnd) + .map(WindowOffset::getExpression).ifPresent(e -> e.accept(this, context)); + Optional.ofNullable(analyticExpression.getWindowElement().getOffset()) + .map(WindowOffset::getExpression).ifPresent(e -> e.accept(this, context)); + } + return null; + } + + @Override + public T visit(ExtractExpression extractExpression, S context) { + extractExpression.getExpression().accept(this, context); + return null; + } + + @Override + public T visit(IntervalExpression intervalExpression, S context) { + return null; + } + + @Override + public T visit(OracleHierarchicalExpression hierarchicalExpression, S context) { + hierarchicalExpression.getConnectExpression().accept(this, context); + hierarchicalExpression.getStartExpression().accept(this, context); + return null; + } + + @Override + public T visit(RegExpMatchOperator regExpMatchOperator, S context) { + visitBinaryExpression(regExpMatchOperator, context); + return null; + } + + @Override + public T visit(ExpressionList expressionList, S context) { + for (Expression expr : expressionList) { + expr.accept(this, context); + } + return null; + } + + @Override + public T visit(RowConstructor rowConstructor, S context) { + for (Expression expr : rowConstructor) { + expr.accept(this, context); + } + return null; + } + + @Override + public T visit(NotExpression notExpr, S context) { + notExpr.getExpression().accept(this, context); + return null; + } + + @Override + public T visit(BitwiseRightShift bitwiseRightShift, S context) { + visitBinaryExpression(bitwiseRightShift, context); + return null; + } + + @Override + public T visit(BitwiseLeftShift bitwiseLeftShift, S context) { + visitBinaryExpression(bitwiseLeftShift, context); + return null; + } + + protected T visitBinaryExpression(BinaryExpression binaryExpression, S context) { + binaryExpression.getLeftExpression().accept(this, context); + binaryExpression.getRightExpression().accept(this, context); + return null; + } + + @Override + public T visit(JsonExpression jsonExpr, S context) { + jsonExpr.getExpression().accept(this, context); + return null; + } + + @Override + public T visit(JsonOperator jsonOperator, S context) { + visitBinaryExpression(jsonOperator, context); + return null; + } + + @Override + public T visit(UserVariable userVariable, S context) { + + return null; + } + + @Override + public T visit(NumericBind numericBind, S context) { + + return null; + } + + @Override + public T visit(KeepExpression keepExpression, S context) { + for (OrderByElement element : keepExpression.getOrderByElements()) { + element.getExpression().accept(this, context); + } + return null; + } + + @Override + public T visit(MySQLGroupConcat groupConcat, S context) { + for (Expression expr : groupConcat.getExpressionList()) { + expr.accept(this, context); + } + if (groupConcat.getOrderByElements() != null) { + for (OrderByElement element : groupConcat.getOrderByElements()) { + element.getExpression().accept(this, context); + } + } + return null; + } + + @Override + public T visit(Pivot pivot, S context) { + for (SelectItem item : pivot.getFunctionItems()) { + item.getExpression().accept(this, context); + } + for (Column col : pivot.getForColumns()) { + col.accept(this, context); + } + if (pivot.getSingleInItems() != null) { + for (SelectItem item : pivot.getSingleInItems()) { + item.getExpression().accept(this, context); + } + } + + if (pivot.getMultiInItems() != null) { + for (SelectItem> item : pivot.getMultiInItems()) { + item.getExpression().accept(this, context); + } + } + return null; + } + + @Override + public T visit(PivotXml pivotXml, S context) { + for (SelectItem item : pivotXml.getFunctionItems()) { + item.getExpression().accept(this, context); + } + for (Column col : pivotXml.getForColumns()) { + col.accept(this, context); + } + if (pivotXml.getInSelect() != null && selectVisitor != null) { + pivotXml.getInSelect().accept(selectVisitor, context); + } + return null; + } + + @Override + public T visit(UnPivot unpivot, S context) { + unpivot.accept(this, context); + return null; + } + + @Override + public T visit(AllColumns allColumns, S context) { + return null; + } + + @Override + public T visit(AllTableColumns allTableColumns, S context) { + return null; + } + + @Override + public T visit(AllValue allValue, S context) { + return null; + } + + @Override + public T visit(IsDistinctExpression isDistinctExpression, S context) { + visitBinaryExpression(isDistinctExpression, context); + return null; + } + + @Override + public T visit(SelectItem selectItem, S context) { + selectItem.getExpression().accept(this, context); + return null; + } + + @Override + public T visit(RowGetExpression rowGetExpression, S context) { + rowGetExpression.getExpression().accept(this, context); + return null; + } + + @Override + public T visit(HexValue hexValue, S context) { + + return null; + } + + @Override + public T visit(OracleHint hint, S context) { + + return null; + } + + @Override + public T visit(TimeKeyExpression timeKeyExpression, S context) { + + return null; + } + + @Override + public T visit(DateTimeLiteralExpression dateTimeLiteralExpression, S context) { + return null; + } + + @Override + public T visit(NextValExpression nextValExpression, S context) { + return null; + } + + @Override + public T visit(CollateExpression collateExpression, S context) { + collateExpression.getLeftExpression().accept(this, context); + return null; + } + + @Override + public T visit(SimilarToExpression similarToExpression, S context) { + visitBinaryExpression(similarToExpression, context); + return null; + } + + @Override + public T visit(ArrayExpression arrayExpression, S context) { + arrayExpression.getObjExpression().accept(this, context); + if (arrayExpression.getIndexExpression() != null) { + arrayExpression.getIndexExpression().accept(this, context); + } + if (arrayExpression.getStartIndexExpression() != null) { + arrayExpression.getStartIndexExpression().accept(this, context); + } + if (arrayExpression.getStopIndexExpression() != null) { + arrayExpression.getStopIndexExpression().accept(this, context); + } + return null; + } + + @Override + public T visit(ArrayConstructor arrayConstructor, S context) { + for (Expression expression : arrayConstructor.getExpressions()) { + expression.accept(this, context); + } + return null; + } + + @Override + public T visit(VariableAssignment variableAssignment, S context) { + variableAssignment.getVariable().accept(this, context); + variableAssignment.getExpression().accept(this, context); + return null; + } + + @Override + public T visit(XMLSerializeExpr xmlSerializeExpr, S context) { + xmlSerializeExpr.getExpression().accept(this, context); + for (OrderByElement elm : xmlSerializeExpr.getOrderByElements()) { + elm.getExpression().accept(this, context); + } + return null; + } + + @Override + public T visit(TimezoneExpression timezoneExpression, S context) { + timezoneExpression.getLeftExpression().accept(this, context); + return null; + } + + @Override + public T visit(JsonAggregateFunction jsonAggregateFunction, S context) { + Expression expr = jsonAggregateFunction.getExpression(); + if (expr != null) { + expr.accept(this, context); + } + + expr = jsonAggregateFunction.getFilterExpression(); + if (expr != null) { + expr.accept(this, context); + } + return null; + } + + @Override + public T visit(JsonFunction jsonFunction, S context) { + for (JsonFunctionExpression expr : jsonFunction.getExpressions()) { + expr.getExpression().accept(this, context); + } + return null; + } + + @Override + public T visit(ConnectByRootOperator connectByRootOperator, S context) { + connectByRootOperator.getColumn().accept(this, context); + return null; + } + + @Override + public T visit(OracleNamedFunctionParameter oracleNamedFunctionParameter, S context) { + oracleNamedFunctionParameter.getExpression().accept(this, context); + return null; + } + + @Override + public T visit(GeometryDistance geometryDistance, S context) { + visitBinaryExpression(geometryDistance, context); + return null; + } + + @Override + public T visit(Select select, S context) { + if (selectVisitor != null) { + if (select.getWithItemsList() != null) { + for (WithItem item : select.getWithItemsList()) { + item.accept(selectVisitor, context); + } + } + select.accept(selectVisitor, context); + } + return null; + } + + @Override + public T visit(TranscodingFunction transcodingFunction, S context) { + + return null; + } + + @Override + public T visit(TrimFunction trimFunction, S context) { + + return null; + } + + @Override + public T visit(RangeExpression rangeExpression, S context) { + rangeExpression.getStartExpression().accept(this, context); + rangeExpression.getEndExpression().accept(this, context); + return null; + } + + @Override + public T visit(TSQLLeftJoin tsqlLeftJoin, S context) { + visitBinaryExpression(tsqlLeftJoin, context); + return null; + } + + @Override + public T visit(TSQLRightJoin tsqlRightJoin, S context) { + visitBinaryExpression(tsqlRightJoin, context); + return null; + } + + @Override + public T visit(StructType structType, S context) { + // @todo: visit the ColType also + if (structType.getArguments() != null) { + for (SelectItem selectItem : structType.getArguments()) { + visit(selectItem, context); + } + } + return null; + } + + @Override + public T visit(LambdaExpression lambdaExpression, S context) { + lambdaExpression.getExpression().accept(this, context); + return null; + } + +} diff --git a/src/main/java/net/sf/jsqlparser/expression/ExtractExpression.java b/src/main/java/net/sf/jsqlparser/expression/ExtractExpression.java new file mode 100644 index 0000000..dcbddfe --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/ExtractExpression.java @@ -0,0 +1,64 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +/** + * Extract value from date/time expression. The name stores the part - name to get from the + * following date/time expression. + * + * @author tw + */ +public class ExtractExpression extends ASTNodeAccessImpl implements Expression { + + private String name; + private Expression expression; + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Expression getExpression() { + return expression; + } + + public void setExpression(Expression expression) { + this.expression = expression; + } + + @Override + public String toString() { + return "EXTRACT(" + name + " FROM " + expression + ')'; + } + + public ExtractExpression withName(String name) { + this.setName(name); + return this; + } + + public ExtractExpression withExpression(Expression expression) { + this.setExpression(expression); + return this; + } + + public E getExpression(Class type) { + return type.cast(getExpression()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/FilterOverImpl.java b/src/main/java/net/sf/jsqlparser/expression/FilterOverImpl.java new file mode 100644 index 0000000..50e5579 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/FilterOverImpl.java @@ -0,0 +1,142 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import java.util.List; + +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; +import net.sf.jsqlparser.statement.select.OrderByElement; + +/** + * @author tw + */ +public class FilterOverImpl extends ASTNodeAccessImpl { + private final OrderByClause orderBy = new OrderByClause(); + private final PartitionByClause partitionBy = new PartitionByClause(); + private AnalyticType analyticType = AnalyticType.FILTER_ONLY; + private Expression filterExpression = null; + private WindowElement windowElement = null; + + public AnalyticType getAnalyticType() { + return analyticType; + } + + public void setAnalyticType(AnalyticType analyticType) { + this.analyticType = analyticType; + } + + public FilterOverImpl withAnalyticType(AnalyticType analyticType) { + this.setAnalyticType(analyticType); + return this; + } + + public List getOrderByElements() { + return orderBy.getOrderByElements(); + } + + public void setOrderByElements(List orderByElements) { + orderBy.setOrderByElements(orderByElements); + } + + public FilterOverImpl withOrderByElements(List orderByElements) { + this.setOrderByElements(orderByElements); + return this; + } + + public ExpressionList getPartitionExpressionList() { + return partitionBy.getPartitionExpressionList(); + } + + public void setPartitionExpressionList(ExpressionList partitionExpressionList) { + setPartitionExpressionList(partitionExpressionList, false); + } + + public void setPartitionExpressionList(ExpressionList partitionExpressionList, + boolean brackets) { + partitionBy.setPartitionExpressionList(partitionExpressionList, brackets); + } + + public boolean isPartitionByBrackets() { + return partitionBy.isBrackets(); + } + + public Expression getFilterExpression() { + return filterExpression; + } + + public void setFilterExpression(Expression filterExpression) { + this.filterExpression = filterExpression; + } + + + public FilterOverImpl withFilterExpression(Expression filterExpression) { + this.setFilterExpression(filterExpression); + return this; + } + + public WindowElement getWindowElement() { + return windowElement; + } + + public void setWindowElement(WindowElement windowElement) { + this.windowElement = windowElement; + } + + public FilterOverImpl withWindowElement(WindowElement windowElement) { + this.setWindowElement(windowElement); + return this; + } + + @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity", + "PMD.MissingBreakInSwitch"}) + public StringBuilder append(StringBuilder builder) { + if (filterExpression != null) { + builder.append("FILTER (WHERE "); + builder.append(filterExpression.toString()); + builder.append(")"); + if (analyticType != AnalyticType.FILTER_ONLY) { + builder.append(" "); + } + } + + switch (analyticType) { + case FILTER_ONLY: + return builder; + case WITHIN_GROUP: + builder.append("WITHIN GROUP"); + break; + default: + builder.append("OVER"); + } + builder.append(" ("); + + partitionBy.toStringPartitionBy(builder); + orderBy.toStringOrderByElements(builder); + + if (windowElement != null) { + if (orderBy.getOrderByElements() != null) { + builder.append(' '); + } + builder.append(windowElement); + } + + builder.append(")"); + + return builder; + } + + @Override + @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"}) + public String toString() { + StringBuilder builder = new StringBuilder(); + return append(builder).toString(); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/Function.java b/src/main/java/net/sf/jsqlparser/expression/Function.java new file mode 100644 index 0000000..ffe2a01 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/Function.java @@ -0,0 +1,456 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.expression.operators.relational.NamedExpressionList; +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; +import net.sf.jsqlparser.schema.Column; +import net.sf.jsqlparser.statement.select.Limit; +import net.sf.jsqlparser.statement.select.OrderByElement; + +import java.util.Arrays; +import java.util.List; + +/** + * A function as MAX,COUNT... + */ +public class Function extends ASTNodeAccessImpl implements Expression { + private List nameparts; + private ExpressionList parameters; + private NamedExpressionList namedParameters; + private boolean allColumns = false; + private boolean distinct = false; + private boolean unique = false; + private boolean isEscaped = false; + private Expression attributeExpression; + private HavingClause havingClause; + private Column attributeColumn = null; + private List orderByElements; + private NullHandling nullHandling = null; + private boolean ignoreNullsOutside = false; // IGNORE NULLS outside function parameters + private Limit limit = null; + private KeepExpression keep = null; + + public Function() {} + + public Function(String name, Expression... parameters) { + this.nameparts = Arrays.asList(name); + this.parameters = new ExpressionList<>(parameters); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + public String getName() { + return nameparts == null ? null + : String.join(nameparts.get(0).equalsIgnoreCase("APPROXIMATE") ? " " : ".", + nameparts); + } + + public void setName(String string) { + nameparts = Arrays.asList(string); + } + + public void setName(List string) { + nameparts = string; + } + + public List getMultipartName() { + return nameparts; + } + + public Function withName(String name) { + this.setName(name); + return this; + } + + public Function withName(List nameparts) { + this.nameparts = nameparts; + return this; + } + + public boolean isAllColumns() { + return allColumns; + } + + public void setAllColumns(boolean b) { + allColumns = b; + } + + public NullHandling getNullHandling() { + return nullHandling; + } + + public Function setNullHandling(NullHandling nullHandling) { + this.nullHandling = nullHandling; + return this; + } + + public boolean isIgnoreNullsOutside() { + return ignoreNullsOutside; + } + + public Function setIgnoreNullsOutside(boolean ignoreNullsOutside) { + this.ignoreNullsOutside = ignoreNullsOutside; + return this; + } + + public Limit getLimit() { + return limit; + } + + public Function setLimit(Limit limit) { + this.limit = limit; + return this; + } + + public boolean isIgnoreNulls() { + return nullHandling != null && nullHandling == NullHandling.IGNORE_NULLS; + } + + /** + * This is at the moment only necessary for AnalyticExpression initialization and not for normal + * functions. Therefore there is no deparsing for it for normal functions. + */ + public void setIgnoreNulls(boolean ignoreNulls) { + this.nullHandling = ignoreNulls ? NullHandling.IGNORE_NULLS : null; + } + + public HavingClause getHavingClause() { + return havingClause; + } + + public Function setHavingClause(HavingClause havingClause) { + this.havingClause = havingClause; + return this; + } + + public Function setHavingClause(String havingType, Expression expression) { + this.havingClause = new HavingClause( + HavingClause.HavingType.valueOf(havingType.trim().toUpperCase()), expression); + return this; + } + + /** + * true if the function is "distinct" + * + * @return true if the function is "distinct" + */ + public boolean isDistinct() { + return distinct; + } + + public void setDistinct(boolean b) { + distinct = b; + } + + /** + * true if the function is "unique" + * + * @return true if the function is "unique" + */ + public boolean isUnique() { + return unique; + } + + public void setUnique(boolean b) { + unique = b; + } + + /** + * The list of parameters of the function (if any, else null) If the parameter is "*", + * allColumns is set to true + * + * @return the list of parameters of the function (if any, else null) + */ + public ExpressionList getParameters() { + return parameters; + } + + public void setParameters(Expression... expressions) { + if (expressions.length == 1 && expressions[0] instanceof ExpressionList) { + parameters = (ExpressionList) expressions[0]; + } else { + parameters = new ExpressionList<>(expressions); + } + } + + public void setParameters(ExpressionList list) { + parameters = list; + } + + /** + * the parameters might be named parameters, e.g. substring('foobar' from 2 for 3) + * + * @return the list of named parameters of the function (if any, else null) + */ + public NamedExpressionList getNamedParameters() { + return namedParameters; + } + + public void setNamedParameters(NamedExpressionList list) { + namedParameters = list; + } + + /** + * Return true if it's in the form "{fn function_body() }" + * + * @return true if it's java-escaped + */ + public boolean isEscaped() { + return isEscaped; + } + + public void setEscaped(boolean isEscaped) { + this.isEscaped = isEscaped; + } + + public Object getAttribute() { + return attributeExpression != null ? attributeExpression : attributeColumn; + } + + public void setAttribute(Expression attributeExpression) { + this.attributeExpression = attributeExpression; + } + + public void setAttribute(Column attributeColumn) { + attributeExpression = null; + this.attributeColumn = attributeColumn; + } + + @Deprecated + public String getAttributeName() { + return attributeColumn.toString(); + } + + public void setAttributeName(String attributeName) { + this.attributeColumn = new Column().withColumnName(attributeName); + } + + public Column getAttributeColumn() { + return attributeColumn; + } + + public Function withAttribute(Column attributeColumn) { + setAttribute(attributeColumn); + return this; + } + + public KeepExpression getKeep() { + return keep; + } + + public void setKeep(KeepExpression keep) { + this.keep = keep; + } + + @Override + @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"}) + public String toString() { + String params; + + if (parameters != null || namedParameters != null) { + if (parameters != null) { + StringBuilder b = new StringBuilder(); + b.append("("); + if (isDistinct()) { + b.append("DISTINCT "); + } else if (isUnique()) { + b.append("UNIQUE "); + } + if (isAllColumns()) { + b.append("ALL "); + } + b.append(parameters); + + if (havingClause != null) { + havingClause.appendTo(b); + } + + if (nullHandling != null && !isIgnoreNullsOutside()) { + switch (nullHandling) { + case IGNORE_NULLS: + b.append(" IGNORE NULLS"); + break; + case RESPECT_NULLS: + b.append(" RESPECT NULLS"); + break; + } + } + if (orderByElements != null) { + b.append(" ORDER BY "); + boolean comma = false; + for (OrderByElement orderByElement : orderByElements) { + if (comma) { + b.append(", "); + } else { + comma = true; + } + b.append(orderByElement); + } + } + if (limit != null) { + b.append(limit); + } + b.append(")"); + params = b.toString(); + } else { + params = namedParameters.toString(); + } + } else { + params = "()"; + } + + String ans = getName() + params; + + if (nullHandling != null && isIgnoreNullsOutside()) { + switch (nullHandling) { + case IGNORE_NULLS: + ans += " IGNORE NULLS"; + break; + case RESPECT_NULLS: + ans += " RESPECT NULLS"; + break; + } + } + + if (attributeExpression != null) { + ans += "." + attributeExpression; + } else if (attributeColumn != null) { + ans += "." + attributeColumn; + } + + if (keep != null) { + ans += " " + keep; + } + + if (isEscaped) { + ans = "{fn " + ans + "}"; + } + + return ans; + } + + public Function withAttribute(Expression attribute) { + this.setAttribute(attribute); + return this; + } + + @Deprecated + public Function withAttributeName(String attributeName) { + this.setAttributeName(attributeName); + return this; + } + + public Function withKeep(KeepExpression keep) { + this.setKeep(keep); + return this; + } + + public Function withIgnoreNulls(boolean ignoreNulls) { + this.setIgnoreNulls(ignoreNulls); + return this; + } + + public Function withParameters(ExpressionList parameters) { + this.setParameters(parameters); + return this; + } + + public Function withParameters(Expression... parameters) { + return withParameters(new ExpressionList<>(parameters)); + } + + public Function withNamedParameters(NamedExpressionList namedParameters) { + this.setNamedParameters(namedParameters); + return this; + } + + public Function withAllColumns(boolean allColumns) { + this.setAllColumns(allColumns); + return this; + } + + public Function withDistinct(boolean distinct) { + this.setDistinct(distinct); + return this; + } + + public Function withUnique(boolean unique) { + this.setUnique(unique); + return this; + } + + public List getOrderByElements() { + return orderByElements; + } + + public void setOrderByElements(List orderByElements) { + this.orderByElements = orderByElements; + } + + public E getAttribute(Class type) { + return type.cast(getAttribute()); + } + + public enum NullHandling { + IGNORE_NULLS, RESPECT_NULLS; + } + + public static class HavingClause extends ASTNodeAccessImpl implements Expression { + HavingType havingType; + Expression expression; + + public HavingClause(HavingType havingType, Expression expression) { + this.havingType = havingType; + this.expression = expression; + } + + public HavingType getHavingType() { + return havingType; + } + + public HavingClause setHavingType(HavingType havingType) { + this.havingType = havingType; + return this; + } + + public Expression getExpression() { + return expression; + } + + public HavingClause setExpression(Expression expression) { + this.expression = expression; + return this; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expression.accept(expressionVisitor, context); + } + + public StringBuilder appendTo(StringBuilder builder) { + builder.append(" HAVING ").append(havingType.name()).append(" ").append(expression); + return builder; + } + + @Override + public String toString() { + return appendTo(new StringBuilder()).toString(); + } + + enum HavingType { + MAX, MIN; + } + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/HexValue.java b/src/main/java/net/sf/jsqlparser/expression/HexValue.java new file mode 100644 index 0000000..cba9a3a --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/HexValue.java @@ -0,0 +1,93 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +import java.nio.charset.StandardCharsets; + +public class HexValue extends ASTNodeAccessImpl implements Expression { + + private String value; + + public HexValue() { + // empty constructor + } + + public HexValue(final String value) { + String val = value; + this.value = val; + } + + public static byte[] hexStringToByteArray(String s) { + int len = s.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + + Character.digit(s.charAt(i + 1), 16)); + } + return data; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public HexValue withValue(String value) { + this.setValue(value); + return this; + } + + @Override + public String toString() { + return value; + } + + public String getDigits() { + return value.toUpperCase().startsWith("0X") + ? value.substring(2) + : value.substring(2, value.length() - 1); + } + + public Long getLong() { + return Long.parseLong( + getDigits(), 16); + } + + public LongValue getLongValue() { + return new LongValue(getLong()); + } + + // `X'C3BC'` --> `'ü'` + public StringValue getStringValue() { + return new StringValue( + new String(hexStringToByteArray(getDigits()), StandardCharsets.UTF_8)); + } + + // `X'C3BC'` --> `\xC3\xBC` + public StringValue getBlob() { + StringBuilder builder = new StringBuilder(); + String digits = getDigits(); + int len = digits.length(); + for (int i = 0; i < len; i += 2) { + builder.append("\\x").append(digits.charAt(i)).append(digits.charAt(i + 1)); + } + return new StringValue(builder.toString()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/IntervalExpression.java b/src/main/java/net/sf/jsqlparser/expression/IntervalExpression.java new file mode 100644 index 0000000..9c028c7 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/IntervalExpression.java @@ -0,0 +1,96 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +import java.util.Objects; + +public class IntervalExpression extends ASTNodeAccessImpl implements Expression { + + private final boolean intervalKeyword; + private String parameter = null; + private String intervalType = null; + private Expression expression = null; + + public IntervalExpression() { + this(true); + } + + public IntervalExpression(boolean intervalKeyword) { + this.intervalKeyword = intervalKeyword; + } + + public IntervalExpression(int value, String type) { + this.parameter = null; + this.intervalKeyword = true; + this.expression = new LongValue(value); + this.intervalType = type; + } + + public boolean isUsingIntervalKeyword() { + return intervalKeyword; + } + + public String getParameter() { + return parameter; + } + + public void setParameter(String parameter) { + this.parameter = parameter; + } + + public String getIntervalType() { + return intervalType; + } + + public void setIntervalType(String intervalType) { + this.intervalType = intervalType; + } + + public Expression getExpression() { + return expression; + } + + public void setExpression(Expression expression) { + this.expression = expression; + } + + @Override + public String toString() { + return (intervalKeyword ? "INTERVAL " : "") + + Objects.toString(expression, parameter) + + (intervalType != null ? " " + intervalType : ""); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + public IntervalExpression withParameter(String parameter) { + this.setParameter(parameter); + return this; + } + + public IntervalExpression withIntervalType(String intervalType) { + this.setIntervalType(intervalType); + return this; + } + + public IntervalExpression withExpression(Expression expression) { + this.setExpression(expression); + return this; + } + + public E getExpression(Class type) { + return type.cast(getExpression()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/JdbcNamedParameter.java b/src/main/java/net/sf/jsqlparser/expression/JdbcNamedParameter.java new file mode 100644 index 0000000..84aa0b3 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/JdbcNamedParameter.java @@ -0,0 +1,55 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +public class JdbcNamedParameter extends ASTNodeAccessImpl implements Expression { + private String parameterCharacter = ":"; + private String name; + + public JdbcNamedParameter() {} + + public JdbcNamedParameter(String name) { + this.name = name; + } + + public String getParameterCharacter() { + return parameterCharacter; + } + + public JdbcNamedParameter setParameterCharacter(String parameterCharacter) { + this.parameterCharacter = parameterCharacter; + return this; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String toString() { + return parameterCharacter + name; + } + + public JdbcNamedParameter withName(String name) { + this.setName(name); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/JdbcParameter.java b/src/main/java/net/sf/jsqlparser/expression/JdbcParameter.java new file mode 100644 index 0000000..f512c47 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/JdbcParameter.java @@ -0,0 +1,88 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A '?' in a statement or a ?<number> e.g. ?4 + */ +public class JdbcParameter extends ASTNodeAccessImpl implements Expression { + + private String parameterCharacter = "?"; + private Integer index; + private boolean useFixedIndex = false; + + public JdbcParameter() {} + + public JdbcParameter(Integer index, boolean useFixedIndex, String parameterCharacter) { + this.index = index; + this.useFixedIndex = useFixedIndex; + this.parameterCharacter = parameterCharacter; + + // This is needed for Parameters starting with "$" like "$2" + // Those will contain the index in the parameterCharacter + final Pattern pattern = Pattern.compile("(\\$)(\\d*)"); + final Matcher matcher = pattern.matcher(parameterCharacter); + if (matcher.find() && matcher.groupCount() == 2) { + this.useFixedIndex = true; + this.parameterCharacter = matcher.group(1); + this.index = Integer.valueOf(matcher.group(2)); + } + } + + public String getParameterCharacter() { + return parameterCharacter; + } + + public JdbcParameter setParameterCharacter(String parameterCharacter) { + this.parameterCharacter = parameterCharacter; + return this; + } + + public Integer getIndex() { + return index; + } + + public void setIndex(Integer index) { + this.index = index; + } + + public boolean isUseFixedIndex() { + return useFixedIndex; + } + + public void setUseFixedIndex(boolean useFixedIndex) { + this.useFixedIndex = useFixedIndex; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String toString() { + return useFixedIndex ? parameterCharacter + index : parameterCharacter; + } + + public JdbcParameter withIndex(Integer index) { + this.setIndex(index); + return this; + } + + public JdbcParameter withUseFixedIndex(boolean useFixedIndex) { + this.setUseFixedIndex(useFixedIndex); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/JsonAggregateFunction.java b/src/main/java/net/sf/jsqlparser/expression/JsonAggregateFunction.java new file mode 100644 index 0000000..13e3ba2 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/JsonAggregateFunction.java @@ -0,0 +1,292 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import java.util.List; +import java.util.Objects; + +import net.sf.jsqlparser.statement.select.OrderByElement; + +/** + * @author Andreas Reichel + */ + +public class JsonAggregateFunction extends FilterOverImpl implements Expression { + private final OrderByClause expressionOrderBy = new OrderByClause(); + private JsonFunctionType functionType; + private Expression expression = null; + private boolean usingKeyKeyword = false; + private String key; + private boolean usingValueKeyword = false; + private Object value; + + private boolean usingFormatJson = false; + + private JsonAggregateOnNullType onNullType; + private JsonAggregateUniqueKeysType uniqueKeysType; + + + public JsonAggregateOnNullType getOnNullType() { + return onNullType; + } + + public void setOnNullType(JsonAggregateOnNullType onNullType) { + this.onNullType = onNullType; + } + + public JsonAggregateFunction withOnNullType(JsonAggregateOnNullType onNullType) { + this.setOnNullType(onNullType); + return this; + } + + public JsonAggregateUniqueKeysType getUniqueKeysType() { + return uniqueKeysType; + } + + public void setUniqueKeysType(JsonAggregateUniqueKeysType uniqueKeysType) { + this.uniqueKeysType = uniqueKeysType; + } + + public JsonAggregateFunction withUniqueKeysType(JsonAggregateUniqueKeysType uniqueKeysType) { + this.setUniqueKeysType(uniqueKeysType); + return this; + } + + public JsonFunctionType getType() { + return functionType; + } + + public void setType(JsonFunctionType type) { + this.functionType = Objects.requireNonNull(type, + "The Type of the JSON Aggregate Function must not be null"); + } + + public void setType(String typeName) { + this.functionType = JsonFunctionType + .valueOf(Objects + .requireNonNull(typeName, + "The Type of the JSON Aggregate Function must not be null") + .toUpperCase()); + } + + public JsonAggregateFunction withType(JsonFunctionType type) { + this.setType(type); + return this; + } + + public JsonAggregateFunction withType(String typeName) { + this.setType(typeName); + return this; + } + + public Expression getExpression() { + return expression; + } + + public void setExpression(Expression expression) { + this.expression = expression; + } + + public JsonAggregateFunction withExpression(Expression expression) { + this.setExpression(expression); + return this; + } + + public boolean isUsingKeyKeyword() { + return usingKeyKeyword; + } + + public void setUsingKeyKeyword(boolean usingKeyKeyword) { + this.usingKeyKeyword = usingKeyKeyword; + } + + public JsonAggregateFunction withUsingKeyKeyword(boolean usingKeyKeyword) { + this.setUsingKeyKeyword(usingKeyKeyword); + return this; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public JsonAggregateFunction withKey(String key) { + this.setKey(key); + return this; + } + + public boolean isUsingValueKeyword() { + return usingValueKeyword; + } + + public void setUsingValueKeyword(boolean usingValueKeyword) { + this.usingValueKeyword = usingValueKeyword; + } + + public JsonAggregateFunction withUsingValueKeyword(boolean usingValueKeyword) { + this.setUsingValueKeyword(usingValueKeyword); + return this; + } + + public Object getValue() { + return value; + } + + public void setValue(Object value) { + this.value = value; + } + + public JsonAggregateFunction withValue(Object value) { + this.setValue(value); + return this; + } + + public boolean isUsingFormatJson() { + return usingFormatJson; + } + + public void setUsingFormatJson(boolean usingFormatJson) { + this.usingFormatJson = usingFormatJson; + } + + public JsonAggregateFunction withUsingFormatJson(boolean usingFormatJson) { + this.setUsingFormatJson(usingFormatJson); + return this; + } + + public List getExpressionOrderByElements() { + return expressionOrderBy.getOrderByElements(); + } + + public void setExpressionOrderByElements(List orderByElements) { + expressionOrderBy.setOrderByElements(orderByElements); + } + + public JsonAggregateFunction withExpressionOrderByElements( + List orderByElements) { + this.setExpressionOrderByElements(orderByElements); + return this; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + // avoid countless Builder --> String conversion + @Override + public StringBuilder append(StringBuilder builder) { + switch (functionType) { + case OBJECT: + appendObject(builder); + break; + case ARRAY: + appendArray(builder); + break; + default: + // this should never happen really + throw new UnsupportedOperationException("JSON Aggregate Function of the type " + + functionType.name() + " has not been implemented yet."); + } + return builder; + } + + @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"}) + public StringBuilder appendObject(StringBuilder builder) { + builder.append("JSON_OBJECTAGG( "); + if (usingValueKeyword) { + if (usingKeyKeyword) { + builder.append("KEY "); + } + builder.append(key).append(" VALUE ").append(value); + } else { + builder.append(key).append(":").append(value); + } + + if (usingFormatJson) { + builder.append(" FORMAT JSON"); + } + + if (onNullType != null) { + switch (onNullType) { + case NULL: + builder.append(" NULL ON NULL"); + break; + case ABSENT: + builder.append(" ABSENT On NULL"); + break; + default: + // this should never happen + } + } + + if (uniqueKeysType != null) { + switch (uniqueKeysType) { + case WITH: + builder.append(" WITH UNIQUE KEYS"); + break; + case WITHOUT: + builder.append(" WITHOUT UNIQUE KEYS"); + break; + default: + // this should never happen + } + } + + builder.append(" ) "); + + + // FILTER( WHERE expression ) OVER windowNameOrSpecification + super.append(builder); + + return builder; + } + + @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"}) + public StringBuilder appendArray(StringBuilder builder) { + builder.append("JSON_ARRAYAGG( "); + builder.append(expression).append(" "); + + if (usingFormatJson) { + builder.append("FORMAT JSON "); + } + + expressionOrderBy.toStringOrderByElements(builder); + + if (onNullType != null) { + switch (onNullType) { + case NULL: + builder.append(" NULL ON NULL "); + break; + case ABSENT: + builder.append(" ABSENT On NULL "); + break; + default: + // "ON NULL" was ommitted + } + } + builder.append(") "); + + + // FILTER( WHERE expression ) OVER windowNameOrSpecification + super.append(builder); + + return builder; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + return append(builder).toString(); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/JsonAggregateOnNullType.java b/src/main/java/net/sf/jsqlparser/expression/JsonAggregateOnNullType.java new file mode 100644 index 0000000..898dad7 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/JsonAggregateOnNullType.java @@ -0,0 +1,37 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2021 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +/* + * Copyright (C) 2021 JSQLParser. + * + * This library is free software; you can redistribute it and/or modify it under the terms of the + * GNU Lesser General Public License as published by the Free Software Foundation; either version + * 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with this library; + * if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +package net.sf.jsqlparser.expression; + +/** + * @author Andreas Reichel + */ +public enum JsonAggregateOnNullType { + NULL, ABSENT; + + public static JsonAggregateOnNullType from(String type) { + return Enum.valueOf(JsonAggregateOnNullType.class, type.toUpperCase()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/JsonAggregateUniqueKeysType.java b/src/main/java/net/sf/jsqlparser/expression/JsonAggregateUniqueKeysType.java new file mode 100644 index 0000000..097aad5 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/JsonAggregateUniqueKeysType.java @@ -0,0 +1,37 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2021 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +/* + * Copyright (C) 2021 JSQLParser. + * + * This library is free software; you can redistribute it and/or modify it under the terms of the + * GNU Lesser General Public License as published by the Free Software Foundation; either version + * 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with this library; + * if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +package net.sf.jsqlparser.expression; + +/** + * @author Andreas Reichel + */ +public enum JsonAggregateUniqueKeysType { + WITH, WITHOUT; + + public static JsonAggregateUniqueKeysType from(String type) { + return Enum.valueOf(JsonAggregateUniqueKeysType.class, type.toUpperCase()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/JsonExpression.java b/src/main/java/net/sf/jsqlparser/expression/JsonExpression.java new file mode 100644 index 0000000..5b0f97b --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/JsonExpression.java @@ -0,0 +1,99 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +public class JsonExpression extends ASTNodeAccessImpl implements Expression { + private final List> idents = new ArrayList<>(); + private Expression expr; + + public JsonExpression() { + + } + + public JsonExpression(Expression expr) { + this.expr = expr; + } + + public JsonExpression(Expression expr, List> idents) { + this.expr = expr; + this.idents.addAll(idents); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + public Expression getExpression() { + return expr; + } + + public void setExpression(Expression expr) { + this.expr = expr; + } + + public void addIdent(String ident, String operator) { + idents.add(new AbstractMap.SimpleEntry<>(ident, operator)); + } + + public void addAllIdents(Collection> idents) { + this.idents.addAll(idents); + } + + public List> getIdentList() { + return idents; + } + + public Map.Entry getIdent(int index) { + return idents.get(index); + } + + @Deprecated + public List getIdents() { + ArrayList l = new ArrayList<>(); + for (Map.Entry ident : idents) { + l.add(ident.getKey()); + } + + return l; + } + + @Deprecated + public List getOperators() { + ArrayList l = new ArrayList<>(); + for (Map.Entry ident : idents) { + l.add(ident.getValue()); + } + return l; + } + + @Override + public String toString() { + StringBuilder b = new StringBuilder(); + b.append(expr.toString()); + for (Map.Entry ident : idents) { + b.append(ident.getValue()).append(ident.getKey()); + } + return b.toString(); + } + + public JsonExpression withExpression(Expression expr) { + this.setExpression(expr); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/JsonFunction.java b/src/main/java/net/sf/jsqlparser/expression/JsonFunction.java new file mode 100644 index 0000000..4422c1b --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/JsonFunction.java @@ -0,0 +1,264 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import java.util.ArrayList; +import java.util.Objects; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +/** + * @author Andreas Reichel + */ + +public class JsonFunction extends ASTNodeAccessImpl implements Expression { + private final ArrayList keyValuePairs = new ArrayList<>(); + private final ArrayList expressions = new ArrayList<>(); + private JsonFunctionType functionType; + private JsonAggregateOnNullType onNullType; + private JsonAggregateUniqueKeysType uniqueKeysType; + + public ArrayList getKeyValuePairs() { + return keyValuePairs; + } + + public ArrayList getExpressions() { + return expressions; + } + + public JsonKeyValuePair getKeyValuePair(int i) { + return keyValuePairs.get(i); + } + + public JsonFunctionExpression getExpression(int i) { + return expressions.get(i); + } + + public boolean add(JsonKeyValuePair keyValuePair) { + return keyValuePairs.add(keyValuePair); + } + + public void add(int i, JsonKeyValuePair keyValuePair) { + keyValuePairs.add(i, keyValuePair); + } + + public boolean add(JsonFunctionExpression expression) { + return expressions.add(expression); + } + + public void add(int i, JsonFunctionExpression expression) { + expressions.add(i, expression); + } + + public boolean isEmpty() { + return keyValuePairs.isEmpty(); + } + + public JsonAggregateOnNullType getOnNullType() { + return onNullType; + } + + public void setOnNullType(JsonAggregateOnNullType onNullType) { + this.onNullType = onNullType; + } + + public JsonFunction withOnNullType(JsonAggregateOnNullType onNullType) { + this.setOnNullType(onNullType); + return this; + } + + public JsonAggregateUniqueKeysType getUniqueKeysType() { + return uniqueKeysType; + } + + public void setUniqueKeysType(JsonAggregateUniqueKeysType uniqueKeysType) { + this.uniqueKeysType = uniqueKeysType; + } + + public JsonFunction withUniqueKeysType(JsonAggregateUniqueKeysType uniqueKeysType) { + this.setUniqueKeysType(uniqueKeysType); + return this; + } + + public JsonFunctionType getType() { + return functionType; + } + + public void setType(JsonFunctionType type) { + this.functionType = + Objects.requireNonNull(type, + "The Type of the JSON Aggregate Function must not be null"); + } + + public void setType(String typeName) { + this.functionType = JsonFunctionType.valueOf( + Objects.requireNonNull(typeName, + "The Type of the JSON Aggregate Function must not be null") + .toUpperCase()); + } + + public JsonFunction withType(JsonFunctionType type) { + this.setType(type); + return this; + } + + public JsonFunction withType(String typeName) { + this.setType(typeName); + return this; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + // avoid countless Builder --> String conversion + public StringBuilder append(StringBuilder builder) { + switch (functionType) { + case OBJECT: + appendObject(builder); + break; + case POSTGRES_OBJECT: + appendPostgresObject(builder); + break; + case MYSQL_OBJECT: + appendMySqlObject(builder); + break; + case ARRAY: + appendArray(builder); + break; + default: + // this should never happen really + } + return builder; + } + + @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"}) + public StringBuilder appendObject(StringBuilder builder) { + builder.append("JSON_OBJECT( "); + int i = 0; + for (JsonKeyValuePair keyValuePair : keyValuePairs) { + if (i > 0) { + builder.append(", "); + } + if (keyValuePair.isUsingValueKeyword()) { + if (keyValuePair.isUsingKeyKeyword()) { + builder.append("KEY "); + } + builder.append(keyValuePair.getKey()).append(" VALUE ") + .append(keyValuePair.getValue()); + } else { + builder.append(keyValuePair.getKey()).append(":").append(keyValuePair.getValue()); + } + + if (keyValuePair.isUsingFormatJson()) { + builder.append(" FORMAT JSON"); + } + i++; + } + + if (onNullType != null) { + switch (onNullType) { + case NULL: + builder.append(" NULL ON NULL"); + break; + case ABSENT: + builder.append(" ABSENT On NULL"); + break; + default: + // this should never happen + } + } + + if (uniqueKeysType != null) { + switch (uniqueKeysType) { + case WITH: + builder.append(" WITH UNIQUE KEYS"); + break; + case WITHOUT: + builder.append(" WITHOUT UNIQUE KEYS"); + break; + default: + // this should never happen + } + } + + builder.append(" ) "); + + return builder; + } + + + @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"}) + public StringBuilder appendPostgresObject(StringBuilder builder) { + builder.append("JSON_OBJECT( "); + for (JsonKeyValuePair keyValuePair : keyValuePairs) { + builder.append(keyValuePair.getKey()); + if (keyValuePair.getValue() != null) { + builder.append(", ").append(keyValuePair.getValue()); + } + } + builder.append(" ) "); + + return builder; + } + + public StringBuilder appendMySqlObject(StringBuilder builder) { + builder.append("JSON_OBJECT( "); + int i = 0; + for (JsonKeyValuePair keyValuePair : keyValuePairs) { + if (i > 0) { + builder.append(", "); + } + builder.append(keyValuePair.getKey()); + builder.append(", ").append(keyValuePair.getValue()); + i++; + } + builder.append(" ) "); + + return builder; + } + + @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"}) + public StringBuilder appendArray(StringBuilder builder) { + builder.append("JSON_ARRAY( "); + int i = 0; + + for (JsonFunctionExpression expr : expressions) { + if (i > 0) { + builder.append(", "); + } + expr.append(builder); + i++; + } + + if (onNullType != null) { + switch (onNullType) { + case NULL: + builder.append(" NULL ON NULL "); + break; + case ABSENT: + builder.append(" ABSENT ON NULL "); + break; + default: + // "ON NULL" was omitted + } + } + builder.append(") "); + + return builder; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + return append(builder).toString(); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/JsonFunctionExpression.java b/src/main/java/net/sf/jsqlparser/expression/JsonFunctionExpression.java new file mode 100644 index 0000000..5df7ad3 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/JsonFunctionExpression.java @@ -0,0 +1,54 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2021 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ + +package net.sf.jsqlparser.expression; + +import java.io.Serializable; +import java.util.Objects; + +/** + * @author Andreas Reichel + */ + +public class JsonFunctionExpression implements Serializable { + private final Expression expression; + + private boolean usingFormatJson = false; + + public JsonFunctionExpression(Expression expression) { + this.expression = Objects.requireNonNull(expression, "The EXPRESSION must not be null"); + } + + public Expression getExpression() { + return expression; + } + + public boolean isUsingFormatJson() { + return usingFormatJson; + } + + public void setUsingFormatJson(boolean usingFormatJson) { + this.usingFormatJson = usingFormatJson; + } + + public JsonFunctionExpression withUsingFormatJson(boolean usingFormatJson) { + this.setUsingFormatJson(usingFormatJson); + return this; + } + + public StringBuilder append(StringBuilder builder) { + return builder.append(getExpression()).append(isUsingFormatJson() ? " FORMAT JSON" : ""); + } + + @Override + public String toString() { + return append(new StringBuilder()).toString(); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/JsonFunctionType.java b/src/main/java/net/sf/jsqlparser/expression/JsonFunctionType.java new file mode 100644 index 0000000..43a33aa --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/JsonFunctionType.java @@ -0,0 +1,22 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2021 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ + +package net.sf.jsqlparser.expression; + +/** + * @author Andreas Reichel + */ +public enum JsonFunctionType { + OBJECT, ARRAY, POSTGRES_OBJECT, MYSQL_OBJECT; + + public static JsonFunctionType from(String type) { + return Enum.valueOf(JsonFunctionType.class, type.toUpperCase()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/JsonKeyValuePair.java b/src/main/java/net/sf/jsqlparser/expression/JsonKeyValuePair.java new file mode 100644 index 0000000..f111907 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/JsonKeyValuePair.java @@ -0,0 +1,126 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2021 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ + +package net.sf.jsqlparser.expression; + +import java.io.Serializable; +import java.util.Objects; + +/** + * @author Andreas Reichel + */ + +public class JsonKeyValuePair implements Serializable { + private final String key; + private final Object value; + private boolean usingKeyKeyword = false; + private boolean usingValueKeyword = false; + private boolean usingFormatJson = false; + + public JsonKeyValuePair(String key, Object value, boolean usingKeyKeyword, + boolean usingValueKeyword) { + this.key = Objects.requireNonNull(key, "The KEY of the Pair must not be null"); + this.value = value; + this.usingKeyKeyword = usingKeyKeyword; + this.usingValueKeyword = usingValueKeyword; + } + + public boolean isUsingKeyKeyword() { + return usingKeyKeyword; + } + + public void setUsingKeyKeyword(boolean usingKeyKeyword) { + this.usingKeyKeyword = usingKeyKeyword; + } + + public JsonKeyValuePair withUsingKeyKeyword(boolean usingKeyKeyword) { + this.setUsingKeyKeyword(usingKeyKeyword); + return this; + } + + public boolean isUsingValueKeyword() { + return usingValueKeyword; + } + + public void setUsingValueKeyword(boolean usingValueKeyword) { + this.usingValueKeyword = usingValueKeyword; + } + + public JsonKeyValuePair withUsingValueKeyword(boolean usingValueKeyword) { + this.setUsingValueKeyword(usingValueKeyword); + return this; + } + + public boolean isUsingFormatJson() { + return usingFormatJson; + } + + public void setUsingFormatJson(boolean usingFormatJson) { + this.usingFormatJson = usingFormatJson; + } + + public JsonKeyValuePair withUsingFormatJson(boolean usingFormatJson) { + this.setUsingFormatJson(usingFormatJson); + return this; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 83 * hash + Objects.hashCode(this.key); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final JsonKeyValuePair other = (JsonKeyValuePair) obj; + return Objects.equals(this.key, other.key); + } + + public String getKey() { + return key; + } + + public Object getValue() { + return value; + } + + public StringBuilder append(StringBuilder builder) { + if (isUsingValueKeyword()) { + if (isUsingKeyKeyword()) { + builder.append("KEY "); + } + builder.append(getKey()).append(" VALUE ").append(getValue()); + } else { + builder.append(getKey()).append(":").append(getValue()); + } + + if (isUsingFormatJson()) { + builder.append(" FORMAT JSON"); + } + + return builder; + } + + @Override + public String toString() { + return append(new StringBuilder()).toString(); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/expression/KeepExpression.java b/src/main/java/net/sf/jsqlparser/expression/KeepExpression.java new file mode 100644 index 0000000..b8e4932 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/KeepExpression.java @@ -0,0 +1,110 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; +import net.sf.jsqlparser.statement.select.OrderByElement; + +public class KeepExpression extends ASTNodeAccessImpl implements Expression { + + private String name; + private List orderByElements; + private boolean first = false; + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + public List getOrderByElements() { + return orderByElements; + } + + public void setOrderByElements(List orderByElements) { + this.orderByElements = orderByElements; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean isFirst() { + return first; + } + + public void setFirst(boolean first) { + this.first = first; + } + + @Override + public String toString() { + StringBuilder b = new StringBuilder(); + + b.append("KEEP (").append(name); + + b.append(" ").append(first ? "FIRST" : "LAST").append(" "); + toStringOrderByElements(b); + + b.append(")"); + + return b.toString(); + } + + private void toStringOrderByElements(StringBuilder b) { + if (orderByElements != null && !orderByElements.isEmpty()) { + b.append("ORDER BY "); + for (int i = 0; i < orderByElements.size(); i++) { + if (i > 0) { + b.append(", "); + } + b.append(orderByElements.get(i).toString()); + } + } + } + + public KeepExpression withName(String name) { + this.setName(name); + return this; + } + + public KeepExpression withOrderByElements(List orderByElements) { + this.setOrderByElements(orderByElements); + return this; + } + + public KeepExpression withFirst(boolean first) { + this.setFirst(first); + return this; + } + + public KeepExpression addOrderByElements(OrderByElement... orderByElements) { + List collection = + Optional.ofNullable(getOrderByElements()).orElseGet(ArrayList::new); + Collections.addAll(collection, orderByElements); + return this.withOrderByElements(collection); + } + + public KeepExpression addOrderByElements(Collection orderByElements) { + List collection = + Optional.ofNullable(getOrderByElements()).orElseGet(ArrayList::new); + collection.addAll(orderByElements); + return this.withOrderByElements(collection); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/LambdaExpression.java b/src/main/java/net/sf/jsqlparser/expression/LambdaExpression.java new file mode 100644 index 0000000..46d057f --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/LambdaExpression.java @@ -0,0 +1,72 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2024 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +import java.util.Arrays; +import java.util.List; + +public class LambdaExpression extends ASTNodeAccessImpl implements Expression { + private List identifiers; + private Expression expression; + + public LambdaExpression(String identifier, Expression expression) { + this.identifiers = Arrays.asList(identifier); + this.expression = expression; + } + + public LambdaExpression(List identifiers, Expression expression) { + this.identifiers = identifiers; + this.expression = expression; + } + + public List getIdentifiers() { + return identifiers; + } + + public LambdaExpression setIdentifiers(List identifiers) { + this.identifiers = identifiers; + return this; + } + + public Expression getExpression() { + return expression; + } + + public LambdaExpression setExpression(Expression expression) { + this.expression = expression; + return this; + } + + public StringBuilder appendTo(StringBuilder builder) { + if (identifiers.size() == 1) { + builder.append(identifiers.get(0)); + } else { + int i = 0; + builder.append("( "); + for (String s : identifiers) { + builder.append(i++ > 0 ? ", " : "").append(s); + } + builder.append(" )"); + } + return builder.append(" -> ").append(expression); + } + + @Override + public String toString() { + return appendTo(new StringBuilder()).toString(); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/LongValue.java b/src/main/java/net/sf/jsqlparser/expression/LongValue.java new file mode 100644 index 0000000..eeba186 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/LongValue.java @@ -0,0 +1,99 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import java.math.BigInteger; +import java.util.Objects; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +/** + * Every number without a point or an exponential format is a LongValue. + */ +public class LongValue extends ASTNodeAccessImpl implements Expression { + + private String stringValue; + + public LongValue() { + // empty constructor + } + + public LongValue(final String value) { + if (value == null || value.length() == 0) { + throw new IllegalArgumentException("value can neither be null nor empty."); + } + String val = value; + if (val.charAt(0) == '+') { + val = val.substring(1); + } + this.stringValue = val; + } + + public LongValue(long value) { + stringValue = String.valueOf(value); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + public long getValue() { + return Long.parseLong(stringValue); + } + + public void setValue(long d) { + stringValue = String.valueOf(d); + } + + public BigInteger getBigIntegerValue() { + return new BigInteger(stringValue); + } + + public LongValue withValue(long d) { + setValue(d); + return this; + } + + public String getStringValue() { + return stringValue; + } + + public void setStringValue(String string) { + stringValue = string; + } + + @Override + public String toString() { + return getStringValue(); + } + + public LongValue withStringValue(String stringValue) { + this.setStringValue(stringValue); + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + LongValue longValue = (LongValue) o; + return stringValue.equals(longValue.stringValue); + } + + @Override + public int hashCode() { + return Objects.hash(stringValue); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/MySQLGroupConcat.java b/src/main/java/net/sf/jsqlparser/expression/MySQLGroupConcat.java new file mode 100644 index 0000000..aa4a533 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/MySQLGroupConcat.java @@ -0,0 +1,124 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; +import net.sf.jsqlparser.statement.select.OrderByElement; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +public class MySQLGroupConcat extends ASTNodeAccessImpl implements Expression { + + private ExpressionList expressionList; + private boolean distinct = false; + private List orderByElements; + private String separator; + + public ExpressionList getExpressionList() { + return expressionList; + } + + public void setExpressionList(ExpressionList expressionList) { + this.expressionList = expressionList; + } + + public boolean isDistinct() { + return distinct; + } + + public void setDistinct(boolean distinct) { + this.distinct = distinct; + } + + public List getOrderByElements() { + return orderByElements; + } + + public void setOrderByElements(List orderByElements) { + this.orderByElements = orderByElements; + } + + public String getSeparator() { + return separator; + } + + public void setSeparator(String separator) { + this.separator = separator; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String toString() { + StringBuilder b = new StringBuilder(); + b.append("GROUP_CONCAT("); + if (isDistinct()) { + b.append("DISTINCT "); + } + b.append(expressionList); + if (orderByElements != null && !orderByElements.isEmpty()) { + b.append(" ORDER BY "); + for (int i = 0; i < orderByElements.size(); i++) { + if (i > 0) { + b.append(", "); + } + b.append(orderByElements.get(i).toString()); + } + } + if (separator != null) { + b.append(" SEPARATOR ").append(separator); + } + b.append(")"); + return b.toString(); + } + + public MySQLGroupConcat withExpressionList(ExpressionList expressionList) { + this.setExpressionList(expressionList); + return this; + } + + public MySQLGroupConcat withDistinct(boolean distinct) { + this.setDistinct(distinct); + return this; + } + + public MySQLGroupConcat withOrderByElements(List orderByElements) { + this.setOrderByElements(orderByElements); + return this; + } + + public MySQLGroupConcat withSeparator(String separator) { + this.setSeparator(separator); + return this; + } + + public MySQLGroupConcat addOrderByElements(OrderByElement... orderByElements) { + List collection = + Optional.ofNullable(getOrderByElements()).orElseGet(ArrayList::new); + Collections.addAll(collection, orderByElements); + return this.withOrderByElements(collection); + } + + public MySQLGroupConcat addOrderByElements( + Collection orderByElements) { + List collection = + Optional.ofNullable(getOrderByElements()).orElseGet(ArrayList::new); + collection.addAll(orderByElements); + return this.withOrderByElements(collection); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/MySQLIndexHint.java b/src/main/java/net/sf/jsqlparser/expression/MySQLIndexHint.java new file mode 100644 index 0000000..7360a4b --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/MySQLIndexHint.java @@ -0,0 +1,53 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import java.io.Serializable; +import java.util.List; + +public class MySQLIndexHint implements Serializable { + + private final String action; + private final String indexQualifier; + private final List indexNames; + + public MySQLIndexHint(String action, String indexQualifier, List indexNames) { + this.action = action; + this.indexQualifier = indexQualifier; + this.indexNames = indexNames; + } + + public String getAction() { + return action; + } + + public String getIndexQualifier() { + return indexQualifier; + } + + public List getIndexNames() { + return indexNames; + } + + @Override + public String toString() { + // use|ignore|force key|index (index1,...,indexN) + StringBuilder buffer = new StringBuilder(); + buffer.append(" ").append(action).append(" ").append(indexQualifier).append(" ("); + for (int i = 0; i < indexNames.size(); i++) { + if (i > 0) { + buffer.append(","); + } + buffer.append(indexNames.get(i)); + } + buffer.append(")"); + return buffer.toString(); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/NextValExpression.java b/src/main/java/net/sf/jsqlparser/expression/NextValExpression.java new file mode 100644 index 0000000..342ad13 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/NextValExpression.java @@ -0,0 +1,66 @@ +/* + * - #%L JSQLParser library %% Copyright (C) 2004 - 2019 JSQLParser %% Dual licensed under GNU LGPL + * 2.1 or Apache License 2.0 #L% + */ +package net.sf.jsqlparser.expression; + +import java.util.List; +import java.util.regex.Pattern; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +public class NextValExpression extends ASTNodeAccessImpl implements Expression { + + public static final Pattern NEXT_VALUE_PATTERN = + Pattern.compile("NEXT\\s+VALUE\\s+FOR", Pattern.CASE_INSENSITIVE); + private final List nameList; + private boolean usingNextValueFor = false; + + public NextValExpression(List nameList, String image) { + this.nameList = nameList; + // Test if we shall use NEXT VALUE FOR instead of NEXTVAL FOR + if (NEXT_VALUE_PATTERN.matcher(image).matches()) { + usingNextValueFor = true; + } + } + + public boolean isUsingNextValueFor() { + return usingNextValueFor; + } + + public void setUsingNextValueFor(boolean usingNextValueFor) { + this.usingNextValueFor = usingNextValueFor; + } + + public NextValExpression withNextValueFor(boolean usingNextValueFor) { + setUsingNextValueFor(usingNextValueFor); + return this; + } + + public List getNameList() { + return nameList; + } + + public String getName() { + StringBuilder b = new StringBuilder(); + for (String name : nameList) { + if (b.length() > 0) { + b.append("."); + } + b.append(name); + } + return b.toString(); + } + + @Override + public String toString() { + return (usingNextValueFor + ? "NEXT VALUE FOR " + : "NEXTVAL FOR ") + getName(); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/NotExpression.java b/src/main/java/net/sf/jsqlparser/expression/NotExpression.java new file mode 100644 index 0000000..bb2769f --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/NotExpression.java @@ -0,0 +1,75 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +/** + * It represents a "not " or "!" before an expression. + */ +public class NotExpression extends ASTNodeAccessImpl implements Expression { + + private Expression expression; + + private boolean exclamationMark = false; + + public NotExpression() { + // empty constructor + } + + public NotExpression(Expression expression) { + this(expression, false); + } + + public NotExpression(Expression expression, boolean useExclamationMark) { + setExpression(expression); + this.exclamationMark = useExclamationMark; + } + + public Expression getExpression() { + return expression; + } + + public final void setExpression(Expression expression) { + this.expression = expression; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String toString() { + return (exclamationMark ? "! " : "NOT ") + expression.toString(); + } + + public boolean isExclamationMark() { + return exclamationMark; + } + + public void setExclamationMark(boolean exclamationMark) { + this.exclamationMark = exclamationMark; + } + + public NotExpression withExpression(Expression expression) { + this.setExpression(expression); + return this; + } + + public NotExpression withExclamationMark(boolean exclamationMark) { + this.setExclamationMark(exclamationMark); + return this; + } + + public E getExpression(Class type) { + return type.cast(getExpression()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/NullValue.java b/src/main/java/net/sf/jsqlparser/expression/NullValue.java new file mode 100644 index 0000000..fb096ef --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/NullValue.java @@ -0,0 +1,25 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +public class NullValue extends ASTNodeAccessImpl implements Expression { + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String toString() { + return "NULL"; + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/NumericBind.java b/src/main/java/net/sf/jsqlparser/expression/NumericBind.java new file mode 100644 index 0000000..f38ff15 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/NumericBind.java @@ -0,0 +1,40 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +public class NumericBind extends ASTNodeAccessImpl implements Expression { + + private int bindId; + + public int getBindId() { + return bindId; + } + + public void setBindId(int bindId) { + this.bindId = bindId; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String toString() { + return ":" + bindId; + } + + public NumericBind withBindId(int bindId) { + this.setBindId(bindId); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/OracleHierarchicalExpression.java b/src/main/java/net/sf/jsqlparser/expression/OracleHierarchicalExpression.java new file mode 100644 index 0000000..6ec4c6a --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/OracleHierarchicalExpression.java @@ -0,0 +1,110 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +public class OracleHierarchicalExpression extends ASTNodeAccessImpl implements Expression { + + boolean connectFirst = false; + private Expression startExpression; + private Expression connectExpression; + private boolean noCycle = false; + + public Expression getStartExpression() { + return startExpression; + } + + public void setStartExpression(Expression startExpression) { + this.startExpression = startExpression; + } + + public Expression getConnectExpression() { + return connectExpression; + } + + public void setConnectExpression(Expression connectExpression) { + this.connectExpression = connectExpression; + } + + public boolean isNoCycle() { + return noCycle; + } + + public void setNoCycle(boolean noCycle) { + this.noCycle = noCycle; + } + + public boolean isConnectFirst() { + return connectFirst; + } + + public void setConnectFirst(boolean connectFirst) { + this.connectFirst = connectFirst; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String toString() { + StringBuilder b = new StringBuilder(); + if (isConnectFirst()) { + b.append(" CONNECT BY "); + if (isNoCycle()) { + b.append("NOCYCLE "); + } + b.append(connectExpression.toString()); + if (startExpression != null) { + b.append(" START WITH ").append(startExpression.toString()); + } + } else { + if (startExpression != null) { + b.append(" START WITH ").append(startExpression.toString()); + } + b.append(" CONNECT BY "); + if (isNoCycle()) { + b.append("NOCYCLE "); + } + b.append(connectExpression.toString()); + } + return b.toString(); + } + + public OracleHierarchicalExpression withStartExpression(Expression startExpression) { + this.setStartExpression(startExpression); + return this; + } + + public OracleHierarchicalExpression withConnectExpression(Expression connectExpression) { + this.setConnectExpression(connectExpression); + return this; + } + + public OracleHierarchicalExpression withNoCycle(boolean noCycle) { + this.setNoCycle(noCycle); + return this; + } + + public OracleHierarchicalExpression withConnectFirst(boolean connectFirst) { + this.setConnectFirst(connectFirst); + return this; + } + + public E getStartExpression(Class type) { + return type.cast(getStartExpression()); + } + + public E getConnectExpression(Class type) { + return type.cast(getConnectExpression()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/OracleHint.java b/src/main/java/net/sf/jsqlparser/expression/OracleHint.java new file mode 100644 index 0000000..35ba8ad --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/OracleHint.java @@ -0,0 +1,101 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; +import net.sf.jsqlparser.statement.select.ParenthesedSelect; +import net.sf.jsqlparser.statement.select.PlainSelect; +import net.sf.jsqlparser.statement.select.Select; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Oracle Hint Expression + */ +public class OracleHint extends ASTNodeAccessImpl implements Expression { + + private static final Pattern SINGLE_LINE = Pattern.compile("--\\+ *([^ ].*[^ ])"); + private static final Pattern MULTI_LINE = + Pattern.compile("\\/\\*\\+ *([^ ].*[^ ]) *\\*+\\/", Pattern.MULTILINE | Pattern.DOTALL); + + private String value; + private boolean singleLine = false; + + public static boolean isHintMatch(String comment) { + return SINGLE_LINE.matcher(comment).find() || MULTI_LINE.matcher(comment).find(); + } + + public static OracleHint getHintFromSelectBody(Select selectBody) { + + if (selectBody instanceof PlainSelect) { + return ((PlainSelect) selectBody).getOracleHint(); + } else if (selectBody instanceof ParenthesedSelect) { + return getHintFromSelectBody(((ParenthesedSelect) selectBody).getSelect()); + } else { + return null; + } + } + + public final void setComment(String comment) { + Matcher m; + m = SINGLE_LINE.matcher(comment); + if (m.find()) { + this.value = m.group(1); + this.singleLine = true; + return; + } + m = MULTI_LINE.matcher(comment); + if (m.find()) { + this.value = m.group(1); + this.singleLine = false; + } + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public boolean isSingleLine() { + return singleLine; + } + + public void setSingleLine(boolean singleLine) { + this.singleLine = singleLine; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String toString() { + if (singleLine) { + return "--+ " + value + "\n"; + } else { + return "/*+ " + value + " */"; + } + } + + public OracleHint withValue(String value) { + this.setValue(value); + return this; + } + + public OracleHint withSingleLine(boolean singleLine) { + this.setSingleLine(singleLine); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/OracleNamedFunctionParameter.java b/src/main/java/net/sf/jsqlparser/expression/OracleNamedFunctionParameter.java new file mode 100644 index 0000000..0dd76b5 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/OracleNamedFunctionParameter.java @@ -0,0 +1,55 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2021 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import java.util.Objects; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +/** + * @author Andreas Reichel + */ +public class OracleNamedFunctionParameter extends ASTNodeAccessImpl implements Expression { + private final String name; + private final Expression expression; + + public OracleNamedFunctionParameter(String name, Expression expression) { + this.name = Objects.requireNonNull(name, + "The NAME of the OracleNamedFunctionParameter must not be null."); + this.expression = Objects.requireNonNull(expression, + "The EXPRESSION of the OracleNamedFunctionParameter must not be null."); + } + + public String getName() { + return name; + } + + public Expression getExpression() { + return expression; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + public StringBuilder appendTo(StringBuilder builder) { + builder.append(name) + .append(" => ") + .append(expression); + + return builder; + } + + @Override + public String toString() { + return appendTo(new StringBuilder()).toString(); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/OrderByClause.java b/src/main/java/net/sf/jsqlparser/expression/OrderByClause.java new file mode 100644 index 0000000..fbf21de --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/OrderByClause.java @@ -0,0 +1,62 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import net.sf.jsqlparser.statement.select.OrderByElement; + +public class OrderByClause implements Serializable { + private List orderByElements; + + public List getOrderByElements() { + return orderByElements; + } + + public void setOrderByElements(List orderByElements) { + this.orderByElements = orderByElements; + } + + public void toStringOrderByElements(StringBuilder b) { + if (orderByElements != null && !orderByElements.isEmpty()) { + b.append("ORDER BY "); + for (int i = 0; i < orderByElements.size(); i++) { + if (i > 0) { + b.append(", "); + } + b.append(orderByElements.get(i).toString()); + } + } + } + + public OrderByClause withOrderByElements(List orderByElements) { + this.setOrderByElements(orderByElements); + return this; + } + + public OrderByClause addOrderByElements(OrderByElement... orderByElements) { + List collection = + Optional.ofNullable(getOrderByElements()).orElseGet(ArrayList::new); + Collections.addAll(collection, orderByElements); + return this.withOrderByElements(collection); + } + + public OrderByClause addOrderByElements(Collection orderByElements) { + List collection = + Optional.ofNullable(getOrderByElements()).orElseGet(ArrayList::new); + collection.addAll(orderByElements); + return this.withOrderByElements(collection); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/OverlapsCondition.java b/src/main/java/net/sf/jsqlparser/expression/OverlapsCondition.java new file mode 100644 index 0000000..09ccd61 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/OverlapsCondition.java @@ -0,0 +1,41 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2022 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +public class OverlapsCondition extends ASTNodeAccessImpl implements Expression { + private final ExpressionList left; + private final ExpressionList right; + + public OverlapsCondition(ExpressionList left, ExpressionList right) { + this.left = left; + this.right = right; + } + + public ExpressionList getLeft() { + return left; + } + + public ExpressionList getRight() { + return right; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String toString() { + return String.format("%s OVERLAPS %s", left.toString(), right.toString()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/Parenthesis.java b/src/main/java/net/sf/jsqlparser/expression/Parenthesis.java new file mode 100644 index 0000000..c162cd1 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/Parenthesis.java @@ -0,0 +1,37 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList; + +/** + * @deprecated This class is deprecated since version 5.0. Use {@link ParenthesedExpressionList} + * instead. The reason for deprecation is the ambiguity and redundancy. + */ +@Deprecated(since = "5.0", forRemoval = true) +public class Parenthesis extends ParenthesedExpressionList { + public Expression getExpression() { + return isEmpty() ? null : get(0); + } + + public Parenthesis setExpression(Expression expression) { + this.set(0, expression); + return this; + } + + public Parenthesis withExpression(Expression expression) { + return this.setExpression(expression); + } + + public E getExpression(Class type) { + return type.cast(getExpression()); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/expression/PartitionByClause.java b/src/main/java/net/sf/jsqlparser/expression/PartitionByClause.java new file mode 100644 index 0000000..2439750 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/PartitionByClause.java @@ -0,0 +1,53 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.statement.select.PlainSelect; + +import java.io.Serializable; + +public class PartitionByClause implements Serializable { + ExpressionList partitionExpressionList; + boolean brackets = false; + + public ExpressionList getPartitionExpressionList() { + return partitionExpressionList; + } + + public void setPartitionExpressionList(ExpressionList partitionExpressionList) { + setPartitionExpressionList(partitionExpressionList, false); + } + + public void setPartitionExpressionList(ExpressionList partitionExpressionList, + boolean brackets) { + this.partitionExpressionList = partitionExpressionList; + this.brackets = brackets; + } + + public void toStringPartitionBy(StringBuilder b) { + if (partitionExpressionList != null + && !partitionExpressionList.getExpressions().isEmpty()) { + b.append("PARTITION BY "); + b.append(PlainSelect.getStringList(partitionExpressionList.getExpressions(), true, + brackets)); + b.append(" "); + } + } + + public boolean isBrackets() { + return brackets; + } + + public PartitionByClause withPartitionExpressionList(ExpressionList partitionExpressionList) { + this.setPartitionExpressionList(partitionExpressionList); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/RangeExpression.java b/src/main/java/net/sf/jsqlparser/expression/RangeExpression.java new file mode 100644 index 0000000..dd05827 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/RangeExpression.java @@ -0,0 +1,50 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2023 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +public class RangeExpression extends ASTNodeAccessImpl implements Expression { + private Expression startExpression; + private Expression endExpression; + + public RangeExpression(Expression startExpression, Expression endExpression) { + this.startExpression = startExpression; + this.endExpression = endExpression; + } + + public Expression getStartExpression() { + return startExpression; + } + + public RangeExpression setStartExpression(Expression startExpression) { + this.startExpression = startExpression; + return this; + } + + public Expression getEndExpression() { + return endExpression; + } + + public RangeExpression setEndExpression(Expression endExpression) { + this.endExpression = endExpression; + return this; + } + + @Override + public String toString() { + return startExpression + ":" + endExpression; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/RowConstructor.java b/src/main/java/net/sf/jsqlparser/expression/RowConstructor.java new file mode 100644 index 0000000..2f5844e --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/RowConstructor.java @@ -0,0 +1,48 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList; + +public class RowConstructor extends ParenthesedExpressionList + implements Expression { + private String name = null; + + public RowConstructor() {} + + public RowConstructor(String name, ExpressionList expressionList) { + this.name = name; + addAll(expressionList); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return (name != null ? name : "") + super.toString(); + } + + public RowConstructor withName(String name) { + this.setName(name); + return this; + } + + @Override + public K accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/RowGetExpression.java b/src/main/java/net/sf/jsqlparser/expression/RowGetExpression.java new file mode 100644 index 0000000..0aaefa1 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/RowGetExpression.java @@ -0,0 +1,48 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2021 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +public final class RowGetExpression extends ASTNodeAccessImpl implements Expression { + private Expression expression; + private String columnName; + + public RowGetExpression(Expression expression, String columnName) { + this.expression = expression; + this.columnName = columnName; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String toString() { + return expression + "." + columnName; + } + + public Expression getExpression() { + return expression; + } + + public void setExpression(Expression expression) { + this.expression = expression; + } + + public String getColumnName() { + return columnName; + } + + public void setColumnName(String columnName) { + this.columnName = columnName; + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/SQLServerHints.java b/src/main/java/net/sf/jsqlparser/expression/SQLServerHints.java new file mode 100644 index 0000000..34d1496 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/SQLServerHints.java @@ -0,0 +1,67 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +public class SQLServerHints implements Serializable { + + private Boolean noLock; + private String indexName; + + public SQLServerHints() {} + + public SQLServerHints withNoLock() { + this.noLock = true; + return this; + } + + public Boolean getNoLock() { + return noLock; + } + + public void setNoLock(Boolean noLock) { + this.noLock = noLock; + } + + public String getIndexName() { + return indexName; + } + + public void setIndexName(String indexName) { + this.indexName = indexName; + } + + @Override + public String toString() { + List hints = new ArrayList<>(); + if (indexName != null) { + hints.add("INDEX (" + indexName + ")"); + } + if (Boolean.TRUE.equals(noLock)) { + hints.add("NOLOCK"); + } + return " WITH (" + + String.join(", ", hints) + + ")"; + } + + public SQLServerHints withNoLock(Boolean noLock) { + this.setNoLock(noLock); + return this; + } + + public SQLServerHints withIndexName(String indexName) { + this.setIndexName(indexName); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/SignedExpression.java b/src/main/java/net/sf/jsqlparser/expression/SignedExpression.java new file mode 100644 index 0000000..725d449 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/SignedExpression.java @@ -0,0 +1,73 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +/** + * It represents a "-" or "+" or "~" before an expression + */ +public class SignedExpression extends ASTNodeAccessImpl implements Expression { + + private char sign; + private Expression expression; + + public SignedExpression() { + // empty constructor + } + + public SignedExpression(char sign, Expression expression) { + setSign(sign); + setExpression(expression); + } + + public char getSign() { + return sign; + } + + public final void setSign(char sign) { + this.sign = sign; + if (sign != '+' && sign != '-' && sign != '~') { + throw new IllegalArgumentException("illegal sign character, only + - ~ allowed"); + } + } + + public Expression getExpression() { + return expression; + } + + public final void setExpression(Expression expression) { + this.expression = expression; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String toString() { + return getSign() + expression.toString(); + } + + public SignedExpression withSign(char sign) { + this.setSign(sign); + return this; + } + + public SignedExpression withExpression(Expression expression) { + this.setExpression(expression); + return this; + } + + public E getExpression(Class type) { + return type.cast(getExpression()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/SpannerInterleaveIn.java b/src/main/java/net/sf/jsqlparser/expression/SpannerInterleaveIn.java new file mode 100644 index 0000000..0bf4992 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/SpannerInterleaveIn.java @@ -0,0 +1,80 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2021 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.schema.Table; + +import java.util.Collections; +import java.util.List; + +public class SpannerInterleaveIn { + + private Table table; + private OnDelete onDelete; + + public SpannerInterleaveIn() { + + } + + public SpannerInterleaveIn(Table table, OnDelete action) { + setTable(table); + setOnDelete(action); + } + + public SpannerInterleaveIn(List nameParts) { + this(new Table(nameParts), null); + } + + public SpannerInterleaveIn(String tableName) { + this(Collections.singletonList(tableName)); + } + + public Table getTable() { + return table; + } + + public void setTable(Table table) { + this.table = table; + } + + public OnDelete getOnDelete() { + return onDelete; + } + + public void setOnDelete(OnDelete action) { + this.onDelete = action; + } + + @Override + public String toString() { + return "INTERLEAVE IN PARENT " + getTable().getName() + + (getOnDelete() == null ? "" + : " ON DELETE " + + (getOnDelete() == OnDelete.CASCADE ? "CASCADE" : "NO ACTION")); + } + + public SpannerInterleaveIn withTable(Table table) { + this.setTable(table); + return this; + } + + public SpannerInterleaveIn withOnDelete(OnDelete action) { + this.setOnDelete(action); + return this; + } + + public enum OnDelete { + CASCADE, NO_ACTION; + + public static OnDelete from(String action) { + return Enum.valueOf(OnDelete.class, action.toUpperCase()); + } + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/StringValue.java b/src/main/java/net/sf/jsqlparser/expression/StringValue.java new file mode 100644 index 0000000..ec77f54 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/StringValue.java @@ -0,0 +1,119 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +/** + * A string as in 'example_string' + */ +public final class StringValue extends ASTNodeAccessImpl implements Expression { + + public static final List ALLOWED_PREFIXES = + Arrays.asList("N", "U", "E", "R", "B", "RB", "_utf8", "Q"); + private String value = ""; + private String prefix = null; + + public StringValue() { + // empty constructor + } + + public StringValue(String escapedValue) { + // removing "'" at the start and at the end + if (escapedValue.length() >= 2 && escapedValue.startsWith("'") + && escapedValue.endsWith("'")) { + value = escapedValue.substring(1, escapedValue.length() - 1); + return; + } + + if (escapedValue.length() > 2) { + for (String p : ALLOWED_PREFIXES) { + if (escapedValue.length() > p.length() + && escapedValue.substring(0, p.length()).equalsIgnoreCase(p) + && escapedValue.charAt(p.length()) == '\'') { + this.prefix = p; + value = escapedValue.substring(p.length() + 1, escapedValue.length() - 1); + return; + } + } + } + + value = escapedValue; + } + + public String getValue() { + return value; + } + + public void setValue(String string) { + value = string; + } + + public String getPrefix() { + return prefix; + } + + public void setPrefix(String prefix) { + this.prefix = prefix; + } + + public String getNotExcapedValue() { + StringBuilder buffer = new StringBuilder(value); + int index = 0; + int deletesNum = 0; + while ((index = value.indexOf("''", index)) != -1) { + buffer.deleteCharAt(index - deletesNum); + index += 2; + deletesNum++; + } + return buffer.toString(); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String toString() { + return (prefix != null ? prefix : "") + "'" + value + "'"; + } + + public StringValue withPrefix(String prefix) { + this.setPrefix(prefix); + return this; + } + + public StringValue withValue(String value) { + this.setValue(value); + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + StringValue that = (StringValue) o; + return Objects.equals(value, that.value) && Objects.equals(prefix, that.prefix); + } + + @Override + public int hashCode() { + return Objects.hash(value, prefix); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/StructType.java b/src/main/java/net/sf/jsqlparser/expression/StructType.java new file mode 100644 index 0000000..4bd38a6 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/StructType.java @@ -0,0 +1,198 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2024 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; +import net.sf.jsqlparser.statement.create.table.ColDataType; +import net.sf.jsqlparser.statement.select.SelectItem; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/* + * STRUCT + * + * Type Declaration Meaning STRUCT Simple struct with a single unnamed 64-bit integer field. + * STRUCT Simple struct with a single parameterized string field named x. STRUCT> A struct with a nested struct named x inside it. The struct x has two + * fields, y and z, both of which are 64-bit integers. STRUCT> A struct + * containing an array named inner_array that holds 64-bit integer elements. + * + * STRUCT( expr1 [AS field_name] [, ... ]) + * + * Syntax Output Type STRUCT(1,2,3) STRUCT STRUCT() STRUCT<> STRUCT('abc') + * STRUCT STRUCT(1, t.str_col) STRUCT STRUCT(1 AS a, 'abc' AS b) + * STRUCT STRUCT(str_col AS abc) STRUCT + * + * + * Struct Literals + * + * Example Output Type (1, 2, 3) STRUCT (1, 'abc') STRUCT + * STRUCT(1 AS foo, 'abc' AS bar) STRUCT STRUCT(1, 'abc') + * STRUCT STRUCT(1) STRUCT STRUCT(1) STRUCT + * + */ +public class StructType extends ASTNodeAccessImpl implements Expression { + private Dialect dialect = Dialect.BIG_QUERY;; + private String keyword; + private List> parameters; + private List> arguments; + + public StructType(Dialect dialect, String keyword, + List> parameters, + List> arguments) { + this.dialect = dialect; + this.keyword = keyword; + this.parameters = parameters; + this.arguments = arguments; + } + + public StructType(Dialect dialect, List> parameters, + List> arguments) { + this.dialect = dialect; + this.parameters = parameters; + this.arguments = arguments; + } + + public StructType(Dialect dialect, List> arguments) { + this.dialect = dialect; + this.arguments = arguments; + } + + public Dialect getDialect() { + return dialect; + } + + public StructType setDialect(Dialect dialect) { + this.dialect = dialect; + return this; + } + + public String getKeyword() { + return keyword; + } + + public StructType setKeyword(String keyword) { + this.keyword = keyword; + return this; + } + + public List> getParameters() { + return parameters; + } + + public StructType setParameters(List> parameters) { + this.parameters = parameters; + return this; + } + + public List> getArguments() { + return arguments; + } + + public StructType setArguments(List> arguments) { + this.arguments = arguments; + return this; + } + + public StructType add(Expression expression, String aliasName) { + if (arguments == null) { + arguments = new ArrayList<>(); + } + arguments.add(new SelectItem<>(expression, aliasName)); + + return this; + } + + public StringBuilder appendTo(StringBuilder builder) { + if (dialect != Dialect.DUCKDB && keyword != null) { + builder.append(keyword); + } + + if (dialect != Dialect.DUCKDB && parameters != null && !parameters.isEmpty()) { + builder.append("<"); + int i = 0; + + for (Map.Entry e : parameters) { + if (0 < i++) { + builder.append(","); + } + // optional name + if (e.getKey() != null && !e.getKey().isEmpty()) { + builder.append(e.getKey()).append(" "); + } + + // mandatory type + builder.append(e.getValue()); + } + + builder.append(">"); + } + + if (arguments != null && !arguments.isEmpty()) { + + if (dialect == Dialect.DUCKDB) { + builder.append("{ "); + int i = 0; + for (SelectItem e : arguments) { + if (0 < i++) { + builder.append(","); + } + builder.append(e.getAlias().getName()); + builder.append(":"); + builder.append(e.getExpression()); + } + builder.append(" }"); + } else { + builder.append("("); + int i = 0; + for (SelectItem e : arguments) { + if (0 < i++) { + builder.append(","); + } + e.appendTo(builder); + } + + builder.append(")"); + } + } + + if (dialect == Dialect.DUCKDB && parameters != null && !parameters.isEmpty()) { + builder.append("::STRUCT( "); + int i = 0; + + for (Map.Entry e : parameters) { + if (0 < i++) { + builder.append(","); + } + builder.append(e.getKey()).append(" "); + builder.append(e.getValue()); + } + builder.append(")"); + } + + return builder; + } + + @Override + public String toString() { + return appendTo(new StringBuilder()).toString(); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + public enum Dialect { + BIG_QUERY, DUCKDB + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/TimeKeyExpression.java b/src/main/java/net/sf/jsqlparser/expression/TimeKeyExpression.java new file mode 100644 index 0000000..759da0e --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/TimeKeyExpression.java @@ -0,0 +1,48 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +public class TimeKeyExpression extends ASTNodeAccessImpl implements Expression { + + private String stringValue; + + public TimeKeyExpression() { + // empty constructor + } + + public TimeKeyExpression(final String value) { + this.stringValue = value; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + public String getStringValue() { + return stringValue; + } + + public void setStringValue(String string) { + stringValue = string; + } + + @Override + public String toString() { + return getStringValue(); + } + + public TimeKeyExpression withStringValue(String stringValue) { + this.setStringValue(stringValue); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/TimeValue.java b/src/main/java/net/sf/jsqlparser/expression/TimeValue.java new file mode 100644 index 0000000..2b3f743 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/TimeValue.java @@ -0,0 +1,56 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +import java.sql.Time; + +/** + * A Time in the form {t 'hh:mm:ss'} + */ +public class TimeValue extends ASTNodeAccessImpl implements Expression { + + private Time value; + + public TimeValue() { + // empty constructor + } + + public TimeValue(String value) { + if (value == null || value.isEmpty()) { + throw new IllegalArgumentException("value can neither be null nor empty."); + } + this.value = Time.valueOf(value.substring(1, value.length() - 1)); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + public Time getValue() { + return value; + } + + public void setValue(Time d) { + value = d; + } + + @Override + public String toString() { + return "{t '" + value + "'}"; + } + + public TimeValue withValue(Time value) { + this.setValue(value); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/TimestampValue.java b/src/main/java/net/sf/jsqlparser/expression/TimestampValue.java new file mode 100644 index 0000000..a060823 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/TimestampValue.java @@ -0,0 +1,74 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +import java.sql.Timestamp; +import java.util.Objects; + +/** + * A Timestamp in the form {ts 'yyyy-mm-dd hh:mm:ss.f . . .'} + */ +public final class TimestampValue extends ASTNodeAccessImpl implements Expression { + + private static final char QUOTATION = '\''; + private Timestamp value; + private String rawValue; + + public TimestampValue() { + // empty constructor + } + + public TimestampValue(String value) { + // if (value == null) { + // throw new IllegalArgumentException("null string"); + // } else { + // setRawValue(value); + // } + setRawValue(Objects.requireNonNull(value, "The Timestamp string value must not be null.")); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + public Timestamp getValue() { + return value; + } + + public void setValue(Timestamp d) { + value = d; + } + + public String getRawValue() { + return rawValue; + } + + public void setRawValue(String rawValue) { + this.rawValue = rawValue; + if (rawValue.charAt(0) == QUOTATION) { + this.value = Timestamp.valueOf(rawValue.substring(1, rawValue.length() - 1)); + } else { + this.value = Timestamp.valueOf(rawValue.substring(0, rawValue.length())); + } + } + + @Override + public String toString() { + return "{ts '" + value + "'}"; + } + + public TimestampValue withValue(Timestamp value) { + this.setValue(value); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/TimezoneExpression.java b/src/main/java/net/sf/jsqlparser/expression/TimezoneExpression.java new file mode 100644 index 0000000..a67572a --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/TimezoneExpression.java @@ -0,0 +1,63 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +import java.util.Arrays; +import java.util.List; + +public class TimezoneExpression extends ASTNodeAccessImpl implements Expression { + + private final ExpressionList timezoneExpressions = new ExpressionList<>(); + private Expression leftExpression; + + public TimezoneExpression() { + leftExpression = null; + } + + public TimezoneExpression(Expression leftExpression, Expression... timezoneExpressions) { + this.leftExpression = leftExpression; + this.timezoneExpressions.addAll(Arrays.asList(timezoneExpressions)); + } + + public Expression getLeftExpression() { + return leftExpression; + } + + public TimezoneExpression setLeftExpression(Expression expression) { + this.leftExpression = expression; + return this; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + public List getTimezoneExpressions() { + return timezoneExpressions; + } + + public void addTimezoneExpression(Expression... timezoneExpr) { + this.timezoneExpressions.addAll(Arrays.asList(timezoneExpr)); + } + + @Override + public String toString() { + StringBuilder returnValue = new StringBuilder(leftExpression.toString()); + for (Expression expr : timezoneExpressions) { + returnValue.append(" AT TIME ZONE ").append(expr.toString()); + } + + return returnValue.toString(); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/TranscodingFunction.java b/src/main/java/net/sf/jsqlparser/expression/TranscodingFunction.java new file mode 100644 index 0000000..343579e --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/TranscodingFunction.java @@ -0,0 +1,112 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2023 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; +import net.sf.jsqlparser.statement.create.table.ColDataType; + +public class TranscodingFunction extends ASTNodeAccessImpl implements Expression { + private boolean isTranscodeStyle = true; + private ColDataType colDataType; + private Expression expression; + private String transcodingName; + + public TranscodingFunction(Expression expression, String transcodingName) { + this.expression = expression; + this.transcodingName = transcodingName; + } + + public TranscodingFunction(ColDataType colDataType, Expression expression, + String transcodingName) { + this.colDataType = colDataType; + this.expression = expression; + this.transcodingName = transcodingName; + this.isTranscodeStyle = false; + } + + public TranscodingFunction() { + this(null, null); + } + + public Expression getExpression() { + return expression; + } + + public void setExpression(Expression expression) { + this.expression = expression; + } + + public TranscodingFunction withExpression(Expression expression) { + this.setExpression(expression); + return this; + } + + public String getTranscodingName() { + return transcodingName; + } + + public void setTranscodingName(String transcodingName) { + this.transcodingName = transcodingName; + } + + public TranscodingFunction withTranscodingName(String transcodingName) { + this.setTranscodingName(transcodingName); + return this; + + } + + public ColDataType getColDataType() { + return colDataType; + } + + public TranscodingFunction setColDataType(ColDataType colDataType) { + this.colDataType = colDataType; + return this; + } + + public boolean isTranscodeStyle() { + return isTranscodeStyle; + } + + public TranscodingFunction setTranscodeStyle(boolean transcodeStyle) { + isTranscodeStyle = transcodeStyle; + return this; + } + + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + public StringBuilder appendTo(StringBuilder builder) { + if (isTranscodeStyle) { + return builder + .append("CONVERT( ") + .append(expression) + .append(" USING ") + .append(transcodingName) + .append(" )"); + } else { + return builder + .append("CONVERT( ") + .append(colDataType) + .append(", ") + .append(expression) + .append(transcodingName != null && !transcodingName.isEmpty() + ? ", " + transcodingName + : "") + .append(" )"); + } + } + + @Override + public String toString() { + return appendTo(new StringBuilder()).toString(); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/TrimFunction.java b/src/main/java/net/sf/jsqlparser/expression/TrimFunction.java new file mode 100644 index 0000000..e8ea030 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/TrimFunction.java @@ -0,0 +1,124 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2023 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +public class TrimFunction extends ASTNodeAccessImpl implements Expression { + private TrimSpecification trimSpecification; + private Expression expression; + private Expression fromExpression; + private boolean isUsingFromKeyword; + + public TrimFunction(TrimSpecification trimSpecification, + Expression expression, + Expression fromExpression, + boolean isUsingFromKeyword) { + + this.trimSpecification = trimSpecification; + this.expression = expression; + this.fromExpression = fromExpression; + this.isUsingFromKeyword = isUsingFromKeyword; + } + + public TrimFunction() { + this(null, null, null, false); + } + + public TrimSpecification getTrimSpecification() { + return trimSpecification; + } + + public void setTrimSpecification(TrimSpecification trimSpecification) { + this.trimSpecification = trimSpecification; + } + + public TrimFunction withTrimSpecification(TrimSpecification trimSpecification) { + this.setTrimSpecification(trimSpecification); + return this; + } + + public Expression getExpression() { + return expression; + } + + public void setExpression(Expression expression) { + this.expression = expression; + } + + public TrimFunction withExpression(Expression expression) { + this.setExpression(expression); + return this; + } + + public Expression getFromExpression() { + return fromExpression; + } + + public void setFromExpression(Expression fromExpression) { + if (fromExpression == null) { + setUsingFromKeyword(false); + } + this.fromExpression = fromExpression; + } + + public TrimFunction withFromExpression(Expression fromExpression) { + this.setFromExpression(fromExpression); + return this; + } + + public boolean isUsingFromKeyword() { + return isUsingFromKeyword; + } + + public void setUsingFromKeyword(boolean useFromKeyword) { + isUsingFromKeyword = useFromKeyword; + } + + public TrimFunction withUsingFromKeyword(boolean useFromKeyword) { + this.setUsingFromKeyword(useFromKeyword); + return this; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + public StringBuilder appendTo(StringBuilder builder) { + builder.append("Trim("); + + if (trimSpecification != null) { + builder.append(" ").append(trimSpecification.name()); + } + + if (expression != null) { + builder.append(" ").append(expression); + } + + if (fromExpression != null) { + builder + .append(isUsingFromKeyword ? " FROM " : ", ") + .append(fromExpression); + } + builder.append(" )"); + + return builder; + } + + @Override + public String toString() { + return appendTo(new StringBuilder()).toString(); + } + + public enum TrimSpecification { + LEADING, TRAILING, BOTH + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/UserVariable.java b/src/main/java/net/sf/jsqlparser/expression/UserVariable.java new file mode 100644 index 0000000..ece2bd1 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/UserVariable.java @@ -0,0 +1,65 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +/** + * Simple uservariables like @test. + */ +public class UserVariable extends ASTNodeAccessImpl implements Expression { + + private String name; + private boolean doubleAdd = false; + + public UserVariable() { + // empty constructor + } + + public UserVariable(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + public boolean isDoubleAdd() { + return doubleAdd; + } + + public void setDoubleAdd(boolean doubleAdd) { + this.doubleAdd = doubleAdd; + } + + @Override + public String toString() { + return "@" + (doubleAdd ? "@" : "") + name; + } + + public UserVariable withName(String name) { + this.setName(name); + return this; + } + + public UserVariable withDoubleAdd(boolean doubleAdd) { + this.setDoubleAdd(doubleAdd); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/VariableAssignment.java b/src/main/java/net/sf/jsqlparser/expression/VariableAssignment.java new file mode 100644 index 0000000..2ad7732 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/VariableAssignment.java @@ -0,0 +1,58 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +/** + * Assignment to a user variable like in select @a = 5. + */ +public class VariableAssignment extends ASTNodeAccessImpl implements Expression { + + private UserVariable variable; + private String operation; + private Expression expression; + + public UserVariable getVariable() { + return variable; + } + + public void setVariable(UserVariable variable) { + this.variable = variable; + } + + public String getOperation() { + return operation; + } + + public void setOperation(String operation) { + this.operation = operation; + } + + public Expression getExpression() { + return expression; + } + + public void setExpression(Expression expression) { + this.expression = expression; + } + + @Override + public String toString() { + return variable.toString() + " " + operation + " " + expression.toString(); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + +} diff --git a/src/main/java/net/sf/jsqlparser/expression/WhenClause.java b/src/main/java/net/sf/jsqlparser/expression/WhenClause.java new file mode 100644 index 0000000..5ed58de --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/WhenClause.java @@ -0,0 +1,78 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +/** + * A clause of following syntax: WHEN condition THEN expression. Which is part of a CaseExpression. + */ +public class WhenClause extends ASTNodeAccessImpl implements Expression { + + private Expression whenExpression; + private Expression thenExpression; + + public WhenClause() {} + + public WhenClause(Expression whenExpression, Expression thenExpression) { + this.whenExpression = whenExpression; + this.thenExpression = thenExpression; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + public Expression getThenExpression() { + return thenExpression; + } + + public void setThenExpression(Expression thenExpression) { + this.thenExpression = thenExpression; + } + + /** + * @return Returns the whenExpression. + */ + public Expression getWhenExpression() { + return whenExpression; + } + + /** + * @param whenExpression The whenExpression to set. + */ + public void setWhenExpression(Expression whenExpression) { + this.whenExpression = whenExpression; + } + + @Override + public String toString() { + return "WHEN " + whenExpression + " THEN " + thenExpression; + } + + public WhenClause withWhenExpression(Expression whenExpression) { + this.setWhenExpression(whenExpression); + return this; + } + + public WhenClause withThenExpression(Expression thenExpression) { + this.setThenExpression(thenExpression); + return this; + } + + public E getThenExpression(Class type) { + return type.cast(getThenExpression()); + } + + public E getWhenExpression(Class type) { + return type.cast(getWhenExpression()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/WindowDefinition.java b/src/main/java/net/sf/jsqlparser/expression/WindowDefinition.java new file mode 100644 index 0000000..a5df35a --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/WindowDefinition.java @@ -0,0 +1,95 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2022 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import java.io.Serializable; +import java.util.List; + +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.statement.select.OrderByElement; + +public class WindowDefinition implements Serializable { + + + final OrderByClause orderBy = new OrderByClause(); + final PartitionByClause partitionBy = new PartitionByClause(); + WindowElement windowElement = null; + private String windowName; + + public OrderByClause getOrderBy() { + return orderBy; + } + + public PartitionByClause getPartitionBy() { + return partitionBy; + } + + public WindowElement getWindowElement() { + return windowElement; + } + + public void setWindowElement(WindowElement windowElement) { + this.windowElement = windowElement; + } + + public List getOrderByElements() { + return orderBy.getOrderByElements(); + } + + public void setOrderByElements(List orderByElements) { + orderBy.setOrderByElements(orderByElements); + } + + public ExpressionList getPartitionExpressionList() { + return partitionBy.getPartitionExpressionList(); + } + + public void setPartitionExpressionList(ExpressionList partitionExpressionList) { + setPartitionExpressionList(partitionExpressionList, false); + } + + public void setPartitionExpressionList(ExpressionList partitionExpressionList, + boolean brackets) { + partitionBy.setPartitionExpressionList(partitionExpressionList, brackets); + } + + public String getWindowName() { + return windowName; + } + + public void setWindowName(String windowName) { + this.windowName = windowName; + } + + public WindowDefinition withWindowName(String windowName) { + setWindowName(windowName); + return this; + } + + @Override + public String toString() { + StringBuilder b = new StringBuilder(); + if (windowName != null) { + b.append(windowName).append(" AS "); + } + b.append("("); + partitionBy.toStringPartitionBy(b); + orderBy.toStringOrderByElements(b); + + if (windowElement != null) { + if (orderBy.getOrderByElements() != null) { + b.append(' '); + } + b.append(windowElement); + } + b.append(")"); + return b.toString(); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/WindowElement.java b/src/main/java/net/sf/jsqlparser/expression/WindowElement.java new file mode 100644 index 0000000..97260ce --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/WindowElement.java @@ -0,0 +1,80 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import java.io.Serializable; + +public class WindowElement implements Serializable { + + private Type type; + private WindowOffset offset; + private WindowRange range; + + public Type getType() { + return type; + } + + public void setType(Type type) { + this.type = type; + } + + public WindowOffset getOffset() { + return offset; + } + + public void setOffset(WindowOffset offset) { + this.offset = offset; + } + + public WindowRange getRange() { + return range; + } + + public void setRange(WindowRange range) { + this.range = range; + } + + @Override + public String toString() { + StringBuilder buffer = new StringBuilder(type.toString()); + + if (offset != null) { + buffer.append(offset.toString()); + } else if (range != null) { + buffer.append(range.toString()); + } + + return buffer.toString(); + } + + public WindowElement withType(Type type) { + this.setType(type); + return this; + } + + public WindowElement withOffset(WindowOffset offset) { + this.setOffset(offset); + return this; + } + + public WindowElement withRange(WindowRange range) { + this.setRange(range); + return this; + } + + public enum Type { + ROWS, RANGE; + + public static Type from(String type) { + return Enum.valueOf(Type.class, type.toUpperCase()); + } + } + +} diff --git a/src/main/java/net/sf/jsqlparser/expression/WindowOffset.java b/src/main/java/net/sf/jsqlparser/expression/WindowOffset.java new file mode 100644 index 0000000..0303b4f --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/WindowOffset.java @@ -0,0 +1,86 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import java.io.Serializable; + +public class WindowOffset implements Serializable { + + private Expression expression; + private Type type; + + public Expression getExpression() { + return expression; + } + + public void setExpression(Expression expression) { + this.expression = expression; + } + + public Type getType() { + return type; + } + + public void setType(Type type) { + this.type = type; + } + + @Override + public String toString() { + StringBuilder buffer = new StringBuilder(); + if (expression != null) { + buffer.append(' ').append(expression); + if (type != null) { + buffer.append(' '); + buffer.append(type); + } + } else { + if (type != null) { + switch (type) { + case PRECEDING: + buffer.append(" UNBOUNDED PRECEDING"); + break; + case FOLLOWING: + buffer.append(" UNBOUNDED FOLLOWING"); + break; + case CURRENT: + buffer.append(" CURRENT ROW"); + break; + default: + break; + } + } + } + return buffer.toString(); + } + + public WindowOffset withExpression(Expression expression) { + this.setExpression(expression); + return this; + } + + public WindowOffset withType(Type type) { + this.setType(type); + return this; + } + + public E getExpression(Class type) { + return type.cast(getExpression()); + } + + public enum Type { + PRECEDING, FOLLOWING, CURRENT, EXPR; + + public static Type from(String type) { + return Enum.valueOf(Type.class, type.toUpperCase()); + } + } + +} diff --git a/src/main/java/net/sf/jsqlparser/expression/WindowRange.java b/src/main/java/net/sf/jsqlparser/expression/WindowRange.java new file mode 100644 index 0000000..fc5f0af --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/WindowRange.java @@ -0,0 +1,52 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import java.io.Serializable; + +public class WindowRange implements Serializable { + + private WindowOffset start; + private WindowOffset end; + + public WindowOffset getEnd() { + return end; + } + + public void setEnd(WindowOffset end) { + this.end = end; + } + + public WindowOffset getStart() { + return start; + } + + public void setStart(WindowOffset start) { + this.start = start; + } + + @Override + public String toString() { + return " BETWEEN" + + start + + " AND" + + end; + } + + public WindowRange withStart(WindowOffset start) { + this.setStart(start); + return this; + } + + public WindowRange withEnd(WindowOffset end) { + this.setEnd(end); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/XMLSerializeExpr.java b/src/main/java/net/sf/jsqlparser/expression/XMLSerializeExpr.java new file mode 100644 index 0000000..d2534e6 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/XMLSerializeExpr.java @@ -0,0 +1,62 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import java.util.List; + +import static java.util.stream.Collectors.joining; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; +import net.sf.jsqlparser.statement.create.table.ColDataType; +import net.sf.jsqlparser.statement.select.OrderByElement; + +public class XMLSerializeExpr extends ASTNodeAccessImpl implements Expression { + + private Expression expression; + private List orderByElements; + private ColDataType dataType; + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + public Expression getExpression() { + return expression; + } + + public void setExpression(Expression expression) { + this.expression = expression; + } + + public List getOrderByElements() { + return orderByElements; + } + + public void setOrderByElements(List orderByElements) { + this.orderByElements = orderByElements; + } + + public ColDataType getDataType() { + return dataType; + } + + public void setDataType(ColDataType dataType) { + this.dataType = dataType; + } + + @Override + public String toString() { + return "xmlserialize(xmlagg(xmltext(" + expression + ")" + + (orderByElements != null ? " ORDER BY " + orderByElements.stream() + .map(OrderByElement::toString).collect(joining(", ")) : "") + + ") AS " + dataType + ")"; + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/Addition.java b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/Addition.java new file mode 100644 index 0000000..fca4fd0 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/Addition.java @@ -0,0 +1,43 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.arithmetic; + +import net.sf.jsqlparser.expression.BinaryExpression; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class Addition extends BinaryExpression { + + public Addition() {} + + public Addition(Expression leftExpression, Expression rightExpression) { + super(leftExpression, rightExpression); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String getStringExpression() { + return "+"; + } + + @Override + public Addition withLeftExpression(Expression arg0) { + return (Addition) super.withLeftExpression(arg0); + } + + @Override + public Addition withRightExpression(Expression arg0) { + return (Addition) super.withRightExpression(arg0); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/BitwiseAnd.java b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/BitwiseAnd.java new file mode 100644 index 0000000..f5eac93 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/BitwiseAnd.java @@ -0,0 +1,43 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.arithmetic; + +import net.sf.jsqlparser.expression.BinaryExpression; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class BitwiseAnd extends BinaryExpression { + + public BitwiseAnd() {} + + public BitwiseAnd(Expression leftExpression, Expression rightExpression) { + super(leftExpression, rightExpression); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String getStringExpression() { + return "&"; + } + + @Override + public BitwiseAnd withLeftExpression(Expression arg0) { + return (BitwiseAnd) super.withLeftExpression(arg0); + } + + @Override + public BitwiseAnd withRightExpression(Expression arg0) { + return (BitwiseAnd) super.withRightExpression(arg0); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/BitwiseLeftShift.java b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/BitwiseLeftShift.java new file mode 100644 index 0000000..0f2094c --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/BitwiseLeftShift.java @@ -0,0 +1,43 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.arithmetic; + +import net.sf.jsqlparser.expression.BinaryExpression; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class BitwiseLeftShift extends BinaryExpression { + + public BitwiseLeftShift() {} + + public BitwiseLeftShift(Expression leftExpression, Expression rightExpression) { + super(leftExpression, rightExpression); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String getStringExpression() { + return "<<"; + } + + @Override + public BitwiseLeftShift withLeftExpression(Expression arg0) { + return (BitwiseLeftShift) super.withLeftExpression(arg0); + } + + @Override + public BitwiseLeftShift withRightExpression(Expression arg0) { + return (BitwiseLeftShift) super.withRightExpression(arg0); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/BitwiseOr.java b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/BitwiseOr.java new file mode 100644 index 0000000..2d11d9e --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/BitwiseOr.java @@ -0,0 +1,43 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.arithmetic; + +import net.sf.jsqlparser.expression.BinaryExpression; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class BitwiseOr extends BinaryExpression { + + public BitwiseOr() {} + + public BitwiseOr(Expression leftExpression, Expression rightExpression) { + super(leftExpression, rightExpression); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String getStringExpression() { + return "|"; + } + + @Override + public BitwiseOr withLeftExpression(Expression arg0) { + return (BitwiseOr) super.withLeftExpression(arg0); + } + + @Override + public BitwiseOr withRightExpression(Expression arg0) { + return (BitwiseOr) super.withRightExpression(arg0); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/BitwiseRightShift.java b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/BitwiseRightShift.java new file mode 100644 index 0000000..13b5dbd --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/BitwiseRightShift.java @@ -0,0 +1,43 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.arithmetic; + +import net.sf.jsqlparser.expression.BinaryExpression; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class BitwiseRightShift extends BinaryExpression { + + public BitwiseRightShift() {} + + public BitwiseRightShift(Expression leftExpression, Expression rightExpression) { + super(leftExpression, rightExpression); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String getStringExpression() { + return ">>"; + } + + @Override + public BitwiseRightShift withLeftExpression(Expression arg0) { + return (BitwiseRightShift) super.withLeftExpression(arg0); + } + + @Override + public BitwiseRightShift withRightExpression(Expression arg0) { + return (BitwiseRightShift) super.withRightExpression(arg0); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/BitwiseXor.java b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/BitwiseXor.java new file mode 100644 index 0000000..89cbcb6 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/BitwiseXor.java @@ -0,0 +1,43 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.arithmetic; + +import net.sf.jsqlparser.expression.BinaryExpression; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class BitwiseXor extends BinaryExpression { + + public BitwiseXor() {} + + public BitwiseXor(Expression leftExpression, Expression rightExpression) { + super(leftExpression, rightExpression); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String getStringExpression() { + return "^"; + } + + @Override + public BitwiseXor withLeftExpression(Expression arg0) { + return (BitwiseXor) super.withLeftExpression(arg0); + } + + @Override + public BitwiseXor withRightExpression(Expression arg0) { + return (BitwiseXor) super.withRightExpression(arg0); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/Concat.java b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/Concat.java new file mode 100644 index 0000000..1ad8bb1 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/Concat.java @@ -0,0 +1,43 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.arithmetic; + +import net.sf.jsqlparser.expression.BinaryExpression; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class Concat extends BinaryExpression { + + public Concat() {} + + public Concat(Expression leftExpression, Expression rightExpression) { + super(leftExpression, rightExpression); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String getStringExpression() { + return "||"; + } + + @Override + public Concat withLeftExpression(Expression arg0) { + return (Concat) super.withLeftExpression(arg0); + } + + @Override + public Concat withRightExpression(Expression arg0) { + return (Concat) super.withRightExpression(arg0); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/Division.java b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/Division.java new file mode 100644 index 0000000..a963d63 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/Division.java @@ -0,0 +1,43 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.arithmetic; + +import net.sf.jsqlparser.expression.BinaryExpression; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class Division extends BinaryExpression { + + public Division() {} + + public Division(Expression leftExpression, Expression rightExpression) { + super(leftExpression, rightExpression); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String getStringExpression() { + return "/"; + } + + @Override + public Division withLeftExpression(Expression arg0) { + return (Division) super.withLeftExpression(arg0); + } + + @Override + public Division withRightExpression(Expression arg0) { + return (Division) super.withRightExpression(arg0); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/IntegerDivision.java b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/IntegerDivision.java new file mode 100644 index 0000000..73489eb --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/IntegerDivision.java @@ -0,0 +1,43 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.arithmetic; + +import net.sf.jsqlparser.expression.BinaryExpression; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class IntegerDivision extends BinaryExpression { + + public IntegerDivision() {} + + public IntegerDivision(Expression leftExpression, Expression rightExpression) { + super(leftExpression, rightExpression); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String getStringExpression() { + return "DIV"; + } + + @Override + public IntegerDivision withLeftExpression(Expression arg0) { + return (IntegerDivision) super.withLeftExpression(arg0); + } + + @Override + public IntegerDivision withRightExpression(Expression arg0) { + return (IntegerDivision) super.withRightExpression(arg0); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/Modulo.java b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/Modulo.java new file mode 100644 index 0000000..63482d2 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/Modulo.java @@ -0,0 +1,46 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.arithmetic; + +import net.sf.jsqlparser.expression.BinaryExpression; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; + +/** + * Modulo expression (a % b). + */ +public class Modulo extends BinaryExpression { + + public Modulo() {} + + public Modulo(Expression leftExpression, Expression rightExpression) { + super(leftExpression, rightExpression); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String getStringExpression() { + return "%"; + } + + @Override + public Modulo withLeftExpression(Expression arg0) { + return (Modulo) super.withLeftExpression(arg0); + } + + @Override + public Modulo withRightExpression(Expression arg0) { + return (Modulo) super.withRightExpression(arg0); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/Multiplication.java b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/Multiplication.java new file mode 100644 index 0000000..774db57 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/Multiplication.java @@ -0,0 +1,43 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.arithmetic; + +import net.sf.jsqlparser.expression.BinaryExpression; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class Multiplication extends BinaryExpression { + + public Multiplication() {} + + public Multiplication(Expression leftExpression, Expression rightExpression) { + super(leftExpression, rightExpression); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String getStringExpression() { + return "*"; + } + + @Override + public Multiplication withLeftExpression(Expression arg0) { + return (Multiplication) super.withLeftExpression(arg0); + } + + @Override + public Multiplication withRightExpression(Expression arg0) { + return (Multiplication) super.withRightExpression(arg0); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/Subtraction.java b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/Subtraction.java new file mode 100644 index 0000000..dc2b74c --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/Subtraction.java @@ -0,0 +1,43 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.arithmetic; + +import net.sf.jsqlparser.expression.BinaryExpression; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class Subtraction extends BinaryExpression { + + public Subtraction() {} + + public Subtraction(Expression leftExpression, Expression rightExpression) { + super(leftExpression, rightExpression); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String getStringExpression() { + return "-"; + } + + @Override + public Subtraction withLeftExpression(Expression arg0) { + return (Subtraction) super.withLeftExpression(arg0); + } + + @Override + public Subtraction withRightExpression(Expression arg0) { + return (Subtraction) super.withRightExpression(arg0); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/conditional/AndExpression.java b/src/main/java/net/sf/jsqlparser/expression/operators/conditional/AndExpression.java new file mode 100644 index 0000000..0d9d363 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/conditional/AndExpression.java @@ -0,0 +1,60 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.conditional; + +import net.sf.jsqlparser.expression.BinaryExpression; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class AndExpression extends BinaryExpression { + private boolean useOperator = false; + + public AndExpression() { + // nothing + } + + public AndExpression(Expression leftExpression, Expression rightExpression) { + setLeftExpression(leftExpression); + setRightExpression(rightExpression); + } + + public boolean isUseOperator() { + return useOperator; + } + + public void setUseOperator(boolean useOperator) { + this.useOperator = useOperator; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String getStringExpression() { + return useOperator ? "&&" : "AND"; + } + + public AndExpression withUseOperator(boolean useOperator) { + this.setUseOperator(useOperator); + return this; + } + + @Override + public AndExpression withLeftExpression(Expression arg0) { + return (AndExpression) super.withLeftExpression(arg0); + } + + @Override + public AndExpression withRightExpression(Expression arg0) { + return (AndExpression) super.withRightExpression(arg0); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/conditional/OrExpression.java b/src/main/java/net/sf/jsqlparser/expression/operators/conditional/OrExpression.java new file mode 100644 index 0000000..f08cc7d --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/conditional/OrExpression.java @@ -0,0 +1,47 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.conditional; + +import net.sf.jsqlparser.expression.BinaryExpression; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class OrExpression extends BinaryExpression { + + public OrExpression() { + // nothing + } + + public OrExpression(Expression leftExpression, Expression rightExpression) { + setLeftExpression(leftExpression); + setRightExpression(rightExpression); + } + + @Override + public OrExpression withLeftExpression(Expression expression) { + return (OrExpression) super.withLeftExpression(expression); + } + + @Override + public OrExpression withRightExpression(Expression expression) { + return (OrExpression) super.withRightExpression(expression); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String getStringExpression() { + return "OR"; + } + +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/conditional/XorExpression.java b/src/main/java/net/sf/jsqlparser/expression/operators/conditional/XorExpression.java new file mode 100644 index 0000000..9bab8b5 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/conditional/XorExpression.java @@ -0,0 +1,47 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2021 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.conditional; + +import net.sf.jsqlparser.expression.BinaryExpression; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class XorExpression extends BinaryExpression { + + public XorExpression() { + // nothing + } + + public XorExpression(Expression leftExpression, Expression rightExpression) { + setLeftExpression(leftExpression); + setRightExpression(rightExpression); + } + + @Override + public XorExpression withLeftExpression(Expression expression) { + return (XorExpression) super.withLeftExpression(expression); + } + + @Override + public XorExpression withRightExpression(Expression expression) { + return (XorExpression) super.withRightExpression(expression); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String getStringExpression() { + return "XOR"; + } + +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/Between.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/Between.java new file mode 100644 index 0000000..1cd9972 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/Between.java @@ -0,0 +1,101 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +/** + * A "BETWEEN" expr1 expr2 statement + */ +public class Between extends ASTNodeAccessImpl implements Expression { + + private Expression leftExpression; + private boolean not = false; + private Expression betweenExpressionStart; + private Expression betweenExpressionEnd; + + public Expression getBetweenExpressionEnd() { + return betweenExpressionEnd; + } + + public void setBetweenExpressionEnd(Expression expression) { + betweenExpressionEnd = expression; + } + + public Expression getBetweenExpressionStart() { + return betweenExpressionStart; + } + + public void setBetweenExpressionStart(Expression expression) { + betweenExpressionStart = expression; + } + + public Expression getLeftExpression() { + return leftExpression; + } + + public void setLeftExpression(Expression expression) { + leftExpression = expression; + } + + public boolean isNot() { + return not; + } + + public void setNot(boolean b) { + not = b; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String toString() { + return leftExpression + " " + (not ? "NOT " : "") + "BETWEEN " + betweenExpressionStart + + " AND " + + betweenExpressionEnd; + } + + public Between withLeftExpression(Expression leftExpression) { + this.setLeftExpression(leftExpression); + return this; + } + + public Between withNot(boolean not) { + this.setNot(not); + return this; + } + + public Between withBetweenExpressionStart(Expression betweenExpressionStart) { + this.setBetweenExpressionStart(betweenExpressionStart); + return this; + } + + public Between withBetweenExpressionEnd(Expression betweenExpressionEnd) { + this.setBetweenExpressionEnd(betweenExpressionEnd); + return this; + } + + public E getBetweenExpressionEnd(Class type) { + return type.cast(getBetweenExpressionEnd()); + } + + public E getBetweenExpressionStart(Class type) { + return type.cast(getBetweenExpressionStart()); + } + + public E getLeftExpression(Class type) { + return type.cast(getLeftExpression()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/ComparisonOperator.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ComparisonOperator.java new file mode 100644 index 0000000..d150811 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ComparisonOperator.java @@ -0,0 +1,52 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.Expression; + +public abstract class ComparisonOperator extends OldOracleJoinBinaryExpression { + + private final String operator; + + public ComparisonOperator(String operator) { + this.operator = operator; + } + + public ComparisonOperator(String operator, Expression left, Expression right) { + this(operator); + setLeftExpression(left); + setRightExpression(right); + } + + @Override + public String getStringExpression() { + return operator; + } + + @Override + public ComparisonOperator withLeftExpression(Expression arg0) { + return (ComparisonOperator) super.withLeftExpression(arg0); + } + + @Override + public ComparisonOperator withRightExpression(Expression arg0) { + return (ComparisonOperator) super.withRightExpression(arg0); + } + + @Override + public ComparisonOperator withOldOracleJoinSyntax(int oldOracleJoinSyntax) { + return (ComparisonOperator) super.withOldOracleJoinSyntax(oldOracleJoinSyntax); + } + + @Override + public ComparisonOperator withOraclePriorPosition(int oraclePriorPosition) { + return (ComparisonOperator) super.withOraclePriorPosition(oraclePriorPosition); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/ContainedBy.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ContainedBy.java new file mode 100644 index 0000000..15562a4 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ContainedBy.java @@ -0,0 +1,25 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2023 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class ContainedBy extends ComparisonOperator { + + public ContainedBy() { + super("<&"); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } +} + diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/Contains.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/Contains.java new file mode 100644 index 0000000..dbfda10 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/Contains.java @@ -0,0 +1,25 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2023 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class Contains extends ComparisonOperator { + + public Contains() { + super("&>"); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } +} + diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/DoubleAnd.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/DoubleAnd.java new file mode 100644 index 0000000..372e123 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/DoubleAnd.java @@ -0,0 +1,24 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2023 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class DoubleAnd extends ComparisonOperator { + + public DoubleAnd() { + super("&&"); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/EqualsTo.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/EqualsTo.java new file mode 100644 index 0000000..8f80f1b --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/EqualsTo.java @@ -0,0 +1,51 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class EqualsTo extends ComparisonOperator { + + public EqualsTo() { + super("="); + } + + public EqualsTo(Expression left, Expression right) { + this(); + setLeftExpression(left); + setRightExpression(right); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public EqualsTo withLeftExpression(Expression expression) { + return (EqualsTo) super.withLeftExpression(expression); + } + + @Override + public EqualsTo withRightExpression(Expression expression) { + return (EqualsTo) super.withRightExpression(expression); + } + + @Override + public EqualsTo withOldOracleJoinSyntax(int arg0) { + return (EqualsTo) super.withOldOracleJoinSyntax(arg0); + } + + @Override + public EqualsTo withOraclePriorPosition(int arg0) { + return (EqualsTo) super.withOraclePriorPosition(arg0); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/ExcludesExpression.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ExcludesExpression.java new file mode 100644 index 0000000..847ff08 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ExcludesExpression.java @@ -0,0 +1,78 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +public class ExcludesExpression extends ASTNodeAccessImpl implements Expression { + + private Expression leftExpression; + private Expression rightExpression; + + public ExcludesExpression() {} + + public ExcludesExpression(Expression leftExpression, Expression rightExpression) { + this.leftExpression = leftExpression; + this.rightExpression = rightExpression; + } + + public Expression getLeftExpression() { + return leftExpression; + } + + public final void setLeftExpression(Expression expression) { + leftExpression = expression; + } + + public ExcludesExpression withLeftExpression(Expression expression) { + this.setLeftExpression(expression); + return this; + } + + public Expression getRightExpression() { + return rightExpression; + } + + public void setRightExpression(Expression rightExpression) { + this.rightExpression = rightExpression; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String toString() { + StringBuilder statementBuilder = new StringBuilder(); + statementBuilder.append(leftExpression); + + statementBuilder.append(" "); + statementBuilder.append("EXCLUDES "); + + statementBuilder.append(rightExpression); + return statementBuilder.toString(); + } + + public ExcludesExpression withRightExpression(Expression rightExpression) { + this.setRightExpression(rightExpression); + return this; + } + + public E getLeftExpression(Class type) { + return type.cast(getLeftExpression()); + } + + public E getRightExpression(Class type) { + return type.cast(getRightExpression()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/ExistsExpression.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ExistsExpression.java new file mode 100644 index 0000000..c8d83f1 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ExistsExpression.java @@ -0,0 +1,64 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +public class ExistsExpression extends ASTNodeAccessImpl implements Expression { + + protected Expression rightExpression; + protected boolean not = false; + + public Expression getRightExpression() { + return rightExpression; + } + + public void setRightExpression(Expression expression) { + rightExpression = expression; + } + + public boolean isNot() { + return not; + } + + public void setNot(boolean b) { + not = b; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + public String getStringExpression() { + return (not ? "NOT " : "") + "EXISTS"; + } + + @Override + public String toString() { + return getStringExpression() + " " + rightExpression.toString(); + } + + public ExistsExpression withRightExpression(Expression rightExpression) { + this.setRightExpression(rightExpression); + return this; + } + + public ExistsExpression withNot(boolean not) { + this.setNot(not); + return this; + } + + public E getRightExpression(Class type) { + return type.cast(getRightExpression()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/ExpressionList.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ExpressionList.java new file mode 100644 index 0000000..2abbae1 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ExpressionList.java @@ -0,0 +1,112 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; +import net.sf.jsqlparser.parser.SimpleNode; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +/** + * A list of expressions, as in SELECT A FROM TAB WHERE B IN (expr1,expr2,expr3) + */ +public class ExpressionList extends ArrayList + implements Expression, Serializable { + private transient SimpleNode node; + + public ExpressionList(Collection expressions) { + addAll(expressions); + } + + public ExpressionList(List expressions) { + super(expressions); + } + + public ExpressionList(T... expressions) { + this(Arrays.asList(expressions)); + } + + @Deprecated + public boolean isUsingBrackets() { + return false; + } + + @Deprecated + public List getExpressions() { + return this; + } + + @Deprecated + public void setExpressions(List expressions) { + this.clear(); + this.addAll(expressions); + } + + public ExpressionList addExpression(T expression) { + this.add(expression); + return this; + } + + public ExpressionList addExpressions(T... expressions) { + addAll(Arrays.asList(expressions)); + return this; + } + + public ExpressionList addExpressions(Collection expressions) { + addAll(expressions); + return this; + } + + public ExpressionList withExpressions(T... expressions) { + this.clear(); + return addExpressions(expressions); + } + + public ExpressionList withExpressions(Collection expressions) { + this.clear(); + return addExpressions(expressions); + } + + public StringBuilder appendTo(StringBuilder builder) { + for (int i = 0; i < size(); i++) { + if (i > 0) { + builder.append(", "); + } + builder.append(get(i)); + } + return builder; + } + + @Override + public String toString() { + return appendTo(new StringBuilder()).toString(); + } + + + @Override + public SimpleNode getASTNode() { + return node; + } + + @Override + public void setASTNode(SimpleNode node) { + this.node = node; + } + + @Override + public K accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/FullTextSearch.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/FullTextSearch.java new file mode 100644 index 0000000..f191ae1 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/FullTextSearch.java @@ -0,0 +1,114 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; +import net.sf.jsqlparser.expression.JdbcNamedParameter; +import net.sf.jsqlparser.expression.JdbcParameter; +import net.sf.jsqlparser.expression.StringValue; +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; +import net.sf.jsqlparser.schema.Column; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.Optional; + +public class FullTextSearch extends ASTNodeAccessImpl implements Expression { + + private ExpressionList _matchColumns; + private Expression _againstValue; + private String _searchModifier; + + public FullTextSearch() { + + } + + public ExpressionList getMatchColumns() { + return this._matchColumns; + } + + public void setMatchColumns(ExpressionList columns) { + this._matchColumns = columns; + } + + public Expression getAgainstValue() { + return this._againstValue; + } + + public void setAgainstValue(StringValue val) { + this._againstValue = val; + } + + public void setAgainstValue(JdbcNamedParameter val) { + this._againstValue = val; + } + + public void setAgainstValue(JdbcParameter val) { + this._againstValue = val; + } + + public String getSearchModifier() { + return this._searchModifier; + } + + public void setSearchModifier(String val) { + this._searchModifier = val; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String toString() { + // Build a list of matched columns + String columnsListCommaSeperated = ""; + Iterator iterator = this._matchColumns.iterator(); + while (iterator.hasNext()) { + Column col = iterator.next(); + columnsListCommaSeperated += col.getFullyQualifiedName(); + if (iterator.hasNext()) { + columnsListCommaSeperated += ","; + } + } + + return "MATCH (" + columnsListCommaSeperated + ") AGAINST (" + this._againstValue + + (this._searchModifier != null ? " " + this._searchModifier : "") + ")"; + } + + public FullTextSearch withMatchColumns(ExpressionList matchColumns) { + this.setMatchColumns(matchColumns); + return this; + } + + public FullTextSearch withAgainstValue(StringValue againstValue) { + this.setAgainstValue(againstValue); + return this; + } + + public FullTextSearch withSearchModifier(String searchModifier) { + this.setSearchModifier(searchModifier); + return this; + } + + public FullTextSearch addMatchColumns(Column... matchColumns) { + return this.addMatchColumns(Arrays.asList(matchColumns)); + } + + public FullTextSearch addMatchColumns(Collection matchColumns) { + ExpressionList collection = + Optional.ofNullable(getMatchColumns()).orElseGet(ExpressionList::new); + collection.addAll(matchColumns); + return this.withMatchColumns(collection); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/GeometryDistance.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/GeometryDistance.java new file mode 100644 index 0000000..9f8438a --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/GeometryDistance.java @@ -0,0 +1,28 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2022 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class GeometryDistance extends ComparisonOperator { + + public GeometryDistance() { + super("<->"); + } + + public GeometryDistance(String operator) { + super(operator); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/GreaterThan.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/GreaterThan.java new file mode 100644 index 0000000..3599ba4 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/GreaterThan.java @@ -0,0 +1,49 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class GreaterThan extends ComparisonOperator { + + public GreaterThan() { + super(">"); + } + + public GreaterThan(Expression leftExpression, Expression rightExpression) { + super(">", leftExpression, rightExpression); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public GreaterThan withLeftExpression(Expression arg0) { + return (GreaterThan) super.withLeftExpression(arg0); + } + + @Override + public GreaterThan withRightExpression(Expression arg0) { + return (GreaterThan) super.withRightExpression(arg0); + } + + @Override + public GreaterThan withOldOracleJoinSyntax(int arg0) { + return (GreaterThan) super.withOldOracleJoinSyntax(arg0); + } + + @Override + public GreaterThan withOraclePriorPosition(int arg0) { + return (GreaterThan) super.withOraclePriorPosition(arg0); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/GreaterThanEquals.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/GreaterThanEquals.java new file mode 100644 index 0000000..39d1c8c --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/GreaterThanEquals.java @@ -0,0 +1,53 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class GreaterThanEquals extends ComparisonOperator { + + public GreaterThanEquals() { + super(">="); + } + + public GreaterThanEquals(String operator) { + super(operator); + } + + public GreaterThanEquals(Expression leftExpression, Expression rightExpression) { + super(">=", leftExpression, rightExpression); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public GreaterThanEquals withLeftExpression(Expression arg0) { + return (GreaterThanEquals) super.withLeftExpression(arg0); + } + + @Override + public GreaterThanEquals withRightExpression(Expression arg0) { + return (GreaterThanEquals) super.withRightExpression(arg0); + } + + @Override + public GreaterThanEquals withOldOracleJoinSyntax(int arg0) { + return (GreaterThanEquals) super.withOldOracleJoinSyntax(arg0); + } + + @Override + public GreaterThanEquals withOraclePriorPosition(int arg0) { + return (GreaterThanEquals) super.withOraclePriorPosition(arg0); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/InExpression.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/InExpression.java new file mode 100644 index 0000000..743d8a0 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/InExpression.java @@ -0,0 +1,157 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +public class InExpression extends ASTNodeAccessImpl + implements Expression, SupportsOldOracleJoinSyntax { + + private Expression leftExpression; + private boolean global = false; + private boolean not = false; + private Expression rightExpression; + private int oldOracleJoinSyntax = NO_ORACLE_JOIN; + + public InExpression() {} + + public InExpression(Expression leftExpression, Expression rightExpression) { + this.leftExpression = leftExpression; + this.rightExpression = rightExpression; + } + + @Override + public int getOldOracleJoinSyntax() { + return oldOracleJoinSyntax; + } + + @Override + public void setOldOracleJoinSyntax(int oldOracleJoinSyntax) { + this.oldOracleJoinSyntax = oldOracleJoinSyntax; + if (oldOracleJoinSyntax < 0 || oldOracleJoinSyntax > 1) { + throw new IllegalArgumentException( + "unexpected join type for oracle found with IN (type=" + oldOracleJoinSyntax + + ")"); + } + } + + public Expression getLeftExpression() { + return leftExpression; + } + + public final void setLeftExpression(Expression expression) { + leftExpression = expression; + } + + public InExpression withLeftExpression(Expression expression) { + this.setLeftExpression(expression); + return this; + } + + public boolean isGlobal() { + return global; + } + + public InExpression setGlobal(boolean b) { + global = b; + return this; + } + + public boolean isNot() { + return not; + } + + public void setNot(boolean b) { + not = b; + } + + public Expression getRightExpression() { + return rightExpression; + } + + public void setRightExpression(Expression rightExpression) { + this.rightExpression = rightExpression; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + private String getLeftExpressionString() { + return leftExpression + (oldOracleJoinSyntax == ORACLE_JOIN_RIGHT ? "(+)" : ""); + } + + @Override + public String toString() { + StringBuilder statementBuilder = new StringBuilder(); + statementBuilder.append(getLeftExpressionString()); + + statementBuilder.append(" "); + if (global) { + statementBuilder.append("GLOBAL "); + } + if (not) { + statementBuilder.append("NOT "); + } + statementBuilder.append("IN "); + statementBuilder.append(rightExpression); + return statementBuilder.toString(); + } + + @Override + public int getOraclePriorPosition() { + return SupportsOldOracleJoinSyntax.NO_ORACLE_PRIOR; + } + + @Override + public void setOraclePriorPosition(int priorPosition) { + if (priorPosition != SupportsOldOracleJoinSyntax.NO_ORACLE_PRIOR) { + throw new IllegalArgumentException("unexpected prior for oracle found"); + } + } + + public InExpression withRightExpression(Expression rightExpression) { + this.setRightExpression(rightExpression); + return this; + } + + @Override + public InExpression withOldOracleJoinSyntax(int oldOracleJoinSyntax) { + this.setOldOracleJoinSyntax(oldOracleJoinSyntax); + return this; + } + + @Override + public InExpression withOraclePriorPosition(int priorPosition) { + this.setOraclePriorPosition(priorPosition); + return this; + } + + public InExpression withGlobal(boolean global) { + this.setGlobal(global); + return this; + } + + public InExpression withNot(boolean not) { + this.setNot(not); + return this; + } + + public E getLeftExpression(Class type) { + return type.cast(getLeftExpression()); + } + + public E getRightExpression(Class type) { + return type.cast(getRightExpression()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/IncludesExpression.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/IncludesExpression.java new file mode 100644 index 0000000..b0b260e --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/IncludesExpression.java @@ -0,0 +1,79 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +public class IncludesExpression extends ASTNodeAccessImpl + implements Expression { + + private Expression leftExpression; + private Expression rightExpression; + + public IncludesExpression() {} + + public IncludesExpression(Expression leftExpression, Expression rightExpression) { + this.leftExpression = leftExpression; + this.rightExpression = rightExpression; + } + + public Expression getLeftExpression() { + return leftExpression; + } + + public final void setLeftExpression(Expression expression) { + leftExpression = expression; + } + + public IncludesExpression withLeftExpression(Expression expression) { + this.setLeftExpression(expression); + return this; + } + + public Expression getRightExpression() { + return rightExpression; + } + + public void setRightExpression(Expression rightExpression) { + this.rightExpression = rightExpression; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String toString() { + StringBuilder statementBuilder = new StringBuilder(); + statementBuilder.append(leftExpression); + + statementBuilder.append(" "); + statementBuilder.append("INCLUDES "); + + statementBuilder.append(rightExpression); + return statementBuilder.toString(); + } + + public IncludesExpression withRightExpression(Expression rightExpression) { + this.setRightExpression(rightExpression); + return this; + } + + public E getLeftExpression(Class type) { + return type.cast(getLeftExpression()); + } + + public E getRightExpression(Class type) { + return type.cast(getRightExpression()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/IsBooleanExpression.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/IsBooleanExpression.java new file mode 100644 index 0000000..3c83365 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/IsBooleanExpression.java @@ -0,0 +1,78 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +public class IsBooleanExpression extends ASTNodeAccessImpl implements Expression { + + private Expression leftExpression; + private boolean not = false; + private boolean isTrue = false; + + public Expression getLeftExpression() { + return leftExpression; + } + + public void setLeftExpression(Expression expression) { + leftExpression = expression; + } + + public boolean isNot() { + return not; + } + + public void setNot(boolean b) { + not = b; + } + + public boolean isTrue() { + return isTrue; + } + + public void setIsTrue(boolean isTrue) { + this.isTrue = isTrue; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String toString() { + if (isTrue()) { + return leftExpression + " IS" + (not ? " NOT" : "") + " TRUE"; + } else { + return leftExpression + " IS" + (not ? " NOT" : "") + " FALSE"; + } + } + + public IsBooleanExpression withIsTrue(boolean isTrue) { + this.setIsTrue(isTrue); + return this; + } + + public IsBooleanExpression withLeftExpression(Expression leftExpression) { + this.setLeftExpression(leftExpression); + return this; + } + + public IsBooleanExpression withNot(boolean not) { + this.setNot(not); + return this; + } + + public E getLeftExpression(Class type) { + return type.cast(getLeftExpression()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/IsDistinctExpression.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/IsDistinctExpression.java new file mode 100644 index 0000000..60add6b --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/IsDistinctExpression.java @@ -0,0 +1,42 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.BinaryExpression; +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class IsDistinctExpression extends BinaryExpression { + + private boolean not = false; + + public boolean isNot() { + return not; + } + + public void setNot(boolean b) { + not = b; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String getStringExpression() { + return " IS " + (isNot() ? "NOT " : "") + "DISTINCT FROM "; + } + + @Override + public String toString() { + String retval = getLeftExpression() + getStringExpression() + getRightExpression(); + return retval; + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/IsNullExpression.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/IsNullExpression.java new file mode 100644 index 0000000..393f9f1 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/IsNullExpression.java @@ -0,0 +1,102 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; +import net.sf.jsqlparser.schema.Column; + +public class IsNullExpression extends ASTNodeAccessImpl implements Expression { + + private Expression leftExpression; + private boolean not = false; + private boolean useIsNull = false; + private boolean useNotNull = false; + + public IsNullExpression() {} + + public IsNullExpression(Expression leftExpression) { + this.leftExpression = leftExpression; + } + + public IsNullExpression(String columnName, boolean not) { + this.leftExpression = new Column(columnName); + this.not = not; + } + + public Expression getLeftExpression() { + return leftExpression; + } + + public void setLeftExpression(Expression expression) { + leftExpression = expression; + } + + public boolean isNot() { + return not; + } + + public void setNot(boolean b) { + not = b; + } + + public boolean isUseIsNull() { + return useIsNull; + } + + public void setUseIsNull(boolean useIsNull) { + this.useIsNull = useIsNull; + } + + public boolean isUseNotNull() { + return useNotNull; + } + + public IsNullExpression setUseNotNull(boolean useNotNull) { + this.useNotNull = useNotNull; + return this; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String toString() { + if (useNotNull) { + return leftExpression + " NOTNULL"; + } else if (useIsNull) { + return leftExpression + (not ? " NOT" : "") + " ISNULL"; + } else { + return leftExpression + " IS " + (not ? "NOT " : "") + "NULL"; + } + } + + public IsNullExpression withUseIsNull(boolean useIsNull) { + this.setUseIsNull(useIsNull); + return this; + } + + public IsNullExpression withLeftExpression(Expression leftExpression) { + this.setLeftExpression(leftExpression); + return this; + } + + public IsNullExpression withNot(boolean not) { + this.setNot(not); + return this; + } + + public E getLeftExpression(Class type) { + return type.cast(getLeftExpression()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/JsonOperator.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/JsonOperator.java new file mode 100644 index 0000000..94237e8 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/JsonOperator.java @@ -0,0 +1,43 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.BinaryExpression; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class JsonOperator extends BinaryExpression { + + private String op; // "@>" + + public JsonOperator(String op) { + this.op = op; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String getStringExpression() { + return op; + } + + @Override + public JsonOperator withLeftExpression(Expression arg0) { + return (JsonOperator) super.withLeftExpression(arg0); + } + + @Override + public JsonOperator withRightExpression(Expression arg0) { + return (JsonOperator) super.withRightExpression(arg0); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/LikeExpression.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/LikeExpression.java new file mode 100644 index 0000000..cf55678 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/LikeExpression.java @@ -0,0 +1,126 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.BinaryExpression; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class LikeExpression extends BinaryExpression { + private boolean not = false; + private boolean useBinary = false; + private Expression escapeExpression = null; + private KeyWord likeKeyWord = KeyWord.LIKE; + + public boolean isNot() { + return not; + } + + public void setNot(boolean b) { + not = b; + } + + public boolean isUseBinary() { + return useBinary; + } + + public LikeExpression setUseBinary(boolean useBinary) { + this.useBinary = useBinary; + return this; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Deprecated + @Override + public String getStringExpression() { + return likeKeyWord.toString(); + } + + @Override + public String toString() { + String retval = getLeftExpression() + " " + (not ? "NOT " : "") + + (likeKeyWord == KeyWord.SIMILAR_TO ? "SIMILAR TO" : likeKeyWord) + " " + + (useBinary ? "BINARY " : "") + getRightExpression(); + if (escapeExpression != null) { + retval += " ESCAPE " + escapeExpression; + } + return retval; + } + + public Expression getEscape() { + return escapeExpression; + } + + public void setEscape(Expression escapeExpression) { + this.escapeExpression = escapeExpression; + } + + @Deprecated + public boolean isCaseInsensitive() { + return likeKeyWord == KeyWord.ILIKE; + } + + @Deprecated + public void setCaseInsensitive(boolean caseInsensitive) { + this.likeKeyWord = KeyWord.ILIKE; + } + + public KeyWord getLikeKeyWord() { + return likeKeyWord; + } + + public LikeExpression setLikeKeyWord(KeyWord likeKeyWord) { + this.likeKeyWord = likeKeyWord; + return this; + } + + public LikeExpression setLikeKeyWord(String likeKeyWord) { + this.likeKeyWord = KeyWord.from(likeKeyWord); + return this; + } + + public LikeExpression withEscape(Expression escape) { + this.setEscape(escape); + return this; + } + + @Deprecated + public LikeExpression withCaseInsensitive(boolean caseInsensitive) { + this.setCaseInsensitive(caseInsensitive); + return this; + } + + public LikeExpression withNot(boolean not) { + this.setNot(not); + return this; + } + + @Override + public LikeExpression withLeftExpression(Expression arg0) { + return (LikeExpression) super.withLeftExpression(arg0); + } + + @Override + public LikeExpression withRightExpression(Expression arg0) { + return (LikeExpression) super.withRightExpression(arg0); + } + + public enum KeyWord { + LIKE, ILIKE, RLIKE, REGEXP, SIMILAR_TO; + + public static KeyWord from(String keyword) { + return Enum.valueOf(KeyWord.class, keyword.toUpperCase().replaceAll("\\s+", "_")); + } + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/Matches.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/Matches.java new file mode 100644 index 0000000..1388fa5 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/Matches.java @@ -0,0 +1,46 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class Matches extends OldOracleJoinBinaryExpression { + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String getStringExpression() { + return "@@"; + } + + @Override + public Matches withLeftExpression(Expression arg0) { + return (Matches) super.withLeftExpression(arg0); + } + + @Override + public Matches withRightExpression(Expression arg0) { + return (Matches) super.withRightExpression(arg0); + } + + @Override + public Matches withOldOracleJoinSyntax(int oldOracleJoinSyntax) { + return (Matches) super.withOldOracleJoinSyntax(oldOracleJoinSyntax); + } + + @Override + public Matches withOraclePriorPosition(int oraclePriorPosition) { + return (Matches) super.withOraclePriorPosition(oraclePriorPosition); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/MemberOfExpression.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/MemberOfExpression.java new file mode 100644 index 0000000..d602e62 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/MemberOfExpression.java @@ -0,0 +1,63 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2023 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +public class MemberOfExpression extends ASTNodeAccessImpl implements Expression { + + Expression leftExpression; + Expression rightExpression; + boolean isNot; + + public MemberOfExpression(Expression leftExpression, Expression rightExpression) { + this.leftExpression = leftExpression; + this.rightExpression = rightExpression; + } + + public Expression getLeftExpression() { + return leftExpression; + } + + public MemberOfExpression setLeftExpression(Expression leftExpression) { + this.leftExpression = leftExpression; + return this; + } + + public Expression getRightExpression() { + return rightExpression; + } + + public MemberOfExpression setRightExpression(Expression rightExpression) { + this.rightExpression = rightExpression; + return this; + } + + public boolean isNot() { + return isNot; + } + + public MemberOfExpression setNot(boolean not) { + isNot = not; + return this; + } + + @Override + public String toString() { + return leftExpression + " MEMBER OF " + rightExpression; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/MinorThan.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/MinorThan.java new file mode 100644 index 0000000..3f6ddd2 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/MinorThan.java @@ -0,0 +1,49 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class MinorThan extends ComparisonOperator { + + public MinorThan() { + super("<"); + } + + public MinorThan(Expression leftExpression, Expression rightExpression) { + super("<", leftExpression, rightExpression); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public MinorThan withLeftExpression(Expression arg0) { + return (MinorThan) super.withLeftExpression(arg0); + } + + @Override + public MinorThan withRightExpression(Expression arg0) { + return (MinorThan) super.withRightExpression(arg0); + } + + @Override + public MinorThan withOldOracleJoinSyntax(int arg0) { + return (MinorThan) super.withOldOracleJoinSyntax(arg0); + } + + @Override + public MinorThan withOraclePriorPosition(int arg0) { + return (MinorThan) super.withOraclePriorPosition(arg0); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/MinorThanEquals.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/MinorThanEquals.java new file mode 100644 index 0000000..5318a0d --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/MinorThanEquals.java @@ -0,0 +1,53 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class MinorThanEquals extends ComparisonOperator { + + public MinorThanEquals() { + super("<="); + } + + public MinorThanEquals(String operator) { + super(operator); + } + + public MinorThanEquals(Expression leftExpression, Expression rightExpression) { + super("<=", leftExpression, rightExpression); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public MinorThanEquals withLeftExpression(Expression arg0) { + return (MinorThanEquals) super.withLeftExpression(arg0); + } + + @Override + public MinorThanEquals withRightExpression(Expression arg0) { + return (MinorThanEquals) super.withRightExpression(arg0); + } + + @Override + public MinorThanEquals withOldOracleJoinSyntax(int arg0) { + return (MinorThanEquals) super.withOldOracleJoinSyntax(arg0); + } + + @Override + public MinorThanEquals withOraclePriorPosition(int arg0) { + return (MinorThanEquals) super.withOraclePriorPosition(arg0); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/NamedExpressionList.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/NamedExpressionList.java new file mode 100644 index 0000000..e7473cf --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/NamedExpressionList.java @@ -0,0 +1,70 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.Expression; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +/** + * A list of named expressions, as in as in select substr('xyzzy' from 2 for 3) + */ +public class NamedExpressionList extends ExpressionList { + private List names; + + public List getNames() { + return names; + } + + public void setNames(List list) { + names = list; + } + + @Override + public String toString() { + + StringBuilder ret = new StringBuilder(); + ret.append("("); + for (int i = 0; i < size(); i++) { + if (i > 0) { + ret.append(" "); + } + if (!names.get(i).equals("")) { + ret.append(names.get(i)).append(" ").append(get(i)); + } else { + ret.append(get(i)); + } + } + ret.append(")"); + + return ret.toString(); + } + + public NamedExpressionList withNames(List names) { + this.setNames(names); + return this; + } + + public NamedExpressionList addNames(String... names) { + List collection = Optional.ofNullable(getNames()).orElseGet(ArrayList::new); + Collections.addAll(collection, names); + return this.withNames(collection); + } + + public NamedExpressionList addNames(Collection names) { + List collection = Optional.ofNullable(getNames()).orElseGet(ArrayList::new); + collection.addAll(names); + return this.withNames(collection); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/NotEqualsTo.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/NotEqualsTo.java new file mode 100644 index 0000000..c6e1ef3 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/NotEqualsTo.java @@ -0,0 +1,55 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class NotEqualsTo extends ComparisonOperator { + + public NotEqualsTo() { + super("<>"); + } + + public NotEqualsTo(String operator) { + super(operator); + } + + public NotEqualsTo(Expression left, Expression right) { + this(); + setLeftExpression(left); + setRightExpression(right); + } + + @Override + public NotEqualsTo withLeftExpression(Expression expression) { + return (NotEqualsTo) super.withLeftExpression(expression); + } + + @Override + public NotEqualsTo withRightExpression(Expression expression) { + return (NotEqualsTo) super.withRightExpression(expression); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public NotEqualsTo withOldOracleJoinSyntax(int arg0) { + return (NotEqualsTo) super.withOldOracleJoinSyntax(arg0); + } + + @Override + public NotEqualsTo withOraclePriorPosition(int arg0) { + return (NotEqualsTo) super.withOraclePriorPosition(arg0); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/OldOracleJoinBinaryExpression.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/OldOracleJoinBinaryExpression.java new file mode 100644 index 0000000..46b093b --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/OldOracleJoinBinaryExpression.java @@ -0,0 +1,77 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.BinaryExpression; +import net.sf.jsqlparser.expression.Expression; + +public abstract class OldOracleJoinBinaryExpression extends BinaryExpression + implements SupportsOldOracleJoinSyntax { + + private int oldOracleJoinSyntax = NO_ORACLE_JOIN; + + private int oraclePriorPosition = NO_ORACLE_PRIOR; + + @Override + public String toString() { + return // (isNot() ? "NOT " : "") + (oraclePriorPosition == ORACLE_PRIOR_START ? "PRIOR " : "") + + getLeftExpression() + + (oldOracleJoinSyntax == ORACLE_JOIN_RIGHT ? "(+)" : "") + " " + + getStringExpression() + " " + + (oraclePriorPosition == ORACLE_PRIOR_END ? "PRIOR " : "") + + getRightExpression() + + (oldOracleJoinSyntax == ORACLE_JOIN_LEFT ? "(+)" : ""); + } + + @Override + public int getOldOracleJoinSyntax() { + return oldOracleJoinSyntax; + } + + @Override + public void setOldOracleJoinSyntax(int oldOracleJoinSyntax) { + this.oldOracleJoinSyntax = oldOracleJoinSyntax; + if (oldOracleJoinSyntax < 0 || oldOracleJoinSyntax > 2) { + throw new IllegalArgumentException( + "unknown join type for oracle found (type=" + oldOracleJoinSyntax + ")"); + } + } + + @Override + public int getOraclePriorPosition() { + return oraclePriorPosition; + } + + @Override + public void setOraclePriorPosition(int oraclePriorPosition) { + this.oraclePriorPosition = oraclePriorPosition; + } + + public OldOracleJoinBinaryExpression withOldOracleJoinSyntax(int oldOracleJoinSyntax) { + this.setOldOracleJoinSyntax(oldOracleJoinSyntax); + return this; + } + + public OldOracleJoinBinaryExpression withOraclePriorPosition(int oraclePriorPosition) { + this.setOraclePriorPosition(oraclePriorPosition); + return this; + } + + @Override + public OldOracleJoinBinaryExpression withLeftExpression(Expression arg0) { + return (OldOracleJoinBinaryExpression) super.withLeftExpression(arg0); + } + + @Override + public OldOracleJoinBinaryExpression withRightExpression(Expression arg0) { + return (OldOracleJoinBinaryExpression) super.withRightExpression(arg0); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/ParenthesedExpressionList.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ParenthesedExpressionList.java new file mode 100644 index 0000000..dc71ea6 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ParenthesedExpressionList.java @@ -0,0 +1,42 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2023 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.statement.select.PlainSelect; + +import java.util.Arrays; +import java.util.Collection; + +public class ParenthesedExpressionList extends ExpressionList { + public ParenthesedExpressionList() {} + + public ParenthesedExpressionList(ExpressionList expressions) { + addAll(expressions); + } + + @SafeVarargs + public ParenthesedExpressionList(T... expressions) { + addAll(Arrays.asList(expressions)); + } + + public ParenthesedExpressionList(Collection expressions) { + addAll(expressions); + } + + public static ParenthesedExpressionList from(ExpressionList expressions) { + return new ParenthesedExpressionList<>(expressions); + } + + @Override + public String toString() { + return PlainSelect.getStringList(this, true, true); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/RegExpMatchOperator.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/RegExpMatchOperator.java new file mode 100644 index 0000000..786f604 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/RegExpMatchOperator.java @@ -0,0 +1,62 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import java.util.Objects; + +import net.sf.jsqlparser.expression.BinaryExpression; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class RegExpMatchOperator extends BinaryExpression { + + private RegExpMatchOperatorType operatorType; + + public RegExpMatchOperator(RegExpMatchOperatorType operatorType) { + this.operatorType = Objects.requireNonNull(operatorType, + "The provided RegExpMatchOperatorType must not be NULL."); + } + + public RegExpMatchOperatorType getOperatorType() { + return operatorType; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String getStringExpression() { + switch (operatorType) { + case MATCH_CASESENSITIVE: + return "~"; + case MATCH_CASEINSENSITIVE: + return "~*"; + case NOT_MATCH_CASESENSITIVE: + return "!~"; + case NOT_MATCH_CASEINSENSITIVE: + return "!~*"; + default: + break; + } + return null; + } + + @Override + public RegExpMatchOperator withLeftExpression(Expression arg0) { + return (RegExpMatchOperator) super.withLeftExpression(arg0); + } + + @Override + public RegExpMatchOperator withRightExpression(Expression arg0) { + return (RegExpMatchOperator) super.withRightExpression(arg0); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/RegExpMatchOperatorType.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/RegExpMatchOperatorType.java new file mode 100644 index 0000000..c22f05e --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/RegExpMatchOperatorType.java @@ -0,0 +1,17 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +/** + * PostgresSQL match operators. + */ +public enum RegExpMatchOperatorType { + MATCH_CASESENSITIVE, MATCH_CASEINSENSITIVE, NOT_MATCH_CASESENSITIVE, NOT_MATCH_CASEINSENSITIVE +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/SimilarToExpression.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/SimilarToExpression.java new file mode 100644 index 0000000..0818d75 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/SimilarToExpression.java @@ -0,0 +1,77 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.BinaryExpression; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class SimilarToExpression extends BinaryExpression { + + private boolean not = false; + private String escape = null; + + public boolean isNot() { + return not; + } + + public void setNot(boolean b) { + not = b; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String getStringExpression() { + return "SIMILAR TO"; + } + + @Override + public String toString() { + String retval = getLeftExpression() + " " + (not ? "NOT " : "") + getStringExpression() + + " " + getRightExpression(); + if (escape != null) { + retval += " ESCAPE " + "'" + escape + "'"; + } + + return retval; + } + + public String getEscape() { + return escape; + } + + public void setEscape(String escape) { + this.escape = escape; + } + + public SimilarToExpression withEscape(String escape) { + this.setEscape(escape); + return this; + } + + public SimilarToExpression withNot(boolean not) { + this.setNot(not); + return this; + } + + @Override + public SimilarToExpression withLeftExpression(Expression arg0) { + return (SimilarToExpression) super.withLeftExpression(arg0); + } + + @Override + public SimilarToExpression withRightExpression(Expression arg0) { + return (SimilarToExpression) super.withRightExpression(arg0); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/SupportsOldOracleJoinSyntax.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/SupportsOldOracleJoinSyntax.java new file mode 100644 index 0000000..9b8777d --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/SupportsOldOracleJoinSyntax.java @@ -0,0 +1,32 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +public interface SupportsOldOracleJoinSyntax { + + int NO_ORACLE_JOIN = 0; + int ORACLE_JOIN_RIGHT = 1; + int ORACLE_JOIN_LEFT = 2; + int NO_ORACLE_PRIOR = 0; + int ORACLE_PRIOR_START = 1; + int ORACLE_PRIOR_END = 2; + + int getOldOracleJoinSyntax(); + + void setOldOracleJoinSyntax(int oldOracleJoinSyntax); + + SupportsOldOracleJoinSyntax withOldOracleJoinSyntax(int oldOracleJoinSyntax); + + int getOraclePriorPosition(); + + void setOraclePriorPosition(int priorPosition); + + SupportsOldOracleJoinSyntax withOraclePriorPosition(int priorPosition); +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/TSQLLeftJoin.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/TSQLLeftJoin.java new file mode 100644 index 0000000..aa1b90e --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/TSQLLeftJoin.java @@ -0,0 +1,24 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class TSQLLeftJoin extends ComparisonOperator { + + public TSQLLeftJoin() { + super("*="); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/TSQLRightJoin.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/TSQLRightJoin.java new file mode 100644 index 0000000..fcb3e9d --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/TSQLRightJoin.java @@ -0,0 +1,24 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class TSQLRightJoin extends ComparisonOperator { + + public TSQLRightJoin() { + super("=*"); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } +} diff --git a/src/main/java/net/sf/jsqlparser/parser/ASTNodeAccess.java b/src/main/java/net/sf/jsqlparser/parser/ASTNodeAccess.java new file mode 100644 index 0000000..6b4993e --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/parser/ASTNodeAccess.java @@ -0,0 +1,19 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.parser; + +import java.io.Serializable; + +public interface ASTNodeAccess extends Serializable { + + SimpleNode getASTNode(); + + void setASTNode(SimpleNode node); +} diff --git a/src/main/java/net/sf/jsqlparser/parser/ASTNodeAccessImpl.java b/src/main/java/net/sf/jsqlparser/parser/ASTNodeAccessImpl.java new file mode 100644 index 0000000..8ecbca8 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/parser/ASTNodeAccessImpl.java @@ -0,0 +1,68 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.parser; + +import java.util.Set; +import java.util.TreeSet; + +public class ASTNodeAccessImpl implements ASTNodeAccess { + + private transient SimpleNode node; + + @Override + public SimpleNode getASTNode() { + return node; + } + + @Override + public void setASTNode(SimpleNode node) { + this.node = node; + } + + public StringBuilder appendTo(StringBuilder builder) { + // don't add spaces around the following punctuation + final Set punctuation = new TreeSet<>(Set.of(".", "[", "]")); + + SimpleNode simpleNode = getASTNode(); + if (simpleNode != null) { + Token token = simpleNode.jjtGetFirstToken(); + Token lastToken = simpleNode.jjtGetLastToken(); + Token prevToken = null; + while (token.next != null && token.absoluteEnd <= lastToken.absoluteEnd) { + if (!punctuation.contains(token.image) + && (prevToken == null || !punctuation.contains(prevToken.image))) { + builder.append(" "); + } + builder.append(token.image); + prevToken = token; + token = token.next; + } + } + return builder; + } + + public ASTNodeAccess getParent() { + SimpleNode parent = (SimpleNode) node.jjtGetParent(); + while (parent.jjtGetValue() == null) { + parent = (SimpleNode) parent.jjtGetParent(); + } + + return ASTNodeAccess.class.cast(parent.jjtGetValue()); + } + + public T getParent(Class clazz) { + SimpleNode parent = (SimpleNode) node.jjtGetParent(); + while (parent.jjtGetValue() == null || !clazz.isInstance(parent.jjtGetValue())) { + parent = (SimpleNode) parent.jjtGetParent(); + } + + return clazz.cast(parent.jjtGetValue()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/parser/AbstractJSqlParser.java b/src/main/java/net/sf/jsqlparser/parser/AbstractJSqlParser.java new file mode 100644 index 0000000..3c414d8 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/parser/AbstractJSqlParser.java @@ -0,0 +1,107 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.parser; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.parser.feature.FeatureConfiguration; + +import java.util.ArrayList; +import java.util.List; + +public abstract class AbstractJSqlParser

{ + + protected int jdbcParameterIndex = 0; + protected boolean errorRecovery = false; + protected List parseErrors = new ArrayList<>(); + + public P withSquareBracketQuotation() { + return withFeature(Feature.allowSquareBracketQuotation, true); + } + + public P withSquareBracketQuotation(boolean allowSquareBracketQuotation) { + return withFeature(Feature.allowSquareBracketQuotation, allowSquareBracketQuotation); + } + + public P withAllowComplexParsing() { + return withFeature(Feature.allowComplexParsing, true); + } + + public P withAllowComplexParsing(boolean allowComplexParsing) { + return withFeature(Feature.allowComplexParsing, allowComplexParsing); + } + + public P withComplexParsing() { + return withFeature(Feature.allowComplexParsing, true); + } + + public P withComplexParsing(boolean allowComplexParsing) { + return withFeature(Feature.allowComplexParsing, allowComplexParsing); + } + + public P withUnsupportedStatements() { + return withFeature(Feature.allowUnsupportedStatements, true); + } + + public P withUnsupportedStatements(boolean allowUnsupportedStatements) { + return withFeature(Feature.allowUnsupportedStatements, allowUnsupportedStatements); + } + + public P withTimeOut(long timeOutMillSeconds) { + return withFeature(Feature.timeOut, timeOutMillSeconds); + } + + public P withBackslashEscapeCharacter() { + return withFeature(Feature.allowBackslashEscapeCharacter, true); + } + + public P withBackslashEscapeCharacter(boolean allowBackslashEscapeCharacter) { + return withFeature(Feature.allowBackslashEscapeCharacter, allowBackslashEscapeCharacter); + } + + public P withFeature(Feature f, boolean enabled) { + getConfiguration().setValue(f, enabled); + return me(); + } + + public P withFeature(Feature f, long value) { + getConfiguration().setValue(f, value); + return me(); + } + + public abstract FeatureConfiguration getConfiguration(); + + public abstract P me(); + + public boolean getAsBoolean(Feature f) { + return getConfiguration().getAsBoolean(f); + } + + public Long getAsLong(Feature f) { + return getConfiguration().getAsLong(f); + } + + public void setErrorRecovery(boolean errorRecovery) { + this.errorRecovery = errorRecovery; + } + + public P withErrorRecovery() { + this.errorRecovery = true; + return me(); + } + + public P withErrorRecovery(boolean errorRecovery) { + this.errorRecovery = errorRecovery; + return me(); + } + + public List getParseErrors() { + return parseErrors; + } +} diff --git a/src/main/java/net/sf/jsqlparser/parser/BaseToken.java b/src/main/java/net/sf/jsqlparser/parser/BaseToken.java new file mode 100644 index 0000000..6972f0a --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/parser/BaseToken.java @@ -0,0 +1,16 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.parser; + +public class BaseToken { + + public int absoluteBegin = 0; + public int absoluteEnd = 0; +} diff --git a/src/main/java/net/sf/jsqlparser/parser/CCJSqlParserManager.java b/src/main/java/net/sf/jsqlparser/parser/CCJSqlParserManager.java new file mode 100644 index 0000000..fb0bf21 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/parser/CCJSqlParserManager.java @@ -0,0 +1,28 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.parser; + +import java.io.Reader; + +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.statement.Statement; + +public class CCJSqlParserManager implements JSqlParser { + + @Override + public Statement parse(Reader statementReader) throws JSQLParserException { + CCJSqlParser parser = new CCJSqlParser(new StreamProvider(statementReader)); + try { + return parser.Statement(); + } catch (Exception ex) { + throw new JSQLParserException(ex); + } + } +} diff --git a/src/main/java/net/sf/jsqlparser/parser/CCJSqlParserUtil.java b/src/main/java/net/sf/jsqlparser/parser/CCJSqlParserUtil.java new file mode 100644 index 0000000..19e31a4 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/parser/CCJSqlParserUtil.java @@ -0,0 +1,546 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.parser; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.util.Stack; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Consumer; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.Statements; +import org.w3c.dom.Node; + +/** + * Toolfunctions to start and use JSqlParser. + * + * @author toben + */ + +@SuppressWarnings("PMD.CyclomaticComplexity") +public final class CCJSqlParserUtil { + public final static Logger LOGGER = Logger.getLogger(CCJSqlParserUtil.class.getName()); + public final static int ALLOWED_NESTING_DEPTH = 10; + + static { + LOGGER.setLevel(Level.OFF); + } + + private CCJSqlParserUtil() {} + + public static Statement parse(Reader statementReader) throws JSQLParserException { + ExecutorService executorService = Executors.newSingleThreadExecutor(); + Statement statement = null; + CCJSqlParser parser = new CCJSqlParser(new StreamProvider(statementReader)); + try { + statement = parseStatement(parser, executorService); + } finally { + executorService.shutdown(); + } + return statement; + } + + public static Statement parse(String sql) throws JSQLParserException { + return parse(sql, null); + } + + /** + * Parses an sql statement while allowing via consumer to configure the used parser before. + *

+ * For instance to activate SQLServer bracket quotation on could use: + *

+ * {@code + * CCJSqlParserUtil.parse("select * from [mytable]", parser -> parser.withSquareBracketQuotation(true)); + * } + * + * @param sql + * @param consumer + * @return + * @throws JSQLParserException + */ + public static Statement parse(String sql, Consumer consumer) + throws JSQLParserException { + + if (sql == null || sql.isEmpty()) { + return null; + } + + ExecutorService executorService = Executors.newSingleThreadExecutor(); + Statement statement = null; + try { + statement = parse(sql, executorService, consumer); + } finally { + executorService.shutdown(); + } + return statement; + } + + public static Statement parse(String sql, ExecutorService executorService, + Consumer consumer) + throws JSQLParserException { + if (sql == null || sql.isEmpty()) { + return null; + } + + Statement statement = null; + // first, try to parse fast and simple + CCJSqlParser parser = newParser(sql); + if (consumer != null) { + consumer.accept(parser); + } + boolean allowComplex = parser.getConfiguration().getAsBoolean(Feature.allowComplexParsing); + LOGGER.info("Allowed Complex Parsing: " + allowComplex); + try { + LOGGER.info("Trying SIMPLE parsing " + (allowComplex ? "first" : "only")); + statement = parseStatement(parser.withAllowComplexParsing(false), executorService); + } catch (JSQLParserException ex) { + LOGGER.info("Nesting Depth" + getNestingDepth(sql)); + if (allowComplex && getNestingDepth(sql) <= ALLOWED_NESTING_DEPTH) { + LOGGER.info("Trying COMPLEX parsing when SIMPLE parsing failed"); + // beware: the parser must not be reused, but needs to be re-initiated + parser = newParser(sql); + if (consumer != null) { + consumer.accept(parser); + } + statement = parseStatement(parser.withAllowComplexParsing(true), executorService); + } else { + throw ex; + } + } + return statement; + } + + public static CCJSqlParser newParser(String sql) { + if (sql == null || sql.isEmpty()) { + return null; + } + + return new CCJSqlParser(new StringProvider(sql)); + } + + public static CCJSqlParser newParser(InputStream is) throws IOException { + return new CCJSqlParser(new StreamProvider(is)); + } + + public static CCJSqlParser newParser(InputStream is, String encoding) throws IOException { + return new CCJSqlParser(new StreamProvider(is, encoding)); + } + + public static Node parseAST(String sql) throws JSQLParserException { + if (sql == null || sql.isEmpty()) { + return null; + } + + CCJSqlParser parser = newParser(sql); + try { + parser.Statement(); + return (Node) parser.jjtree.rootNode(); + } catch (Exception ex) { + throw new JSQLParserException(ex); + } + } + + public static Statement parse(InputStream is) throws JSQLParserException { + try { + CCJSqlParser parser = newParser(is); + return parser.Statement(); + } catch (Exception ex) { + throw new JSQLParserException(ex); + } + } + + public static Statement parse(InputStream is, String encoding) throws JSQLParserException { + try { + CCJSqlParser parser = newParser(is, encoding); + return parser.Statement(); + } catch (Exception ex) { + throw new JSQLParserException(ex); + } + } + + public static Expression parseExpression(String expression) throws JSQLParserException { + if (expression == null || expression.isEmpty()) { + return null; + } + + return parseExpression(expression, true); + } + + public static Expression parseExpression(String expression, boolean allowPartialParse) + throws JSQLParserException { + if (expression == null || expression.isEmpty()) { + return null; + } + + return parseExpression(expression, allowPartialParse, p -> { + }); + } + + @SuppressWarnings("PMD.CyclomaticComplexity") + public static Expression parseExpression(String expressionStr, boolean allowPartialParse, + Consumer consumer) throws JSQLParserException { + if (expressionStr == null || expressionStr.isEmpty()) { + return null; + } + + Expression expression = null; + // first, try to parse fast and simple + try { + CCJSqlParser parser = newParser(expressionStr).withAllowComplexParsing(false); + if (consumer != null) { + consumer.accept(parser); + } + try { + expression = parser.Expression(); + if (parser.getNextToken().kind != CCJSqlParserTokenManager.EOF) { + throw new JSQLParserException( + "could only parse partial expression " + expression.toString()); + } + } catch (ParseException ex) { + throw new JSQLParserException(ex); + } + } catch (JSQLParserException ex1) { + // when fast simple parsing fails, try complex parsing but only if it has a chance to + // succeed + if (getNestingDepth(expressionStr) <= ALLOWED_NESTING_DEPTH) { + CCJSqlParser parser = newParser(expressionStr).withAllowComplexParsing(true); + if (consumer != null) { + consumer.accept(parser); + } + try { + expression = parser.Expression(); + if (!allowPartialParse + && parser.getNextToken().kind != CCJSqlParserTokenManager.EOF) { + throw new JSQLParserException( + "could only parse partial expression " + expression.toString()); + } + } catch (JSQLParserException ex) { + throw ex; + } catch (ParseException ex) { + throw new JSQLParserException(ex); + } + } + } + return expression; + } + + /** + * Parse an conditional expression. This is the expression after a where clause. Partial parsing + * is enabled. + * + * @param condExpr + * @return the expression parsed + * @see #parseCondExpression(String, boolean) + */ + public static Expression parseCondExpression(String condExpr) throws JSQLParserException { + if (condExpr == null || condExpr.isEmpty()) { + return null; + } + return parseCondExpression(condExpr, true); + } + + /** + * Parse an conditional expression. This is the expression after a where clause. + * + * @param condExpr + * @param allowPartialParse false: needs the whole string to be processed. + * @return the expression parsed + * @see #parseCondExpression(String) + */ + public static Expression parseCondExpression(String condExpr, boolean allowPartialParse) + throws JSQLParserException { + if (condExpr == null || condExpr.isEmpty()) { + return null; + } + return parseCondExpression(condExpr, allowPartialParse, p -> { + }); + } + + @SuppressWarnings("PMD.CyclomaticComplexity") + public static Expression parseCondExpression(String conditionalExpressionStr, + boolean allowPartialParse, Consumer consumer) throws JSQLParserException { + if (conditionalExpressionStr == null || conditionalExpressionStr.isEmpty()) { + return null; + } + + Expression expression = null; + // first, try to parse fast and simple + try { + CCJSqlParser parser = + newParser(conditionalExpressionStr).withAllowComplexParsing(false); + if (consumer != null) { + consumer.accept(parser); + } + try { + expression = parser.Expression(); + if (parser.getNextToken().kind != CCJSqlParserTokenManager.EOF) { + throw new JSQLParserException( + "could only parse partial expression " + expression.toString()); + } + } catch (ParseException ex) { + throw new JSQLParserException(ex); + } + } catch (JSQLParserException ex1) { + if (getNestingDepth(conditionalExpressionStr) <= ALLOWED_NESTING_DEPTH) { + CCJSqlParser parser = + newParser(conditionalExpressionStr).withAllowComplexParsing(true); + if (consumer != null) { + consumer.accept(parser); + } + try { + expression = parser.Expression(); + if (!allowPartialParse + && parser.getNextToken().kind != CCJSqlParserTokenManager.EOF) { + throw new JSQLParserException( + "could only parse partial expression " + expression.toString()); + } + } catch (JSQLParserException ex) { + throw ex; + } catch (ParseException ex) { + throw new JSQLParserException(ex); + } + } + } + return expression; + } + + /** + * @param parser the Parser armed with a Statement text + * @param executorService the Executor Service for parsing within a Thread + * @return the parsed Statement + * @throws JSQLParserException when either the Statement can't be parsed or the configured + * timeout is reached + */ + + public static Statement parseStatement(CCJSqlParser parser, ExecutorService executorService) + throws JSQLParserException { + Statement statement = null; + Future future = executorService.submit(new Callable() { + @Override + public Statement call() throws ParseException { + return parser.Statement(); + } + }); + try { + statement = future.get(parser.getConfiguration().getAsLong(Feature.timeOut), + TimeUnit.MILLISECONDS); + } catch (TimeoutException ex) { + parser.interrupted = true; + future.cancel(true); + throw new JSQLParserException("Time out occurred.", ex); + } catch (Exception ex) { + throw new JSQLParserException(ex); + } + return statement; + } + + /** + * Parse a statement list. + * + * @return the statements parsed + */ + public static Statements parseStatements(String sqls) throws JSQLParserException { + if (sqls == null || sqls.isEmpty()) { + return null; + } + + return parseStatements(sqls, null); + } + + public static Statements parseStatements(String sqls, Consumer consumer) + throws JSQLParserException { + if (sqls == null || sqls.isEmpty()) { + return null; + } + + ExecutorService executorService = Executors.newSingleThreadExecutor(); + final Statements statements = parseStatements(sqls, executorService, consumer); + executorService.shutdown(); + + return statements; + } + + /** + * Parse a statement list. + * + * @return the statements parsed + */ + public static Statements parseStatements(String sqls, ExecutorService executorService, + Consumer consumer) + throws JSQLParserException { + if (sqls == null || sqls.isEmpty()) { + return null; + } + + Statements statements = null; + CCJSqlParser parser = newParser(sqls); + if (consumer != null) { + consumer.accept(parser); + } + boolean allowComplex = parser.getConfiguration().getAsBoolean(Feature.allowComplexParsing); + + // first, try to parse fast and simple + try { + statements = parseStatements(parser.withAllowComplexParsing(false), executorService); + } catch (JSQLParserException ex) { + // when fast simple parsing fails, try complex parsing but only if it has a chance to + // succeed + if (allowComplex && getNestingDepth(sqls) <= ALLOWED_NESTING_DEPTH) { + // beware: parser must not be re-used but needs to be re-initiated + parser = newParser(sqls); + if (consumer != null) { + consumer.accept(parser); + } + statements = parseStatements(parser.withAllowComplexParsing(true), executorService); + } + } + return statements; + } + + /** + * @param parser the Parser armed with a Statement text + * @param executorService the Executor Service for parsing within a Thread + * @return the Statements (representing a List of single statements) + * @throws JSQLParserException when either the Statement can't be parsed or the configured + * timeout is reached + */ + public static Statements parseStatements(CCJSqlParser parser, ExecutorService executorService) + throws JSQLParserException { + Statements statements = null; + Future future = executorService.submit(new Callable() { + @Override + public Statements call() throws ParseException { + return parser.Statements(); + } + }); + try { + statements = future.get(parser.getConfiguration().getAsLong(Feature.timeOut), + TimeUnit.MILLISECONDS); + } catch (TimeoutException ex) { + parser.interrupted = true; + future.cancel(true); + throw new JSQLParserException("Time out occurred.", ex); + } catch (Exception ex) { + throw new JSQLParserException(ex); + } + return statements; + } + + public static void streamStatements(StatementListener listener, InputStream is, String encoding) + throws JSQLParserException { + try { + CCJSqlParser parser = newParser(is, encoding); + while (true) { + Statement stmt = parser.SingleStatement(); + listener.accept(stmt); + if (parser.getToken(1).kind == CCJSqlParserTokenManager.ST_SEMICOLON) { + parser.getNextToken(); + } + + if (parser.getToken(1).kind == CCJSqlParserTokenManager.EOF) { + break; + } + } + } catch (Exception ex) { + throw new JSQLParserException(ex); + } + } + + public static int getNestingDepth(String sql) { + int maxlevel = 0; + int level = 0; + + char[] chars = sql.toCharArray(); + for (char c : chars) { + switch (c) { + case '(': + level++; + break; + case ')': + if (maxlevel < level) { + maxlevel = level; + } + level--; + break; + default: + // Codazy/PMD insists in a Default statement + } + } + return maxlevel; + } + + public static int getUnbalancedPosition(String text) { + Stack stack = new Stack<>(); + boolean insideQuote = false; + + for (int i = 0; i < text.length(); i++) { + char c = text.charAt(i); + if (c == '"' || c == '\'') { + if (!insideQuote) { + stack.push(c); // Add quote to stack + } else if (stack.peek() == c) { + stack.pop(); // Matching quote found, remove from stack + } + insideQuote = !insideQuote; // Toggle insideQuote flag + } else if (!insideQuote && (c == '(' || c == '[' || c == '{')) { + stack.push(c); // Add opening bracket to stack + } else if (!insideQuote && (c == ')' || c == ']' || c == '}')) { + if (stack.isEmpty()) { + return i; // Return position of unbalanced closing bracket + } + char top = stack.pop(); + if (c == ')' && top != '(' || c == ']' && top != '[' || c == '}' && top != '{') { + return i; // Return position of unbalanced closing bracket + } + } + } + + if (!stack.isEmpty()) { + char unbalanced = stack.peek(); + for (int i = 0; i < text.length(); i++) { + if (text.charAt(i) == unbalanced) { + return i; // Return position of unbalanced opening bracket or quote + } + } + } + + return -1; // Return -1 if all brackets and quotes are balanced + } + + public static String sanitizeSingleSql(String sqlStr) { + final Pattern SQL_DELIMITER_SPLIT = + Pattern.compile("((?:'[^']*+'|[^\\n])*+)"); + final StringBuilder builder = new StringBuilder(); + final Matcher matcher = SQL_DELIMITER_SPLIT.matcher(sqlStr); + while (matcher.find()) { + for (int i = 1; i <= matcher.groupCount(); i++) { + if (!matcher.group(i).isEmpty()) { + builder.append("\n").append(matcher.group(i)); + } + } + } + return builder.toString(); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/parser/JSqlParser.java b/src/main/java/net/sf/jsqlparser/parser/JSqlParser.java new file mode 100644 index 0000000..8a2aa95 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/parser/JSqlParser.java @@ -0,0 +1,20 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.parser; + +import java.io.Reader; + +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.statement.Statement; + +public interface JSqlParser { + + Statement parse(Reader statementReader) throws JSQLParserException; +} diff --git a/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java b/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java new file mode 100644 index 0000000..28acdd7 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java @@ -0,0 +1,360 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2021 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.parser; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ParserKeywordsUtils { + public final static CharsetEncoder CHARSET_ENCODER = StandardCharsets.US_ASCII.newEncoder(); + + public final static int RESTRICTED_FUNCTION = 1; + public final static int RESTRICTED_SCHEMA = 2; + public final static int RESTRICTED_TABLE = 4; + public final static int RESTRICTED_COLUMN = 8; + public final static int RESTRICTED_EXPRESSION = 16; + public final static int RESTRICTED_ALIAS = 32; + public final static int RESTRICTED_SQL2016 = 64; + + public final static int RESTRICTED_JSQLPARSER = 128 + | RESTRICTED_FUNCTION + | RESTRICTED_SCHEMA + | RESTRICTED_TABLE + | RESTRICTED_COLUMN + | RESTRICTED_EXPRESSION + | RESTRICTED_ALIAS + | RESTRICTED_SQL2016; + + + // Classification follows http://www.h2database.com/html/advanced.html#keywords + public final static Object[][] ALL_RESERVED_KEYWORDS = { + {"ABSENT", RESTRICTED_JSQLPARSER}, + {"ALL", RESTRICTED_SQL2016}, + {"AND", RESTRICTED_SQL2016}, + {"ANY", RESTRICTED_JSQLPARSER}, + {"AS", RESTRICTED_SQL2016}, + {"BETWEEN", RESTRICTED_SQL2016}, + {"BOTH", RESTRICTED_SQL2016}, + {"CASEWHEN", RESTRICTED_ALIAS}, + {"CHECK", RESTRICTED_SQL2016}, + {"CONNECT", RESTRICTED_ALIAS}, + {"CONNECT_BY_ROOT", RESTRICTED_JSQLPARSER}, + {"CONSTRAINT", RESTRICTED_SQL2016}, + {"CREATE", RESTRICTED_ALIAS}, + {"CROSS", RESTRICTED_SQL2016}, + {"CURRENT", RESTRICTED_JSQLPARSER}, + {"DISTINCT", RESTRICTED_SQL2016}, + {"DOUBLE", RESTRICTED_ALIAS}, + {"ELSE", RESTRICTED_JSQLPARSER}, + {"EXCEPT", RESTRICTED_SQL2016}, + {"EXCLUDES", RESTRICTED_JSQLPARSER}, + {"EXISTS", RESTRICTED_SQL2016}, + {"FETCH", RESTRICTED_SQL2016}, + {"FINAL", RESTRICTED_JSQLPARSER}, + {"FOR", RESTRICTED_SQL2016}, + {"FORCE", RESTRICTED_SQL2016}, + {"FOREIGN", RESTRICTED_SQL2016}, + {"FROM", RESTRICTED_SQL2016}, + {"FULL", RESTRICTED_SQL2016}, + {"GLOBAL", RESTRICTED_ALIAS}, + {"GROUP", RESTRICTED_SQL2016}, + {"GROUPING", RESTRICTED_ALIAS}, + {"QUALIFY", RESTRICTED_ALIAS}, + {"HAVING", RESTRICTED_SQL2016}, + {"IF", RESTRICTED_SQL2016}, + {"IIF", RESTRICTED_ALIAS}, + {"IGNORE", RESTRICTED_ALIAS}, + {"ILIKE", RESTRICTED_SQL2016}, + {"IN", RESTRICTED_SQL2016}, + {"INCLUDES", RESTRICTED_JSQLPARSER}, + {"INNER", RESTRICTED_SQL2016}, + {"INTERSECT", RESTRICTED_SQL2016}, + {"INTERVAL", RESTRICTED_SQL2016}, + {"INTO", RESTRICTED_JSQLPARSER}, + {"IS", RESTRICTED_SQL2016}, + {"JOIN", RESTRICTED_JSQLPARSER}, + {"LATERAL", RESTRICTED_SQL2016}, + {"LEFT", RESTRICTED_SQL2016}, + {"LIKE", RESTRICTED_SQL2016}, + {"LIMIT", RESTRICTED_SQL2016}, + {"MINUS", RESTRICTED_SQL2016}, + {"NATURAL", RESTRICTED_SQL2016}, + {"NOCYCLE", RESTRICTED_JSQLPARSER}, + {"NOT", RESTRICTED_SQL2016}, + {"NULL", RESTRICTED_SQL2016}, + {"OFFSET", RESTRICTED_SQL2016}, + {"ON", RESTRICTED_SQL2016}, + {"ONLY", RESTRICTED_JSQLPARSER}, + {"OPTIMIZE", RESTRICTED_ALIAS}, + {"OR", RESTRICTED_SQL2016}, + {"ORDER", RESTRICTED_SQL2016}, + {"OUTER", RESTRICTED_JSQLPARSER}, + {"OUTPUT", RESTRICTED_JSQLPARSER}, + {"OPTIMIZE ", RESTRICTED_JSQLPARSER}, + {"PIVOT", RESTRICTED_JSQLPARSER}, + {"PROCEDURE", RESTRICTED_ALIAS}, + {"PUBLIC", RESTRICTED_ALIAS}, + {"RETURNING", RESTRICTED_JSQLPARSER}, + {"RIGHT", RESTRICTED_SQL2016}, + {"SAMPLE", RESTRICTED_ALIAS}, + {"SEL", RESTRICTED_ALIAS}, + {"SELECT", RESTRICTED_ALIAS}, + {"SEMI", RESTRICTED_JSQLPARSER}, + {"SET", RESTRICTED_JSQLPARSER}, + {"SOME", RESTRICTED_JSQLPARSER}, + {"START", RESTRICTED_JSQLPARSER}, + {"TABLES", RESTRICTED_ALIAS}, + {"TOP", RESTRICTED_SQL2016}, + {"TRAILING", RESTRICTED_SQL2016}, + {"UNBOUNDED", RESTRICTED_JSQLPARSER}, + {"UNION", RESTRICTED_SQL2016}, + {"UNIQUE", RESTRICTED_SQL2016}, + {"UNPIVOT", RESTRICTED_JSQLPARSER}, + {"USE", RESTRICTED_JSQLPARSER}, + {"USING", RESTRICTED_SQL2016}, + {"SQL_CACHE", RESTRICTED_JSQLPARSER}, + {"SQL_CALC_FOUND_ROWS", RESTRICTED_JSQLPARSER}, + {"SQL_NO_CACHE", RESTRICTED_JSQLPARSER}, + {"STRAIGHT_JOIN", RESTRICTED_JSQLPARSER}, + {"TABLESAMPLE", RESTRICTED_ALIAS}, + {"VALUE", RESTRICTED_JSQLPARSER}, + {"VALUES", RESTRICTED_SQL2016}, + {"VARYING", RESTRICTED_JSQLPARSER}, + {"WHEN", RESTRICTED_SQL2016}, + {"WHERE", RESTRICTED_SQL2016}, + {"WINDOW", RESTRICTED_SQL2016}, + {"WITH", RESTRICTED_SQL2016}, + {"XOR", RESTRICTED_JSQLPARSER}, + {"XMLSERIALIZE", RESTRICTED_JSQLPARSER}, + + // add keywords from the composite token definitions: + // tk= | tk= | tk= + // we will use the composite tokens instead, which are always hit first before the + // simple keywords + // @todo: figure out a way to remove these composite tokens, as they do more harm than + // good + {"SEL", RESTRICTED_JSQLPARSER}, + {"SELECT", RESTRICTED_JSQLPARSER}, + {"DATE", RESTRICTED_JSQLPARSER}, + {"TIME", RESTRICTED_JSQLPARSER}, + {"TIMESTAMP", RESTRICTED_JSQLPARSER}, + {"YEAR", RESTRICTED_JSQLPARSER}, + {"MONTH", RESTRICTED_JSQLPARSER}, + {"DAY", RESTRICTED_JSQLPARSER}, + {"HOUR", RESTRICTED_JSQLPARSER}, + {"MINUTE", RESTRICTED_JSQLPARSER}, + {"SECOND", RESTRICTED_JSQLPARSER}, + {"SUBSTR", RESTRICTED_JSQLPARSER}, + {"SUBSTRING", RESTRICTED_JSQLPARSER}, + {"TRIM", RESTRICTED_JSQLPARSER}, + {"POSITION", RESTRICTED_JSQLPARSER}, + {"OVERLAY", RESTRICTED_JSQLPARSER}, + {"NEXTVAL", RESTRICTED_COLUMN}, + + // @todo: Object Names should not start with Hex-Prefix, we shall not find that Token + {"0x", RESTRICTED_JSQLPARSER} + }; + + @SuppressWarnings({"PMD.ExcessiveMethodLength"}) + public static List getReservedKeywords(int restriction) { + ArrayList keywords = new ArrayList<>(); + for (Object[] data : ALL_RESERVED_KEYWORDS) { + int value = (int) data[1]; + + // test if bit is not set + if ((value & restriction) == restriction || (restriction & value) == value) { + keywords.add((String) data[0]); + } + } + + return keywords; + } + + /** + * @param args with: Grammar File, Keyword Documentation File + * @throws Exception + */ + public static void main(String[] args) throws Exception { + if (args.length < 2) { + throw new IllegalArgumentException("No filename provided aS context ARGS[0]"); + } + + File grammarFile = new File(args[0]); + if (grammarFile.exists() && grammarFile.canRead() && grammarFile.canWrite()) { + buildGrammarForRelObjectName(grammarFile); + buildGrammarForRelObjectNameWithoutValue(grammarFile); + } else { + throw new FileNotFoundException("Can't read file " + args[0]); + } + + File keywordDocumentationFile = new File(args[1]); + keywordDocumentationFile.createNewFile(); + if (keywordDocumentationFile.canWrite()) { + writeKeywordsDocumentationFile(keywordDocumentationFile); + } else { + throw new FileNotFoundException("Can't read file " + args[1]); + } + } + + public static TreeSet getAllKeywordsUsingRegex(File file) throws IOException { + Pattern tokenBlockPattern = Pattern.compile( + "TOKEN\\s*:\\s*/\\*.*\\*/*(?:\\r?\\n|\\r)\\{(?:[^}{]+|\\{(?:[^}{]+|\\{[^}{]*})*})*}", + Pattern.MULTILINE); + Pattern tokenStringValuePattern = Pattern.compile("\"(\\w{2,})\"", Pattern.MULTILINE); + + TreeSet allKeywords = new TreeSet<>(); + + Path path = file.toPath(); + Charset charset = Charset.defaultCharset(); + String content = new String(Files.readAllBytes(path), charset); + + Matcher tokenBlockmatcher = tokenBlockPattern.matcher(content); + while (tokenBlockmatcher.find()) { + String tokenBlock = tokenBlockmatcher.group(0); + Matcher tokenStringValueMatcher = tokenStringValuePattern.matcher(tokenBlock); + while (tokenStringValueMatcher.find()) { + String tokenValue = tokenStringValueMatcher.group(1); + // test if pure US-ASCII + if (CHARSET_ENCODER.canEncode(tokenValue) && tokenValue.matches("[A-Za-z]+")) { + allKeywords.add(tokenValue); + } + } + } + return allKeywords; + } + + + public static void buildGrammarForRelObjectNameWithoutValue(File file) throws Exception { + Pattern methodBlockPattern = Pattern.compile( + "String\\W*RelObjectNameWithoutValue\\W*\\(\\W*\\)\\W*:\\s*\\{(?:[^}{]+|\\{(?:[^}{]+|\\{[^}{]*})*})*}\\s*\\{(?:[^}{]+|\\{(?:[^}{]+|\\{[^}{]*})*})*}", + Pattern.MULTILINE); + + TreeSet allKeywords = getAllKeywords(file); + + for (String reserved : getReservedKeywords(RESTRICTED_JSQLPARSER)) { + allKeywords.remove(reserved); + } + + StringBuilder builder = new StringBuilder(); + builder.append("String RelObjectNameWithoutValue() :\n" + + "{ Token tk = null; }\n" + + "{\n" + // @todo: find a way to avoid those hardcoded compound tokens + + " ( tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= \n" + + " "); + + for (String keyword : allKeywords) { + builder.append(" | tk=\"").append(keyword).append("\""); + } + + builder.append(" )\n" + " { return tk.image; }\n" + "}"); + + replaceInFile(file, methodBlockPattern, builder.toString()); + } + + public static void buildGrammarForRelObjectName(File file) throws Exception { + // Pattern pattern = + // Pattern.compile("String\\W*RelObjectName\\W*\\(\\W*\\)\\W*:\\s*\\{(?:[^}{]+|\\{(?:[^}{]+|\\{[^}{]*})*})*}\\s*\\{(?:[^}{]+|\\{(?:[^}{]+|\\{[^}{]*})*})*}", + // Pattern.MULTILINE); + TreeSet allKeywords = new TreeSet<>(); + for (String reserved : getReservedKeywords(RESTRICTED_ALIAS)) { + allKeywords.add(reserved); + } + + for (String reserved : getReservedKeywords(RESTRICTED_JSQLPARSER & ~RESTRICTED_ALIAS)) { + allKeywords.remove(reserved); + } + + StringBuilder builder = new StringBuilder(); + builder.append("String RelObjectName() :\n" + + "{ Token tk = null; String result = null; }\n" + + "{\n" + + " (result = RelObjectNameWithoutValue()\n" + + " "); + + for (String keyword : allKeywords) { + builder.append(" | tk=\"").append(keyword).append("\""); + } + + builder.append(" )\n" + " { return tk!=null ? tk.image : result; }\n" + "}"); + + // @todo: Needs fine-tuning, we are not replacing this part yet + // replaceInFile(file, pattern, builder.toString()); + } + + public static TreeSet getAllKeywords(File file) throws Exception { + return getAllKeywordsUsingRegex(file); + } + + private static void replaceInFile(File file, Pattern pattern, String replacement) + throws IOException { + Path path = file.toPath(); + Charset charset = Charset.defaultCharset(); + + String content = new String(Files.readAllBytes(path), charset); + content = pattern.matcher(content).replaceAll(replacement); + Files.write(file.toPath(), content.getBytes(charset)); + } + + public static String rightPadding(String input, char ch, int length) { + return String.format("%" + (-length) + "s", input).replace(' ', ch); + } + + public static void writeKeywordsDocumentationFile(File file) throws IOException { + StringBuilder builder = new StringBuilder(); + builder.append("***********************\n"); + builder.append("Restricted Keywords\n"); + builder.append("***********************\n"); + builder.append("\n"); + + builder.append( + "The following Keywords are **restricted** in JSQLParser-|JSQLPARSER_VERSION| and must not be used for **Naming Objects**: \n"); + builder.append("\n"); + + builder.append("+----------------------+-------------+-----------+\n"); + builder.append("| **Keyword** | JSQL Parser | SQL:2016 |\n"); + builder.append("+----------------------+-------------+-----------+\n"); + + for (Object[] keywordDefinition : ALL_RESERVED_KEYWORDS) { + builder.append("| ").append(rightPadding(keywordDefinition[0].toString(), ' ', 20)) + .append(" | "); + + int value = (int) keywordDefinition[1]; + int restriction = RESTRICTED_JSQLPARSER; + String s = (value & restriction) == restriction || (restriction & value) == value + ? "Yes" + : ""; + builder.append(rightPadding(s, ' ', 11)).append(" | "); + + restriction = RESTRICTED_SQL2016; + s = (value & restriction) == restriction || (restriction & value) == value + ? "Yes" + : ""; + builder.append(rightPadding(s, ' ', 9)).append(" | "); + + builder.append("\n"); + builder.append("+----------------------+-------------+-----------+\n"); + } + try (FileWriter fileWriter = new FileWriter(file)) { + fileWriter.append(builder); + fileWriter.flush(); + } + } +} diff --git a/src/main/java/net/sf/jsqlparser/parser/SimpleCharStream.java b/src/main/java/net/sf/jsqlparser/parser/SimpleCharStream.java new file mode 100644 index 0000000..b96578e --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/parser/SimpleCharStream.java @@ -0,0 +1,511 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.parser; + +import java.io.IOException; + +@SuppressWarnings({"PMD.MethodNamingConventions", "PMD.CyclomaticComplexity"}) +public class SimpleCharStream { + + /** + * Whether parser is static. + */ + @SuppressWarnings("checkstyle:constantname") + public static final boolean staticFlag = false; + /** + * Position in buffer. + */ + public int bufpos = -1; + protected int bufline[]; + protected int bufcolumn[]; + protected int column = 0; + protected int line = 1; + protected boolean prevCharIsCR = false; + protected boolean prevCharIsLF = false; + protected Provider inputStream; + protected char[] buffer; + protected int maxNextCharInd = 0; + protected int inBuf = 0; + protected int tabSize = 1; + protected boolean trackLineColumn = true; + protected int totalCharsRead = 0; + protected int absoluteTokenBegin = 0; + int bufsize; + int available; + int tokenBegin; + private boolean isStringProvider; + + /** + * Constructor + * + * @param dstream + * @param startline + * @param startcolumn + * @param buffersize + */ + public SimpleCharStream(Provider dstream, int startline, + int startcolumn, int buffersize) { + inputStream = dstream; + isStringProvider = dstream instanceof StringProvider; + line = startline; + column = startcolumn - 1; + + if (isStringProvider) { + int bs = ((StringProvider) inputStream)._string.length(); + available = bufsize = bs; + bufline = new int[bs]; + bufcolumn = new int[bs]; + } else { + available = bufsize = buffersize; + buffer = new char[buffersize]; + bufline = new int[buffersize]; + bufcolumn = new int[buffersize]; + } + } + + /** + * Constructor + * + * @param dstream + * @param startline + * @param startcolumn + */ + public SimpleCharStream(Provider dstream, int startline, + int startcolumn) { + this(dstream, startline, startcolumn, 4096); + } + + /** + * Constructor + * + * @param dstream + */ + public SimpleCharStream(Provider dstream) { + this(dstream, 1, 1, 4096); + } + + public int getTabSize() { + return tabSize; + } + + public void setTabSize(int i) { + tabSize = i; + } + + public final int getAbsoluteTokenBegin() { + return absoluteTokenBegin; + } + + protected void ExpandBuff(boolean wrapAround) throws IOException { + char[] newbuffer = new char[bufsize + 2048]; + int newbufline[] = new int[bufsize + 2048]; + int newbufcolumn[] = new int[bufsize + 2048]; + + try { + if (wrapAround) { + System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin); + System.arraycopy(buffer, 0, newbuffer, bufsize - tokenBegin, bufpos); + buffer = newbuffer; + + System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin); + System.arraycopy(bufline, 0, newbufline, bufsize - tokenBegin, bufpos); + bufline = newbufline; + + System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin); + System.arraycopy(bufcolumn, 0, newbufcolumn, bufsize - tokenBegin, bufpos); + bufcolumn = newbufcolumn; + + maxNextCharInd = bufpos += bufsize - tokenBegin; + } else { + System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin); + buffer = newbuffer; + + System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin); + bufline = newbufline; + + System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin); + bufcolumn = newbufcolumn; + + maxNextCharInd = bufpos -= tokenBegin; + } + } catch (Throwable t) { + throw new IOException("Errow expanding the buffer.", t); + } + + bufsize += 2048; + available = bufsize; + tokenBegin = 0; + } + + protected void FillBuff() throws IOException { + if (!isStringProvider && maxNextCharInd == available) { + if (available == bufsize) { + if (tokenBegin > 2048) { + bufpos = maxNextCharInd = 0; + available = tokenBegin; + } else if (tokenBegin < 0) { + bufpos = maxNextCharInd = 0; + } else { + ExpandBuff(false); + } + } else if (available > tokenBegin) { + available = bufsize; + } else if ((tokenBegin - available) < 2048) { + ExpandBuff(true); + } else { + available = tokenBegin; + } + } + + int i; + try { + if (inputStream instanceof StringProvider) { + i = ((StringProvider) inputStream)._string.length(); + if (maxNextCharInd == i) { + throw new IOException(); + } + maxNextCharInd = i; + } else { + if ((i = inputStream.read(buffer, maxNextCharInd, + available - maxNextCharInd)) == -1) { + inputStream.close(); + throw new IOException(); + } else { + maxNextCharInd += i; + } + } + return; + } catch (IOException e) { + --bufpos; + backup(0); + if (tokenBegin == -1) { + tokenBegin = bufpos; + } + throw e; + } + } + + /** + * Start. + * + * @return the character read + * @throws IOException + */ + public char BeginToken() throws IOException { + tokenBegin = -1; + char c = readChar(); + tokenBegin = bufpos; + absoluteTokenBegin = totalCharsRead; + return c; + } + + protected void UpdateLineColumn(char c) { + column++; + + if (prevCharIsLF) { + prevCharIsLF = false; + line += column = 1; + } else if (prevCharIsCR) { + prevCharIsCR = false; + if (c == '\n') { + prevCharIsLF = true; + } else { + line += column = 1; + } + } + + switch (c) { + case '\r': + prevCharIsCR = true; + break; + case '\n': + prevCharIsLF = true; + break; + case '\t': + column--; + column += tabSize - (column % tabSize); + break; + default: + break; + } + + bufline[bufpos] = line; + bufcolumn[bufpos] = column; + } + + private char readChar(int pos) { + if (this.inputStream instanceof StringProvider) { + return ((StringProvider) inputStream)._string.charAt(pos); + } else { + return buffer[pos]; + } + } + + /** + * Read a character. + * + * @return the character read + * @throws IOException + */ + public char readChar() throws IOException { + if (inBuf > 0) { + --inBuf; + + if (++bufpos == bufsize) { + bufpos = 0; + } + + totalCharsRead++; + return readChar(bufpos); + } + + if (++bufpos >= maxNextCharInd) { + FillBuff(); + } + + totalCharsRead++; + + char c = readChar(bufpos); + + UpdateLineColumn(c); + return c; + } + + /** + * @return the column + * @deprecated @see #getEndColumn + */ + @Deprecated + public int getColumn() { + return bufcolumn[bufpos]; + } + + /** + * @return the line + * @deprecated @see #getEndLine + */ + @Deprecated + public int getLine() { + return bufline[bufpos]; + } + + /** + * @return get token end column number. + */ + public int getEndColumn() { + return bufcolumn[bufpos]; + } + + /** + * @return get token end line number. + */ + public int getEndLine() { + return bufline[bufpos]; + } + + /** + * @return get token beginning column number. + */ + public int getBeginColumn() { + return bufcolumn[tokenBegin]; + } + + /** + * @return get token beginning line number. + */ + public int getBeginLine() { + return bufline[tokenBegin]; + } + + /** + * Backup a number of characters. + * + * @param amount + */ + public void backup(int amount) { + + inBuf += amount; + totalCharsRead -= amount; + if ((bufpos -= amount) < 0) { + bufpos += bufsize; + } + } + + /** + * Reinitialise. + * + * @param dstream + * @param startline + * @param startcolumn + * @param buffersize + */ + public void ReInit(Provider dstream, int startline, + int startcolumn, int buffersize) { + inputStream = dstream; + isStringProvider = dstream instanceof StringProvider; + line = startline; + column = startcolumn - 1; + if (isStringProvider) { + int bs = ((StringProvider) inputStream)._string.length(); + available = bufsize = bs; + bufline = new int[bs]; + bufcolumn = new int[bs]; + } else { + if (buffer == null || buffersize != buffer.length) { + available = bufsize = buffersize; + buffer = new char[buffersize]; + bufline = new int[buffersize]; + bufcolumn = new int[buffersize]; + } + } + prevCharIsLF = prevCharIsCR = false; + tokenBegin = inBuf = maxNextCharInd = 0; + bufpos = -1; + } + + /** + * Reinitialise. + * + * @param dstream + * @param startline + * @param startcolumn + */ + public void ReInit(Provider dstream, int startline, + int startcolumn) { + ReInit(dstream, startline, startcolumn, 4096); + } + + /** + * Reinitialise. + * + * @param dstream + */ + public void ReInit(Provider dstream) { + ReInit(dstream, 1, 1, 4096); + } + + /** + * @return get token literal value. + */ + public String GetImage() { + if (isStringProvider) { + String data = ((StringProvider) inputStream)._string; + if (bufpos >= tokenBegin) { + return data.substring(tokenBegin, bufpos + 1); + } else { + return data.substring(tokenBegin, bufsize) + + data.substring(0, bufpos + 1); + } + } else { + if (bufpos >= tokenBegin) { + return new String(buffer, tokenBegin, bufpos - tokenBegin + 1); + } else { + return new String(buffer, tokenBegin, bufsize - tokenBegin) + + new String(buffer, 0, bufpos + 1); + } + } + } + + /** + * @param len + * @return get the suffix. + */ + public char[] GetSuffix(int len) { + + char[] ret = new char[len]; + + if (isStringProvider) { + String str = ((StringProvider) inputStream)._string; + if ((bufpos + 1) >= len) { + str.getChars(bufpos - len + 1, bufpos - len + 1 + len, ret, 0); + } else { + str.getChars(bufsize - (len - bufpos - 1), + bufsize - (len - bufpos - 1) + len - bufpos - 1, ret, 0); + str.getChars(0, bufpos + 1, ret, len - bufpos - 1); + } + } else { + if ((bufpos + 1) >= len) { + System.arraycopy(buffer, bufpos - len + 1, ret, 0, len); + } else { + System.arraycopy(buffer, bufsize - (len - bufpos - 1), ret, 0, + len - bufpos - 1); + System.arraycopy(buffer, 0, ret, len - bufpos - 1, bufpos + 1); + } + } + + return ret; + } + + /** + * Reset buffer when finished. + */ + public void Done() { + buffer = null; + bufline = null; + bufcolumn = null; + } + + /** + * Method to adjust line and column numbers for the start of a token. + * + * @param newLine + * @param newCol + */ + public void adjustBeginLineColumn(int newLine, int newCol) { + int nl = newLine; + int start = tokenBegin; + int len; + + if (bufpos >= tokenBegin) { + len = bufpos - tokenBegin + inBuf + 1; + } else { + len = bufsize - tokenBegin + bufpos + 1 + inBuf; + } + + int i = 0; + int j = 0; + int k = 0; + int nextColDiff = 0; + int columnDiff = 0; + + while (i < len && bufline[j = start % bufsize] == bufline[k = ++start % bufsize]) { + bufline[j] = nl; + nextColDiff = columnDiff + bufcolumn[k] - bufcolumn[j]; + bufcolumn[j] = newCol + columnDiff; + columnDiff = nextColDiff; + i++; + } + + if (i < len) { + bufline[j] = nl++; + bufcolumn[j] = newCol + columnDiff; + + while (i++ < len) { + if (bufline[j = start % bufsize] != bufline[++start % bufsize]) { + bufline[j] = nl++; + } else { + bufline[j] = nl; + } + } + } + + line = bufline[j]; + column = bufcolumn[j]; + } + + boolean getTrackLineColumn() { + return trackLineColumn; + } + + void setTrackLineColumn(boolean tlc) { + trackLineColumn = tlc; + } +} +/* JavaCC - OriginalChecksum=47e65cd0a1ed785f7a51c9e0c60893c9 (do not edit this line) */ diff --git a/src/main/java/net/sf/jsqlparser/parser/StatementListener.java b/src/main/java/net/sf/jsqlparser/parser/StatementListener.java new file mode 100644 index 0000000..3e26a00 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/parser/StatementListener.java @@ -0,0 +1,20 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.parser; + +import net.sf.jsqlparser.statement.Statement; + +/** + * @author Tobias Warneke (t.warneke@gmx.net) + */ +public interface StatementListener { + + void accept(Statement statement); +} diff --git a/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java b/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java new file mode 100644 index 0000000..244a388 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java @@ -0,0 +1,821 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.parser.feature; + +import net.sf.jsqlparser.expression.Function; +import net.sf.jsqlparser.expression.JdbcNamedParameter; +import net.sf.jsqlparser.expression.JdbcParameter; +import net.sf.jsqlparser.expression.OracleHierarchicalExpression; +import net.sf.jsqlparser.expression.OracleHint; +import net.sf.jsqlparser.expression.operators.relational.SupportsOldOracleJoinSyntax; +import net.sf.jsqlparser.statement.Block; +import net.sf.jsqlparser.statement.Commit; +import net.sf.jsqlparser.statement.CreateFunctionalStatement; +import net.sf.jsqlparser.statement.DeclareStatement; +import net.sf.jsqlparser.statement.DescribeStatement; +import net.sf.jsqlparser.statement.ExplainStatement; +import net.sf.jsqlparser.statement.ResetStatement; +import net.sf.jsqlparser.statement.SetStatement; +import net.sf.jsqlparser.statement.ShowColumnsStatement; +import net.sf.jsqlparser.statement.ShowStatement; +import net.sf.jsqlparser.statement.UseStatement; +import net.sf.jsqlparser.statement.alter.Alter; +import net.sf.jsqlparser.statement.alter.sequence.AlterSequence; +import net.sf.jsqlparser.statement.analyze.Analyze; +import net.sf.jsqlparser.statement.comment.Comment; +import net.sf.jsqlparser.statement.create.function.CreateFunction; +import net.sf.jsqlparser.statement.create.index.CreateIndex; +import net.sf.jsqlparser.statement.create.procedure.CreateProcedure; +import net.sf.jsqlparser.statement.create.schema.CreateSchema; +import net.sf.jsqlparser.statement.create.sequence.CreateSequence; +import net.sf.jsqlparser.statement.create.synonym.CreateSynonym; +import net.sf.jsqlparser.statement.create.table.CreateTable; +import net.sf.jsqlparser.statement.create.view.AlterView; +import net.sf.jsqlparser.statement.create.view.CreateView; +import net.sf.jsqlparser.statement.delete.Delete; +import net.sf.jsqlparser.statement.drop.Drop; +import net.sf.jsqlparser.statement.execute.Execute; +import net.sf.jsqlparser.statement.grant.Grant; +import net.sf.jsqlparser.statement.merge.Merge; +import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement; +import net.sf.jsqlparser.statement.select.Fetch; +import net.sf.jsqlparser.statement.select.First; +import net.sf.jsqlparser.statement.select.KSQLWindow; +import net.sf.jsqlparser.statement.select.Limit; +import net.sf.jsqlparser.statement.select.Offset; +import net.sf.jsqlparser.statement.select.OptimizeFor; +import net.sf.jsqlparser.statement.select.Pivot; +import net.sf.jsqlparser.statement.select.PivotXml; +import net.sf.jsqlparser.statement.select.Skip; +import net.sf.jsqlparser.statement.select.TableFunction; +import net.sf.jsqlparser.statement.select.Top; +import net.sf.jsqlparser.statement.select.UnPivot; +import net.sf.jsqlparser.statement.show.*; +import net.sf.jsqlparser.statement.truncate.Truncate; +import net.sf.jsqlparser.statement.update.Update; +import net.sf.jsqlparser.statement.upsert.Upsert; + +public enum Feature { + + // SQL KEYWORD FEATURES + /** + * "SELECT" + */ + select, + /** + * "GROUP BY" + */ + selectGroupBy, + /** + * "GROUPING SETS" + */ + selectGroupByGroupingSets, + /** + * "HAVING" + */ + selectHaving, + /** + * "INTO table(, table)*" + */ + selectInto, + + /** + * @see Limit + */ + limit, + /** + * "LIMIT NULL" + * + * @see Limit#isLimitNull() + */ + limitNull, + /** + * "LIMIT ALL" + * + * @see Limit#isLimitAll() + */ + limitAll, + /** + * "LIMIT offset, limit" + * + * @see Limit#getOffset() + */ + limitOffset, + /** + * "OFFSET offset" + * + * @see Offset + */ + offset, + /** + * "OFFSET offset param" where param is ROW | ROWS + * + * @see Offset#getOffsetParam() + */ + offsetParam, + + /** + * @see Fetch + */ + fetch, + /** + * "FETCH FIRST row_count (ROW | ROWS) ONLY" + * + * @see Fetch#isFetchParamFirst() + */ + fetchFirst, + /** + * "FETCH NEXT row_count (ROW | ROWS) ONLY" if not {@link #fetchFirst} + * + * @see Fetch#isFetchParamFirst() + */ + fetchNext, + + /** + * "JOIN" + */ + join, + /** + * join tables by ", OUTER" placing the join specification in WHERE-clause + */ + joinOuterSimple, + /** + * join tables by "," placing the join specification in WHERE-clause + */ + joinSimple, + /** + * "RIGHT" join + */ + joinRight, + /** + * "NATURAL" join + */ + joinNatural, + /** + * "FULL" join + */ + joinFull, + /** + * "LEFT" join + */ + joinLeft, + /** + * "CROSS" join + */ + joinCross, + /** + * "OUTER" join + */ + joinOuter, + /** + * "SEMI" join + */ + joinSemi, + /** + * "INNER" join + */ + joinInner, + /** + * "STRAIGHT_JOIN" join + */ + joinStraight, + /** + * "APPLY" join + */ + joinApply, + + joinWindow, joinUsingColumns, + + /** + * "SKIP variable" | "SKIP ?" | "SKIP rowCount" + * + * @see Skip + */ + skip, + /** + * "FIRST" \?|[0-9]+|variable or "LIMIT" \?|[0-9]+|variable + * + * @see First + */ + first, + /** + * "TOP" ? "PERCENT" + * + * @see Top + */ + top, + /** + * "OPTIMIZE FOR rowCount ROWS" + * + * @see OptimizeFor + */ + optimizeFor, + + /** + * "UNIQUE" keyword + */ + selectUnique, + /** + * "DISTINCT" keyword + */ + distinct, + /** + * "DISTINCT ON (col1, ...)" + */ + distinctOn, + + /** + * "ORDER BY" + */ + orderBy, + /** + * "ORDER BY expression [ NULLS { FIRST | LAST } ]" + */ + orderByNullOrdering, + + /** + * "FOR UPDATE" + */ + selectForUpdate, + + /** + * "FOR SHARE" + */ + selectForShare, + + /** + * "FOR KEY SHARE" + */ + selectForKeyShare, + + /** + * "NO KEY UPDATE" + */ + selectForNoKeyUpdate, + + /** + * "FOR UPDATE OF table" + */ + selectForUpdateOfTable, + /** + * "FOR UPDATE WAIT timeout" + */ + selectForUpdateWait, + /** + * "FOR UPDATE NOWAIT" + */ + selectForUpdateNoWait, + /** + * "FOR UPDATE SKIP LOCKED" + */ + selectForUpdateSkipLocked, + + + /** + * SQL "INSERT" statement is allowed + */ + insert, + /** + * "INSERT .. SELECT" + */ + insertFromSelect, + /** + * "LOW_PRIORITY | DELAYED | HIGH_PRIORITY | IGNORE" + */ + insertModifierPriority, + /** + * "IGNORE" + */ + insertModifierIgnore, + /** + * "INSERT .. SET" + */ + insertUseSet, + /** + * "ON DUPLICATE KEY UPDATE" + */ + insertUseDuplicateKeyUpdate, + /** + * "RETURNING *" + */ + insertReturningAll, + /** + * "RETURNING expr(, expr)*" + * + * @see net.sf.jsqlparser.expression.operators.relational.ExpressionList + */ + insertReturningExpressionList, + + /** + * "VALUES" + */ + insertValues, + /** + * @see net.sf.jsqlparser.statement.select.Values + */ + values, + + /** + * SQL "TABLE table_name [ORDER BY column_name] [LIMIT number [OFFSET number]]“ + */ + tableStatement, + + /** + * SQL "UPDATE" statement is allowed + * + * @see Update + */ + update, + /** + * "UPDATE table1 SET ... FROM table2 + */ + updateFrom, + /** + * "UPDATE table1, table2 ..." + */ + updateJoins, + /** + * UPDATE table SET (col, ...) = (SELECT col, ... )" + */ + updateUseSelect, updateOrderBy, updateLimit, + /** + * "RETURNING expr(, expr)*" + * + * @see net.sf.jsqlparser.statement.select.SelectItem + */ + updateReturning, + /** + * SQL "DELETE" statement is allowed + * + * @see Delete + */ + delete, + /** + * "DELETE FROM table1, table1 ..." + */ + deleteJoin, + /** + * "DELETE table1, table1 FROM table ..." + */ + deleteTables, + /** + * "LIMIT row_count" + */ + deleteLimit, + /** + * "ORDER BY ..." + */ + deleteOrderBy, + /** + * "RETURNING expr(, expr)*" + * + * @see net.sf.jsqlparser.statement.select.SelectItem + */ + deleteReturningExpressionList, + + /** + * SQL "UPSERT" statement is allowed + * + * @see Upsert + * @see https://wiki.postgresql.org/wiki/UPSERT + */ + upsert, + /** + * SQL "MERGE" statement is allowed + * + * @see Merge + */ + merge, + + /** + * SQL "ALTER" statement is allowed + * + * @see Alter + */ + alterTable, + /** + * SQL "ALTER SEQUENCE" statement is allowed + * + * @see AlterSequence + */ + alterSequence, + /** + * SQL "ALTER VIEW" statement is allowed + * + * @see AlterView + */ + alterView, + + /** + * SQL "REFRESH MATERIALIZED VIEW" statement is allowed + * + * @see RefreshMaterializedViewStatement + */ + refreshMaterializedView, refreshMaterializedWithDataView, refreshMaterializedWithNoDataView, + + /** + * SQL "REPLACE VIEW" statement is allowed + * + * @see AlterView + */ + alterViewReplace, + /** + * SQL "ALTER INDEX" statement is allowed + */ + alterIndex, + + + /** + * SQL "ANALYZE" statement is allowed + * + * @see Analyze + */ + analyze, + + /** + * SQL "TRUNCATE" statement is allowed + * + * @see Truncate + */ + truncate, + /** + * SQL "CALL|EXEC|EXECUTE" stored procedure is allowed + * + * @see Execute + */ + execute, executeExec, executeCall, executeExecute, + + /** + * SQL "EXECUTE" statement is allowed + */ + executeStatement, + /** + * SQL "EXECUTE IMMEDIATE" statement is allowed + */ + executeStatementImmediate, + + executeUsing, + /** + * SQL "REPLACE" statement is allowed + */ + @Deprecated + replace, + /** + * SQL "DROP" statement is allowed + * + * @see Drop + */ + drop, dropTable, dropIndex, dropView, dropSchema, dropSequence, dropTableIfExists, dropIndexIfExists, dropViewIfExists, dropSchemaIfExists, dropSequenceIfExists, + + /** + * SQL "CREATE SCHEMA" statement is allowed + * + * @see CreateSchema + */ + createSchema, + /** + * SQL "CREATE VIEW" statement is allowed + * + * @see CreateView + */ + createView, + /** + * "CREATE FORCE VIEW" + */ + createViewForce, + /** + * "CREATE TEMPORARAY VIEW" + */ + createViewTemporary, + /** + * "CREATE OR REPLACE VIEW" + */ + createOrReplaceView, + /** + * SQL "CREATE MATERIALIZED VIEW" statement is allowed + */ + createViewMaterialized, + + /** + * SQL "CREATE VIEW(x comment 'x', y comment 'y') comment 'view'" statement is allowed + */ + createViewWithComment, + + /** + * SQL "CREATE TABLE" statement is allowed + * + * @see CreateTable + */ + createTable, + /** + * "CREATE GLOBAL UNLOGGED" + */ + createTableUnlogged, + /** + * i.e. "CREATE GLOBAL TEMPORARY TABLE", "CREATE SHARDED TABLE" + */ + createTableCreateOptionStrings, + /** + * i.e. "ENGINE = InnoDB AUTO_INCREMENT = 8761 DEFAULT CHARSET = utf8" + */ + createTableTableOptionStrings, + /** + * "CREATE TABLE IF NOT EXISTS table" + */ + createTableIfNotExists, + /** + * " ROW MOVEMENT" + */ + createTableRowMovement, + /** + * "CREATE TABLE (colspec) SELECT ... + */ + createTableFromSelect, + /** + * SQL "CREATE INDEX" statement is allowed + * + * @see CreateIndex + */ + createIndex, + /** + * SQL "CREATE SEQUENCE" statement is allowed + * + * @see CreateSequence + */ + createSequence, + /** + * SQL "CREATE SYNONYM" statement is allowed + * + * @see CreateSynonym + */ + createSynonym, + /** + * SQL "CREATE TRIGGER" statement is allowed + */ + createTrigger, + /** + * SQL "COMMIT" statement is allowed + * + * @see Commit + */ + commit, + /** + * SQL "COMMENT ON" statement is allowed + * + * @see Comment + */ + comment, + /** + * "COMMENT ON table" + */ + commentOnTable, + /** + * "COMMENT ON column" + */ + commentOnColumn, + /** + * "COMMENT ON view" + */ + commentOnView, + + /** + * SQL "DESCRIBE" statement is allowed + * + * @see DescribeStatement + */ + describe, + + /** + * SQL "DESC" statement is allowed + * + * @see DescribeStatement + */ + desc, + + /** + * SQL "EXPLAIN" statement is allowed + * + * @see ExplainStatement + */ + explain, + /** + * @see ShowStatement + */ + show, + /** + * @see ShowTablesStatement + */ + showTables, + /** + * @see ShowColumnsStatement + */ + showColumns, + /** + * @see ShowIndexStatement + */ + showIndex, + /** + * @see UseStatement + */ + use, + /** + * @see Grant + */ + grant, + /** + * @see Function + */ + function, + /** + * @see CreateFunction + */ + createFunction, + /** + * @see CreateProcedure + */ + createProcedure, + /** + * @see CreateFunctionalStatement + */ + functionalStatement, + /** + * SQL block starting with "BEGIN" and ends with "END" statement is allowed + * + * @see Block + */ + block, + /** + * @see DeclareStatement + */ + declare, + /** + * @see SetStatement + */ + set, + /** + * @see ResetStatement + */ + reset, + /** + * @see Pivot + */ + pivot, + /** + * @see UnPivot + */ + unpivot, + /** + * @see PivotXml + */ + pivotXml, + + setOperation, setOperationUnion, setOperationIntersect, setOperationExcept, setOperationMinus, + + /** + * "WITH name query" + */ + withItem, withItemRecursive, + + lateralSubSelect, + /** + * @see net.sf.jsqlparser.statement.select.Values + */ + valuesList, + /** + * @see TableFunction + */ + tableFunction, + + // JDBC + /** + * @see JdbcParameter + */ + jdbcParameter, + /** + * @see JdbcNamedParameter + */ + jdbcNamedParameter, + + // EXPRESSIONS + /** + * "LIKE" + */ + exprLike, + /** + * "SIMILAR TO" + */ + exprSimilarTo, + + // VENDOR SPECIFIC SYNTAX FEATURES + + /** + * @see KSQLWindow + */ + kSqlWindow, + + // ORACLE + + /** + * allows old oracle join syntax (+) + * + * @see SupportsOldOracleJoinSyntax + */ + oracleOldJoinSyntax, + /** + * allows oracle prior position + * + * @see SupportsOldOracleJoinSyntax + */ + oraclePriorPosition, + /** + * @see OracleHint + */ + oracleHint, + /** + * oracle SQL "CONNECT BY" + * + * @see OracleHierarchicalExpression + */ + oracleHierarchicalExpression, oracleOrderBySiblings, + + // MYSQL + + mySqlHintStraightJoin, mysqlSqlCacheFlag, mysqlCalcFoundRows, + + // SQLSERVER + + /** + * "FOR XML PATH(...)" + */ + selectForXmlPath, + + /** + * allows square brackets for names, disabled by default + */ + allowSquareBracketQuotation(false), + + /** + * allow parsing of RDBMS specific syntax by switching off SQL Standard Compliant Syntax + */ + allowPostgresSpecificSyntax(false), + + // PERFORMANCE + + /** + * allows complex expression parameters or named parameters for functions will be switched off, + * when deep nesting of functions is detected + */ + allowComplexParsing(true), + + /** + * allows passing through Unsupported Statements as a plain List of Tokens needs to be switched + * off, when VALIDATING statements or parsing blocks + */ + allowUnsupportedStatements(false), + + timeOut(8000), + + /** + * allows Backslash '\' as Escape Character + */ + allowBackslashEscapeCharacter(false), + ; + + private final Object value; + private final boolean configurable; + + /** + * a feature which can't configured within the parser + */ + Feature() { + this.value = null; + this.configurable = false; + } + + /** + * a feature which can be configured by {@link FeatureConfiguration} + * + * @param value The Value Object of the Parameter. + */ + Feature(Object value) { + this.value = value; + this.configurable = true; + } + + public Object getDefaultValue() { + return value; + } + + public boolean isConfigurable() { + return configurable; + } + +} diff --git a/src/main/java/net/sf/jsqlparser/parser/feature/FeatureConfiguration.java b/src/main/java/net/sf/jsqlparser/parser/feature/FeatureConfiguration.java new file mode 100644 index 0000000..da5ddd2 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/parser/feature/FeatureConfiguration.java @@ -0,0 +1,72 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.parser.feature; + +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class FeatureConfiguration { + + private static final Logger LOG = Logger.getLogger(FeatureConfiguration.class.getName()); + + private Map featureEnabled = new EnumMap<>(Feature.class); + + public FeatureConfiguration() { + // set default-value for all switchable features + EnumSet.allOf(Feature.class).stream().filter(Feature::isConfigurable) + .forEach(f -> setValue(f, f.getDefaultValue())); + } + + /** + * @param feature + * @param value + * @return this + */ + public FeatureConfiguration setValue(Feature feature, Object value) { + if (feature.isConfigurable()) { + featureEnabled.put(feature, value); + } else { + if (LOG.isLoggable(Level.WARNING)) { + LOG.warning(feature.name() + " is not switchable - cannot set enabled = " + value); + } + } + return this; + } + + /** + * @param feature + * @return the configured feature value - can be null + * @throws IllegalStateException - if given {@link Feature#isConfigurable()} == false + */ + public Object getValue(Feature feature) { + if (feature.isConfigurable()) { + return featureEnabled.get(feature); + } else { + throw new IllegalStateException("The feature " + feature + " is not configurable!"); + } + } + + public boolean getAsBoolean(Feature f) { + return Boolean.parseBoolean(String.valueOf(getValue(f))); + } + + public Long getAsLong(Feature f) { + return Long.valueOf(String.valueOf(getValue(f))); + } + + public String getAsString(Feature f) { + Object value = getValue(f); + return value == null ? null : String.valueOf(value); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/parser/feature/FeatureSet.java b/src/main/java/net/sf/jsqlparser/parser/feature/FeatureSet.java new file mode 100644 index 0000000..551e0c5 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/parser/feature/FeatureSet.java @@ -0,0 +1,60 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.parser.feature; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import net.sf.jsqlparser.util.validation.feature.FeaturesAllowed; + +public interface FeatureSet { + + Set getFeatures(); + + /** + * @return true if the feature is identical to one of the features contained in + * this set, false otherwise + */ + default boolean contains(Feature feature) { + return getFeatures().contains(feature); + } + + /** + * @return a new {@link HashSet} with a copy of supported features + */ + default Set getFeaturesClone() { + return new HashSet<>(getFeatures()); + } + + /** + * @param features + * @return all features within this feature set which are not contained in given set + */ + default Set getNotContained(Collection features) { + Set f = getFeaturesClone(); + f.removeAll(features); + return f; + } + + /** + * @param features + * @return all features within this feature set which are contained in given set too. + */ + default Set retainAll(Collection features) { + Set f = getFeaturesClone(); + f.retainAll(features); + return f; + } + + default ModifyableFeatureSet copy() { + return new FeaturesAllowed().add(this.getFeatures()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/parser/feature/ModifyableFeatureSet.java b/src/main/java/net/sf/jsqlparser/parser/feature/ModifyableFeatureSet.java new file mode 100644 index 0000000..7c1954f --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/parser/feature/ModifyableFeatureSet.java @@ -0,0 +1,60 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.parser.feature; + +import java.util.Collection; + +public interface ModifyableFeatureSet extends FeatureSet { + + /** + * @param featureSets + * @return this + */ + ModifyableFeatureSet add(FeatureSet... featureSets); + + /** + * @param features + * @return this + */ + ModifyableFeatureSet add(Feature... features); + + /** + * @param features + * @return this + */ + ModifyableFeatureSet add(Collection features); + + /** + * @param featureSets + * @return this + */ + ModifyableFeatureSet remove(FeatureSet... featureSets); + + /** + * @param features + * @return this + */ + ModifyableFeatureSet remove(Feature... features); + + /** + * @param features + * @return this + */ + ModifyableFeatureSet remove(Collection features); + + /** + * makes the inner {@link Feature}-set unmodifiable + * + * @return this + * @see #copy() + */ + FeatureSet unmodifyable(); + +} diff --git a/src/main/java/net/sf/jsqlparser/schema/Column.java b/src/main/java/net/sf/jsqlparser/schema/Column.java new file mode 100644 index 0000000..b14c1d0 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/schema/Column.java @@ -0,0 +1,192 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.schema; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import net.sf.jsqlparser.expression.ArrayConstructor; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +/** + * A column. It can have the table name it belongs to. + */ +public class Column extends ASTNodeAccessImpl implements Expression, MultiPartName { + + private Table table; + private String columnName; + private String commentText; + private ArrayConstructor arrayConstructor; + private String tableDelimiter = "."; + + public Column() {} + + public Column(Table table, String columnName) { + setTable(table); + setColumnName(columnName); + } + + public Column(List nameParts) { + this(nameParts, nameParts.size() > 1 ? Collections.nCopies(nameParts.size() - 1, ".") + : new ArrayList<>()); + } + + public Column(List nameParts, List delimiters) { + this( + nameParts.size() > 1 ? new Table(nameParts.subList(0, nameParts.size() - 1), + delimiters.subList(0, delimiters.size() - 1)) : null, + nameParts.get(nameParts.size() - 1)); + setTableDelimiter(delimiters.isEmpty() ? "." : delimiters.get(delimiters.size() - 1)); + } + + public Column(String columnName) { + this(null, columnName); + } + + public ArrayConstructor getArrayConstructor() { + return arrayConstructor; + } + + public Column setArrayConstructor(ArrayConstructor arrayConstructor) { + this.arrayConstructor = arrayConstructor; + return this; + } + + /** + * Retrieve the information regarding the {@code Table} this {@code Column} does belong to, if + * any can be inferred. + *

+ * The inference is based only on local information, and not on the whole SQL command. For + * example, consider the following query:

+ * + *
+     *  SELECT x FROM Foo
+     * 
+ * + *
Given the {@code Column} called {@code x}, this method would return + * {@code null}, and not the info about the table {@code Foo}. On the other hand, consider: + *
+ * + *
+     *  SELECT t.x FROM Foo t
+     * 
+ * + *
Here, we will get a {@code Table} object for a table called {@code t}. But + * because the inference is local, such object will not know that {@code t} is just an alias for + * {@code Foo}. + * + * @return an instance of {@link net.sf.jsqlparser.schema.Table} representing the table this + * column does belong to, if it can be inferred. Can be {@code null}. + */ + public Table getTable() { + return table; + } + + public void setTable(Table table) { + this.table = table; + } + + public String getColumnName() { + return columnName; + } + + public void setColumnName(String string) { + columnName = string; + } + + public String getTableDelimiter() { + return tableDelimiter; + } + + public void setTableDelimiter(String tableDelimiter) { + this.tableDelimiter = tableDelimiter; + } + + @Override + public String getFullyQualifiedName() { + return getFullyQualifiedName(false); + } + + public String getFullyQualifiedName(boolean aliases) { + StringBuilder fqn = new StringBuilder(); + + if (table != null) { + if (table.getAlias() != null && aliases) { + fqn.append(table.getAlias().getName()); + } else { + fqn.append(table.getFullyQualifiedName()); + } + } + if (fqn.length() > 0) { + fqn.append(tableDelimiter); + } + if (columnName != null) { + fqn.append(columnName); + } + + if (commentText != null) { + fqn.append(" COMMENT "); + fqn.append(commentText); + } + + if (arrayConstructor != null) { + fqn.append(arrayConstructor); + } + + return fqn.toString(); + } + + // old and confusing, don't use it! + @Deprecated + public String getName(boolean aliases) { + return columnName; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Override + public String toString() { + return getFullyQualifiedName(true); + } + + public Column withTable(Table table) { + this.setTable(table); + return this; + } + + public Column withColumnName(String columnName) { + this.setColumnName(columnName); + return this; + } + + public Column withCommentText(String commentText) { + this.setCommentText(commentText); + return this; + } + + public Column withTableDelimiter(String delimiter) { + this.setTableDelimiter(delimiter); + return this; + } + + public String getCommentText() { + return commentText; + } + + public void setCommentText(String commentText) { + this.commentText = commentText; + } +} diff --git a/src/main/java/net/sf/jsqlparser/schema/Database.java b/src/main/java/net/sf/jsqlparser/schema/Database.java new file mode 100644 index 0000000..00f65f8 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/schema/Database.java @@ -0,0 +1,74 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.schema; + +public final class Database implements MultiPartName { + + private Server server; + private String databaseName; + + public Database(String databaseName) { + setDatabaseName(databaseName); + } + + public Database(Server server, String databaseName) { + setServer(server); + setDatabaseName(databaseName); + } + + public Server getServer() { + return server; + } + + public void setServer(Server server) { + this.server = server; + } + + public String getDatabaseName() { + return databaseName; + } + + public void setDatabaseName(String databaseName) { + this.databaseName = databaseName; + } + + @Override + public String getFullyQualifiedName() { + String fqn = ""; + + if (server != null) { + fqn += server.getFullyQualifiedName(); + } + if (!fqn.isEmpty()) { + fqn += "."; + } + + if (databaseName != null) { + fqn += databaseName; + } + + return fqn; + } + + @Override + public String toString() { + return getFullyQualifiedName(); + } + + public Database withServer(Server server) { + this.setServer(server); + return this; + } + + public Database withDatabaseName(String databaseName) { + this.setDatabaseName(databaseName); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/schema/MultiPartName.java b/src/main/java/net/sf/jsqlparser/schema/MultiPartName.java new file mode 100644 index 0000000..5796085 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/schema/MultiPartName.java @@ -0,0 +1,15 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.schema; + +public interface MultiPartName { + + String getFullyQualifiedName(); +} diff --git a/src/main/java/net/sf/jsqlparser/schema/Sequence.java b/src/main/java/net/sf/jsqlparser/schema/Sequence.java new file mode 100644 index 0000000..d58f04a --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/schema/Sequence.java @@ -0,0 +1,214 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.schema; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +/** + * Represents the database type for a {@code SEQUENCE} + */ +public class Sequence extends ASTNodeAccessImpl implements MultiPartName { + + private static final int NAME_IDX = 0; + private static final int SCHEMA_IDX = 1; + private static final int DATABASE_IDX = 2; + private static final int SERVER_IDX = 3; + private List partItems = new ArrayList<>(); + + private List parameters; + + public Sequence() {} + + public Sequence(List partItems) { + this.partItems = new ArrayList<>(partItems); + Collections.reverse(this.partItems); + } + + public List getParameters() { + return parameters; + } + + public void setParameters(List parameters) { + this.parameters = parameters; + } + + public Database getDatabase() { + return new Database(getIndex(DATABASE_IDX)); + } + + public void setDatabase(Database database) { + setIndex(DATABASE_IDX, database.getDatabaseName()); + if (database.getServer() != null) { + setIndex(SERVER_IDX, database.getServer().getFullyQualifiedName()); + } + } + + public Sequence withDatabase(Database database) { + setDatabase(database); + return this; + } + + public String getSchemaName() { + return getIndex(SCHEMA_IDX); + } + + public void setSchemaName(String string) { + setIndex(SCHEMA_IDX, string); + } + + public Sequence withSchemaName(String string) { + setSchemaName(string); + return this; + } + + public String getName() { + return getIndex(NAME_IDX); + } + + public void setName(String string) { + setIndex(NAME_IDX, string); + } + + public Sequence withName(String string) { + setName(string); + return this; + } + + private void setIndex(int idx, String value) { + int size = partItems.size(); + for (int i = 0; i < idx - size + 1; i++) { + partItems.add(null); + } + partItems.set(idx, value); + } + + private String getIndex(int idx) { + if (idx < partItems.size()) { + return partItems.get(idx); + } else { + return null; + } + } + + @Override + public String getFullyQualifiedName() { + StringBuilder fqn = new StringBuilder(); + + for (int i = partItems.size() - 1; i >= 0; i--) { + String part = partItems.get(i); + if (part == null) { + part = ""; + } + fqn.append(part); + if (i != 0) { + fqn.append("."); + } + } + + return fqn.toString(); + } + + @Override + public String toString() { + StringBuilder sql = new StringBuilder(getFullyQualifiedName()); + if (parameters != null) { + for (Sequence.Parameter parameter : parameters) { + sql.append(" ").append(parameter.formatParameter()); + } + } + return sql.toString(); + } + + public Sequence withParameters(List parameters) { + this.setParameters(parameters); + return this; + } + + public Sequence addParameters(Parameter... parameters) { + List collection = Optional.ofNullable(getParameters()).orElseGet(ArrayList::new); + Collections.addAll(collection, parameters); + return this.withParameters(collection); + } + + public Sequence addParameters(Collection parameters) { + List collection = Optional.ofNullable(getParameters()).orElseGet(ArrayList::new); + collection.addAll(parameters); + return this.withParameters(collection); + } + + /** + * The available parameters to a sequence + */ + public enum ParameterType { + INCREMENT_BY, START_WITH, RESTART_WITH, MAXVALUE, NOMAXVALUE, MINVALUE, NOMINVALUE, CYCLE, NOCYCLE, CACHE, NOCACHE, ORDER, NOORDER, KEEP, NOKEEP, SESSION, GLOBAL; + + public static ParameterType from(String type) { + return Enum.valueOf(ParameterType.class, type.toUpperCase()); + } + } + + /** + * Represents a parameter when declaring a sequence + */ + public static class Parameter { + + private final ParameterType option; + private Long value; + + public Parameter(ParameterType option) { + this.option = option; + } + + public Long getValue() { + return value; + } + + public void setValue(Long value) { + this.value = value; + } + + public String formatParameter() { + switch (option) { + case INCREMENT_BY: + return prefix("INCREMENT BY"); + case START_WITH: + return prefix("START WITH"); + case RESTART_WITH: + if (value != null) { + return prefix("RESTART WITH"); + } else { + return "RESTART"; + } + case MAXVALUE: + case MINVALUE: + case CACHE: + return prefix(option.name()); + default: + // fallthrough just return option name + return option.name(); + } + } + + private String prefix(String prefix) { + return prefix + " " + value; + } + + public Parameter withValue(Long value) { + this.setValue(value); + return this; + } + } +} diff --git a/src/main/java/net/sf/jsqlparser/schema/Server.java b/src/main/java/net/sf/jsqlparser/schema/Server.java new file mode 100644 index 0000000..d04c5c6 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/schema/Server.java @@ -0,0 +1,86 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.schema; + +import java.util.regex.*; + +public final class Server implements MultiPartName { + + public static final Pattern SERVER_PATTERN = + Pattern.compile("\\[([^\\]]+?)(?:\\\\([^\\]]+))?\\]"); + + private String serverName; + + private String instanceName; + + private String simpleName; + + public Server(String serverAndInstanceName) { + if (serverAndInstanceName != null) { + final Matcher matcher = SERVER_PATTERN.matcher(serverAndInstanceName); + if (!matcher.find()) { + simpleName = serverAndInstanceName; + } else { + setServerName(matcher.group(1)); + setInstanceName(matcher.group(2)); + } + } + } + + public Server(String serverName, String instanceName) { + setServerName(serverName); + setInstanceName(instanceName); + } + + public String getServerName() { + return serverName; + } + + public void setServerName(String serverName) { + this.serverName = serverName; + } + + public String getInstanceName() { + return instanceName; + } + + public void setInstanceName(String instanceName) { + this.instanceName = instanceName; + } + + @Override + public String getFullyQualifiedName() { + if (serverName != null && !serverName.isEmpty() && instanceName != null + && !instanceName.isEmpty()) { + return String.format("[%s\\%s]", serverName, instanceName); + } else if (serverName != null && !serverName.isEmpty()) { + return String.format("[%s]", serverName); + } else if (simpleName != null && !simpleName.isEmpty()) { + return simpleName; + } else { + return ""; + } + } + + @Override + public String toString() { + return getFullyQualifiedName(); + } + + public Server withServerName(String serverName) { + this.setServerName(serverName); + return this; + } + + public Server withInstanceName(String instanceName) { + this.setInstanceName(instanceName); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/schema/Synonym.java b/src/main/java/net/sf/jsqlparser/schema/Synonym.java new file mode 100644 index 0000000..ae938d3 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/schema/Synonym.java @@ -0,0 +1,114 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.schema; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class Synonym extends ASTNodeAccessImpl implements MultiPartName { + + private static final int NAME_IDX = 0; + private static final int SCHEMA_IDX = 1; + private static final int DATABASE_IDX = 2; + private static final int SERVER_IDX = 3; + private List partItems = new ArrayList<>(); + + public Synonym() {} + + public Synonym(List partItems) { + this.partItems = new ArrayList<>(partItems); + Collections.reverse(this.partItems); + } + + public Database getDatabase() { + return new Database(getIndex(DATABASE_IDX)); + } + + public void setDatabase(Database database) { + setIndex(DATABASE_IDX, database.getDatabaseName()); + if (database.getServer() != null) { + setIndex(SERVER_IDX, database.getServer().getFullyQualifiedName()); + } + } + + public Synonym withDatabase(Database database) { + setDatabase(database); + return this; + } + + public String getSchemaName() { + return getIndex(SCHEMA_IDX); + } + + public void setSchemaName(String string) { + setIndex(SCHEMA_IDX, string); + } + + public Synonym withSchemaName(String string) { + setSchemaName(string); + return this; + } + + public String getName() { + return getIndex(NAME_IDX); + } + + public void setName(String string) { + setIndex(NAME_IDX, string); + } + + public Synonym withName(String string) { + setName(string); + return this; + } + + private void setIndex(int idx, String value) { + int size = partItems.size(); + for (int i = 0; i < idx - size + 1; i++) { + partItems.add(null); + } + partItems.set(idx, value); + } + + private String getIndex(int idx) { + if (idx < partItems.size()) { + return partItems.get(idx); + } else { + return null; + } + } + + @Override + public String getFullyQualifiedName() { + StringBuilder fqn = new StringBuilder(); + + for (int i = partItems.size() - 1; i >= 0; i--) { + String part = partItems.get(i); + if (part == null) { + part = ""; + } + fqn.append(part); + if (i != 0) { + fqn.append("."); + } + } + + return fqn.toString(); + } + + @Override + public String toString() { + StringBuilder sql = new StringBuilder(getFullyQualifiedName()); + return sql.toString(); + } +} diff --git a/src/main/java/net/sf/jsqlparser/schema/Table.java b/src/main/java/net/sf/jsqlparser/schema/Table.java new file mode 100644 index 0000000..dd9f5a6 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/schema/Table.java @@ -0,0 +1,330 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.schema; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import net.sf.jsqlparser.expression.Alias; +import net.sf.jsqlparser.expression.MySQLIndexHint; +import net.sf.jsqlparser.expression.SQLServerHints; +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; +import net.sf.jsqlparser.statement.select.FromItem; +import net.sf.jsqlparser.statement.select.FromItemVisitor; +import net.sf.jsqlparser.statement.select.IntoTableVisitor; +import net.sf.jsqlparser.statement.select.Pivot; +import net.sf.jsqlparser.statement.select.SampleClause; +import net.sf.jsqlparser.statement.select.UnPivot; + +/** + * A table. It can have an alias and the schema name it belongs to. + */ +public class Table extends ASTNodeAccessImpl implements FromItem, MultiPartName { + + // private Database database; + // private String schemaName; + // private String name; + private static final int NAME_IDX = 0; + + private static final int SCHEMA_IDX = 1; + + private static final int DATABASE_IDX = 2; + + private static final int SERVER_IDX = 3; + + private List partItems = new ArrayList<>(); + + private List partDelimiters = new ArrayList<>(); + + private Alias alias; + + private SampleClause sampleClause; + + private Pivot pivot; + + private UnPivot unpivot; + + private MySQLIndexHint mysqlHints; + + private SQLServerHints sqlServerHints; + + public Table() {} + + public Table(String name) { + setName(name); + } + + public Table(String schemaName, String name) { + setName(name); + setSchemaName(schemaName); + } + + public Table(Database database, String schemaName, String name) { + setName(name); + setSchemaName(schemaName); + setDatabase(database); + } + + public Table(String catalogName, String schemaName, String tableName) { + setName(tableName); + setSchemaName(schemaName); + setDatabase(new Database(catalogName)); + } + + public Table(List partItems) { + this.partItems = new ArrayList<>(partItems); + Collections.reverse(this.partItems); + } + + public Table(List partItems, List partDelimiters) { + if (partDelimiters.size() != partItems.size() - 1) { + throw new IllegalArgumentException( + "the length of the delimiters list must be 1 less than nameParts"); + } + this.partItems = new ArrayList<>(partItems); + this.partDelimiters = new ArrayList<>(partDelimiters); + Collections.reverse(this.partItems); + Collections.reverse(this.partDelimiters); + } + + public String getCatalogName() { + return getIndex(DATABASE_IDX); + } + + public Database getDatabase() { + return new Database(getIndex(DATABASE_IDX)); + } + + public void setDatabase(Database database) { + setIndex(DATABASE_IDX, database.getDatabaseName()); + if (database.getServer() != null) { + setIndex(SERVER_IDX, database.getServer().getFullyQualifiedName()); + } + } + + public Table withDatabase(Database database) { + setDatabase(database); + return this; + } + + public String getSchemaName() { + return getIndex(SCHEMA_IDX); + } + + public void setSchemaName(String schemaName) { + setIndex(SCHEMA_IDX, schemaName); + } + + public Table withSchemaName(String schemaName) { + setSchemaName(schemaName); + return this; + } + + public String getName() { + String name = getIndex(NAME_IDX); + if (name != null && name.contains("@")) { + int pos = name.lastIndexOf('@'); + if (pos > 0) { + name = name.substring(0, pos); + } + } + return name; + } + + public void setName(String name) { + setIndex(NAME_IDX, name); + } + + public String getDBLinkName() { + String name = getIndex(NAME_IDX); + if (name != null && name.contains("@")) { + int pos = name.lastIndexOf('@'); + if (pos > 0) { + name = name.substring(pos + 1); + } + } + return name; + } + + public Table withName(String name) { + this.setName(name); + return this; + } + + @Override + public Alias getAlias() { + return alias; + } + + @Override + public void setAlias(Alias alias) { + this.alias = alias; + } + + private void setIndex(int idx, String value) { + int size = partItems.size(); + for (int i = 0; i < idx - size + 1; i++) { + partItems.add(null); + } + + if (value == null && idx == partItems.size() - 1) { + partItems.remove(idx); + } else { + partItems.set(idx, value); + } + } + + private String getIndex(int idx) { + if (idx < partItems.size()) { + return partItems.get(idx); + } else { + return null; + } + } + + @Override + public String getFullyQualifiedName() { + StringBuilder fqn = new StringBuilder(); + + for (int i = partItems.size() - 1; i >= 0; i--) { + String part = partItems.get(i); + if (part == null) { + part = ""; + } + fqn.append(part); + if (i != 0) { + fqn.append(partDelimiters.isEmpty() ? "." : partDelimiters.get(i - 1)); + } + } + + return fqn.toString(); + } + + @Override + public T accept(FromItemVisitor fromItemVisitor, S context) { + return fromItemVisitor.visit(this, context); + } + + public T accept(IntoTableVisitor intoTableVisitor, S context) { + return intoTableVisitor.visit(this, context); + } + + @Override + public Pivot getPivot() { + return pivot; + } + + @Override + public void setPivot(Pivot pivot) { + this.pivot = pivot; + } + + @Override + public UnPivot getUnPivot() { + return this.unpivot; + } + + @Override + public void setUnPivot(UnPivot unpivot) { + this.unpivot = unpivot; + } + + public MySQLIndexHint getIndexHint() { + return mysqlHints; + } + + public Table withHint(MySQLIndexHint hint) { + setHint(hint); + return this; + } + + public void setHint(MySQLIndexHint hint) { + this.mysqlHints = hint; + } + + public SQLServerHints getSqlServerHints() { + return sqlServerHints; + } + + public void setSqlServerHints(SQLServerHints sqlServerHints) { + this.sqlServerHints = sqlServerHints; + } + + public SampleClause getSampleClause() { + return sampleClause; + } + + public Table setSampleClause(SampleClause sampleClause) { + this.sampleClause = sampleClause; + return this; + } + + public StringBuilder appendTo(StringBuilder builder) { + builder.append(getFullyQualifiedName()); + if (alias != null) { + builder.append(alias); + } + + if (sampleClause != null) { + sampleClause.appendTo(builder); + } + + if (pivot != null) { + builder.append(" ").append(pivot); + } + + if (unpivot != null) { + builder.append(" ").append(unpivot); + } + + if (mysqlHints != null) { + builder.append(mysqlHints); + } + + if (sqlServerHints != null) { + builder.append(sqlServerHints); + } + return builder; + } + + @Override + public String toString() { + return appendTo(new StringBuilder()).toString(); + } + + @Override + public Table withUnPivot(UnPivot unpivot) { + return (Table) FromItem.super.withUnPivot(unpivot); + } + + @Override + public Table withAlias(Alias alias) { + return (Table) FromItem.super.withAlias(alias); + } + + @Override + public Table withPivot(Pivot pivot) { + return (Table) FromItem.super.withPivot(pivot); + } + + public Table withSqlServerHints(SQLServerHints sqlServerHints) { + this.setSqlServerHints(sqlServerHints); + return this; + } + + public List getNameParts() { + return partItems; + } + + public List getNamePartDelimiters() { + return partDelimiters; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/Block.java b/src/main/java/net/sf/jsqlparser/statement/Block.java new file mode 100644 index 0000000..83b9a23 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/Block.java @@ -0,0 +1,59 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement; + +public class Block implements Statement { + private boolean hasSemicolonAfterEnd = false; + + private Statements statements; + + public Statements getStatements() { + return statements; + } + + public void setStatements(Statements statements) { + this.statements = statements; + } + + public boolean hasSemicolonAfterEnd() { + return hasSemicolonAfterEnd; + } + + public void setSemicolonAfterEnd(boolean hasSemicolonAfterEnd) { + this.hasSemicolonAfterEnd = hasSemicolonAfterEnd; + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + public StringBuilder appendTo(StringBuilder builder) { + builder.append("BEGIN\n"); + if (statements != null) { + builder.append(statements); + } + builder.append("END"); + if (hasSemicolonAfterEnd) { + builder.append(";"); + } + return builder; + } + + @Override + public String toString() { + return appendTo(new StringBuilder()).toString(); + } + + public Block withStatements(Statements statements) { + this.setStatements(statements); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/Commit.java b/src/main/java/net/sf/jsqlparser/statement/Commit.java new file mode 100644 index 0000000..7ce4650 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/Commit.java @@ -0,0 +1,22 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement; + +public class Commit implements Statement { + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + @Override + public String toString() { + return "COMMIT"; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/CreateFunctionalStatement.java b/src/main/java/net/sf/jsqlparser/statement/CreateFunctionalStatement.java new file mode 100644 index 0000000..536a2b6 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/CreateFunctionalStatement.java @@ -0,0 +1,120 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +/** + * A base for the declaration of function like statements + */ +public abstract class CreateFunctionalStatement implements Statement { + + private String kind; + private boolean orReplace = false; + + private List functionDeclarationParts; + + protected CreateFunctionalStatement(String kind) { + this.kind = kind; + } + + protected CreateFunctionalStatement(String kind, List functionDeclarationParts) { + this(false, kind, functionDeclarationParts); + } + + protected CreateFunctionalStatement(boolean orReplace, String kind, + List functionDeclarationParts) { + this.orReplace = orReplace; + this.kind = kind; + this.functionDeclarationParts = functionDeclarationParts; + } + + /** + * @return the declaration parts after {@code CREATE FUNCTION|PROCEDURE} + */ + public List getFunctionDeclarationParts() { + return functionDeclarationParts; + } + + public void setFunctionDeclarationParts(List functionDeclarationParts) { + this.functionDeclarationParts = functionDeclarationParts; + } + + /** + * @return the kind of functional statement + */ + public String getKind() { + return kind; + } + + public void setOrReplace(boolean orReplace) { + this.orReplace = orReplace; + } + + /** + * @return a whitespace appended String with the declaration parts with some minimal formatting. + */ + public String formatDeclaration() { + StringBuilder declaration = new StringBuilder(); + int currIndex = 0; + while (currIndex < functionDeclarationParts.size()) { + String token = functionDeclarationParts.get(currIndex); + declaration.append(token); + // if the next token is a ; don't put a space + if (currIndex + 1 < functionDeclarationParts.size()) { + // peek ahead just to format nicely + String nextToken = functionDeclarationParts.get(currIndex + 1); + if (!nextToken.equals(";")) { + declaration.append(" "); + } + } + currIndex++; + } + return declaration.toString(); + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + @Override + public String toString() { + return "CREATE " + + (orReplace ? "OR REPLACE " : "") + + kind + " " + formatDeclaration(); + } + + public CreateFunctionalStatement withFunctionDeclarationParts( + List functionDeclarationParts) { + this.setFunctionDeclarationParts(functionDeclarationParts); + return this; + } + + public CreateFunctionalStatement addFunctionDeclarationParts( + String... functionDeclarationParts) { + List collection = + Optional.ofNullable(getFunctionDeclarationParts()).orElseGet(ArrayList::new); + Collections.addAll(collection, functionDeclarationParts); + return this.withFunctionDeclarationParts(collection); + } + + public CreateFunctionalStatement addFunctionDeclarationParts( + Collection functionDeclarationParts) { + List collection = + Optional.ofNullable(getFunctionDeclarationParts()).orElseGet(ArrayList::new); + collection.addAll(functionDeclarationParts); + return this.withFunctionDeclarationParts(collection); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/DeclareStatement.java b/src/main/java/net/sf/jsqlparser/statement/DeclareStatement.java new file mode 100644 index 0000000..d015dc5 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/DeclareStatement.java @@ -0,0 +1,217 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.UserVariable; +import net.sf.jsqlparser.statement.create.table.ColDataType; +import net.sf.jsqlparser.statement.create.table.ColumnDefinition; + +public final class DeclareStatement implements Statement { + + private UserVariable userVariable = null; + private DeclareType declareType = DeclareType.TYPE; + private String typeName; + private List typeDefExprList = new ArrayList<>(); + private List columnDefinitions = new ArrayList<>(); + + public DeclareStatement() {} + + public UserVariable getUserVariable() { + return userVariable; + } + + public void setUserVariable(UserVariable userVariable) { + this.userVariable = userVariable; + } + + /** + * @return the {@link DeclareType} + * @deprecated use {@link #getDeclareType()} + */ + @Deprecated + public DeclareType getType() { + return getDeclareType(); + } + + /** + * @return the {@link DeclareType} + */ + public DeclareType getDeclareType() { + return declareType; + } + + public void setDeclareType(DeclareType declareType) { + this.declareType = declareType; + } + + public String getTypeName() { + return typeName; + } + + public void setTypeName(String typeName) { + this.typeName = typeName; + } + + public void addType(ColDataType colDataType, Expression defaultExpr) { + addTypeDefExprList(new TypeDefExpr(colDataType, defaultExpr)); + } + + public void addType(UserVariable userVariable, ColDataType colDataType, + Expression defaultExpr) { + addTypeDefExprList(new TypeDefExpr(userVariable, colDataType, defaultExpr)); + } + + public DeclareStatement addTypeDefExprList(TypeDefExpr... typeDefExpressions) { + List collection = + Optional.ofNullable(getTypeDefExprList()).orElseGet(ArrayList::new); + Collections.addAll(collection, typeDefExpressions); + return this.withTypeDefExprList(collection); + } + + public DeclareStatement addTypeDefExprList( + Collection typeDefExpressions) { + List collection = + Optional.ofNullable(getTypeDefExprList()).orElseGet(ArrayList::new); + collection.addAll(typeDefExpressions); + return this.withTypeDefExprList(collection); + } + + public DeclareStatement withTypeDefExprList(List typeDefExpressions) { + setTypeDefExprList(typeDefExpressions); + return this; + } + + public List getTypeDefExprList() { + return this.typeDefExprList; + } + + public void setTypeDefExprList(List expr) { + this.typeDefExprList = expr; + } + + public void addColumnDefinition(ColumnDefinition colDef) { + columnDefinitions.add(colDef); + } + + public List getColumnDefinitions() { + return columnDefinitions; + } + + public void setColumnDefinitions(List columnDefinitions) { + this.columnDefinitions = columnDefinitions; + } + + public List getTypeDefinitions() { + return typeDefExprList; + } + + @Override + public String toString() { + StringBuilder b = new StringBuilder("DECLARE "); + if (declareType == DeclareType.AS) { + b.append(userVariable.toString()); + b.append(" AS ").append(typeName); + } else { + if (declareType == DeclareType.TABLE) { + b.append(userVariable.toString()); + b.append(" TABLE ("); + for (int i = 0; i < columnDefinitions.size(); i++) { + if (i > 0) { + b.append(", "); + } + b.append(columnDefinitions.get(i).toString()); + } + b.append(")"); + } else { + for (int i = 0; i < typeDefExprList.size(); i++) { + if (i > 0) { + b.append(", "); + } + final TypeDefExpr type = typeDefExprList.get(i); + if (type.userVariable != null) { + b.append(type.userVariable.toString()).append(" "); + } + b.append(type.colDataType.toString()); + if (type.defaultExpr != null) { + b.append(" = ").append(type.defaultExpr.toString()); + } + } + } + } + return b.toString(); + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + public DeclareStatement withUserVariable(UserVariable userVariable) { + this.setUserVariable(userVariable); + return this; + } + + public DeclareStatement withTypeName(String typeName) { + this.setTypeName(typeName); + return this; + } + + public DeclareStatement withDeclareType(DeclareType declareType) { + this.setDeclareType(declareType); + return this; + } + + public DeclareStatement withColumnDefinitions(List columnDefinitions) { + this.setColumnDefinitions(columnDefinitions); + return this; + } + + public DeclareStatement addColumnDefinitions(ColumnDefinition... statements) { + List collection = + Optional.ofNullable(getColumnDefinitions()).orElseGet(ArrayList::new); + Collections.addAll(collection, statements); + return this.withColumnDefinitions(collection); + } + + + public DeclareStatement addColumnDefinitions( + Collection columnDefinitions) { + List collection = + Optional.ofNullable(getColumnDefinitions()).orElseGet(ArrayList::new); + collection.addAll(columnDefinitions); + return this.withColumnDefinitions(collection); + } + + public static class TypeDefExpr implements Serializable { + + public final UserVariable userVariable; + public final ColDataType colDataType; + public final Expression defaultExpr; + + public TypeDefExpr(ColDataType colDataType, Expression defaultExpr) { + this(null, colDataType, defaultExpr); + } + + public TypeDefExpr(UserVariable userVariable, ColDataType colDataType, + Expression defaultExpr) { + this.userVariable = userVariable; + this.colDataType = colDataType; + this.defaultExpr = defaultExpr; + } + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/DeclareType.java b/src/main/java/net/sf/jsqlparser/statement/DeclareType.java new file mode 100644 index 0000000..5208a33 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/DeclareType.java @@ -0,0 +1,21 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement; + +/** + * @author tobens + */ +public enum DeclareType { + TABLE, AS, TYPE; + + public static DeclareType from(String type) { + return Enum.valueOf(DeclareType.class, type.toUpperCase()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/DescribeStatement.java b/src/main/java/net/sf/jsqlparser/statement/DescribeStatement.java new file mode 100644 index 0000000..10a9276 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/DescribeStatement.java @@ -0,0 +1,58 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement; + +import net.sf.jsqlparser.schema.Table; + +public class DescribeStatement implements Statement { + + private Table table; + private String describeType; + + public DescribeStatement() { + // empty constructor + } + + public DescribeStatement(Table table) { + this.table = table; + } + + public Table getTable() { + return table; + } + + public void setTable(Table table) { + this.table = table; + } + + @Override + public String toString() { + return this.describeType + " " + table.getFullyQualifiedName(); + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + public DescribeStatement withTable(Table table) { + this.setTable(table); + return this; + } + + public String getDescribeType() { + return describeType; + } + + public DescribeStatement setDescribeType(String describeType) { + this.describeType = describeType; + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/ExplainStatement.java b/src/main/java/net/sf/jsqlparser/statement/ExplainStatement.java new file mode 100644 index 0000000..55e5c67 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/ExplainStatement.java @@ -0,0 +1,145 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement; + +import java.io.Serializable; +import java.util.LinkedHashMap; +import java.util.stream.Collectors; + +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.select.Select; + +/** + * An {@code EXPLAIN} statement + */ +public class ExplainStatement implements Statement { + + private Select select; + private LinkedHashMap options; + private Table table; + + public ExplainStatement() { + // empty constructor + } + + public ExplainStatement(Select select) { + this.select = select; + } + + public Table getTable() { + return table; + } + + public ExplainStatement setTable(Table table) { + this.table = table; + return this; + } + + public Select getStatement() { + return select; + } + + public void setStatement(Select select) { + this.select = select; + } + + public LinkedHashMap getOptions() { + return options == null ? null : new LinkedHashMap<>(options); + } + + public void addOption(Option option) { + if (options == null) { + options = new LinkedHashMap<>(); + } + + options.put(option.getType(), option); + } + + /** + * Returns the first option that matches this optionType + * + * @param optionType the option type to retrieve an Option for + * @return an option of that type, or null. In case of duplicate options, the first found option + * will be returned. + */ + public Option getOption(OptionType optionType) { + if (options == null) { + return null; + } + return options.get(optionType); + } + + @Override + public String toString() { + StringBuilder statementBuilder = new StringBuilder("EXPLAIN"); + if (table != null) { + statementBuilder.append(" ").append(table); + } else { + if (options != null) { + statementBuilder.append(" "); + statementBuilder.append(options.values().stream().map(Option::formatOption) + .collect(Collectors.joining(" "))); + } + + statementBuilder.append(" "); + if (select != null) { + statementBuilder.append(select.toString()); + } + } + + return statementBuilder.toString(); + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + public enum OptionType { + ANALYZE, VERBOSE, COSTS, BUFFERS, FORMAT; + + public static OptionType from(String type) { + return Enum.valueOf(OptionType.class, type.toUpperCase()); + } + } + + public static class Option implements Serializable { + + private final OptionType type; + private String value; + + public Option(OptionType type) { + this.type = type; + } + + public OptionType getType() { + return type; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String formatOption() { + return type.name() + (value != null + ? " " + value + : ""); + } + + public Option withValue(String value) { + this.setValue(value); + return this; + } + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/IfElseStatement.java b/src/main/java/net/sf/jsqlparser/statement/IfElseStatement.java new file mode 100644 index 0000000..848c886 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/IfElseStatement.java @@ -0,0 +1,88 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2021 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ + +package net.sf.jsqlparser.statement; + +import java.util.Objects; + +import net.sf.jsqlparser.expression.Expression; + +/** + * @author Andreas Reichel + */ +public class IfElseStatement implements Statement { + private final Expression condition; + private final Statement ifStatement; + private Statement elseStatement; + private boolean usingSemicolonForIfStatement = false; + private boolean usingSemicolonForElseStatement = false; + + public IfElseStatement(Expression condition, Statement ifStatement) { + this.condition = + Objects.requireNonNull(condition, + "The CONDITION of the IfElseStatement must not be null."); + this.ifStatement = Objects.requireNonNull(ifStatement, + "The IF Statement of the IfElseStatement must not be null."); + } + + public Expression getCondition() { + return condition; + } + + public Statement getIfStatement() { + return ifStatement; + } + + public Statement getElseStatement() { + return elseStatement; + } + + public void setElseStatement(Statement elseStatement) { + this.elseStatement = elseStatement; + } + + public boolean isUsingSemicolonForElseStatement() { + return usingSemicolonForElseStatement; + } + + public void setUsingSemicolonForElseStatement(boolean usingSemicolonForElseStatement) { + this.usingSemicolonForElseStatement = usingSemicolonForElseStatement; + } + + public boolean isUsingSemicolonForIfStatement() { + return usingSemicolonForIfStatement; + } + + public void setUsingSemicolonForIfStatement(boolean usingSemicolonForIfStatement) { + this.usingSemicolonForIfStatement = usingSemicolonForIfStatement; + } + + public StringBuilder appendTo(StringBuilder builder) { + builder.append("IF ").append(condition).append(" ").append(ifStatement) + .append(usingSemicolonForIfStatement ? ";" : ""); + + if (elseStatement != null) { + builder.append(" ELSE ").append(elseStatement) + .append(usingSemicolonForElseStatement ? ";" : ""); + } + return builder; + } + + @Override + public String toString() { + return appendTo(new StringBuilder()).toString(); + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/statement/OutputClause.java b/src/main/java/net/sf/jsqlparser/statement/OutputClause.java new file mode 100644 index 0000000..fe2fd60 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/OutputClause.java @@ -0,0 +1,109 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2022 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement; + +import net.sf.jsqlparser.expression.UserVariable; +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.select.PlainSelect; +import net.sf.jsqlparser.statement.select.SelectItem; + +import java.io.Serializable; +import java.util.List; +import java.util.Objects; + +/** + * T-SQL Output Clause + * + * @see OUTPUT + * Clause (Transact-SQL) + * + *
+ * <OUTPUT_CLAUSE> ::=
+ * {
+ *     [ OUTPUT <dml_select_list> INTO { @table_variable | output_table } [ ( column_list ) ] ]
+ *     [ OUTPUT <dml_select_list> ]
+ * }
+ * <dml_select_list> ::=
+ * { <column_name> | scalar_expression } [ [AS] column_alias_identifier ]
+ *     [ ,...n ]
+ *
+ * <column_name> ::=
+ * { DELETED | INSERTED | from_table_name } . { * | column_name }
+ *     | $action
+ *      
+ */ +public class OutputClause implements Serializable { + List> selectItemList; + UserVariable tableVariable; + Table outputTable; + List columnList; + + public OutputClause(List> selectItemList, UserVariable tableVariable, + Table outputTable, List columnList) { + this.selectItemList = Objects.requireNonNull(selectItemList, + "The Select List of the Output Clause must not be null."); + this.tableVariable = tableVariable; + this.outputTable = outputTable; + this.columnList = columnList; + } + + public List> getSelectItemList() { + return selectItemList; + } + + public void setSelectItemList(List> selectItemList) { + this.selectItemList = selectItemList; + } + + public UserVariable getTableVariable() { + return tableVariable; + } + + public void setTableVariable(UserVariable tableVariable) { + this.tableVariable = tableVariable; + } + + public Table getOutputTable() { + return outputTable; + } + + public void setOutputTable(Table outputTable) { + this.outputTable = outputTable; + } + + public List getColumnList() { + return columnList; + } + + public void setColumnList(List columnList) { + this.columnList = columnList; + } + + public StringBuilder appendTo(StringBuilder builder) { + builder.append(" OUTPUT "); + PlainSelect.appendStringListTo(builder, selectItemList, true, false); + + if (tableVariable != null) { + builder.append(" INTO ").append(tableVariable); + } else if (outputTable != null) { + builder.append(" INTO ").append(outputTable); + } + + PlainSelect.appendStringListTo(builder, columnList, true, false); + + return builder.append(" "); + } + + @Override + public String toString() { + return appendTo(new StringBuilder()).toString(); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/PurgeObjectType.java b/src/main/java/net/sf/jsqlparser/statement/PurgeObjectType.java new file mode 100644 index 0000000..4cb2755 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/PurgeObjectType.java @@ -0,0 +1,21 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2021 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement; + +/** + * @author Andreas Reichel + */ +public enum PurgeObjectType { + TABLE, INDEX, RECYCLEBIN, DBA_RECYCLEBIN, TABLESPACE; + + public static PurgeObjectType from(String type) { + return Enum.valueOf(PurgeObjectType.class, type.toUpperCase()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/PurgeStatement.java b/src/main/java/net/sf/jsqlparser/statement/PurgeStatement.java new file mode 100644 index 0000000..2dca9fb --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/PurgeStatement.java @@ -0,0 +1,108 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2021 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ + +package net.sf.jsqlparser.statement; + +import java.util.Objects; + +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.create.table.Index; + +/** + * @author Andreas Reichel + * @see Purge + */ + +public class PurgeStatement implements Statement { + private final PurgeObjectType purgeObjectType; + private final Object object; + private String userName; + + public PurgeStatement(Table table) { + this.purgeObjectType = PurgeObjectType.TABLE; + this.object = Objects.requireNonNull(table, + "The TABLE of the PURGE TABLE statement must not be null."); + } + + public PurgeStatement(Index index) { + this.purgeObjectType = PurgeObjectType.INDEX; + this.object = Objects.requireNonNull(index, + "The INDEX of the PURGE INDEX statement must not be null."); + } + + public PurgeStatement(PurgeObjectType purgeObjectType) { + this.purgeObjectType = purgeObjectType; + this.object = null; + } + + public PurgeStatement(PurgeObjectType purgeObjectType, String tableSpaceName, String userName) { + this.purgeObjectType = purgeObjectType; + this.object = Objects.requireNonNull(tableSpaceName, + "The TABLESPACE NAME of the PURGE TABLESPACE statement must not be null."); + this.userName = userName; + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + @SuppressWarnings({"PMD.MissingBreakInSwitch", "PMD.SwitchStmtsShouldHaveDefault", + "PMD.CyclomaticComplexity"}) + public StringBuilder appendTo(StringBuilder builder) { + builder.append("PURGE "); + + switch (purgeObjectType) { + case RECYCLEBIN: + case DBA_RECYCLEBIN: + builder.append(purgeObjectType); + break; + case TABLE: + case INDEX: + builder.append(purgeObjectType); + if (object != null) { + builder.append(" ").append(object); + } + break; + case TABLESPACE: + builder.append(purgeObjectType); + if (object != null) { + builder.append(" ").append(object); + } + if (userName != null && userName.length() > 0) { + builder.append(" USER ").append(userName); + } + break; + } + return builder; + } + + @Override + public String toString() { + return appendTo(new StringBuilder()).toString(); + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public PurgeObjectType getPurgeObjectType() { + return purgeObjectType; + } + + public Object getObject() { + return object; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/ReferentialAction.java b/src/main/java/net/sf/jsqlparser/statement/ReferentialAction.java new file mode 100644 index 0000000..5691aed --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/ReferentialAction.java @@ -0,0 +1,126 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement; + +import java.io.Serializable; + +public class ReferentialAction implements Serializable { + + private Type type; + private Action action; + + public ReferentialAction() { + // default constructor + } + + public ReferentialAction(Type type, Action action) { + this.type = type; + this.action = action; + } + + public Type getType() { + return type; + } + + public void setType(Type type) { + this.type = type; + } + + public ReferentialAction withType(Type type) { + setType(type); + return this; + } + + public Action getAction() { + return action; + } + + public void setAction(Action action) { + this.action = action; + } + + public ReferentialAction withAction(Action action) { + setAction(action); + return this; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((action == null) ? 0 : action.hashCode()); + result = prime * result + ((type == null) ? 0 : type.hashCode()); + return result; + } + + @Override + public String toString() { + return " ON " + getType().name() + " " + + getAction().getAction(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + ReferentialAction other = (ReferentialAction) obj; + // if (action != other.action) { + // return false; + // } + // if (type != other.type) { + // return false; + return action == other.action && type == other.type; + } + + public enum Type { + DELETE, UPDATE; + + public static Type from(String name) { + return Enum.valueOf(Type.class, name.toUpperCase()); + } + } + + public enum Action { + CASCADE("CASCADE"), RESTRICT("RESTRICT"), NO_ACTION("NO ACTION"), SET_DEFAULT( + "SET DEFAULT"), SET_NULL("SET NULL"); + + private final String action; + + Action(String action) { + this.action = action; + } + + /** + * @param action + * @return the {@link Action}, if found, otherwise null + */ + public static Action from(String action) { + // We can't use Enum.valueOf() since there White Space involved + for (Action a : values()) { + if (a.getAction().equals(action)) { + return a; + } + } + return null; + } + + public String getAction() { + return action; + } + } + +} diff --git a/src/main/java/net/sf/jsqlparser/statement/ResetStatement.java b/src/main/java/net/sf/jsqlparser/statement/ResetStatement.java new file mode 100644 index 0000000..dc769a9 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/ResetStatement.java @@ -0,0 +1,48 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement; + + +public final class ResetStatement implements Statement { + + private String name = ""; + + public ResetStatement() { + // empty constructor + } + + public ResetStatement(String name) { + add(name); + } + + public void add(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + StringBuilder b = new StringBuilder("RESET ").append(name); + return b.toString(); + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/statement/ReturningClause.java b/src/main/java/net/sf/jsqlparser/statement/ReturningClause.java new file mode 100644 index 0000000..c84a079 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/ReturningClause.java @@ -0,0 +1,96 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2023 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement; + +import net.sf.jsqlparser.statement.select.SelectItem; + +import java.util.ArrayList; +import java.util.List; + +/** + * RETURNING clause according to Part of UPDATE, INSERT, DELETE statements + */ + +public class ReturningClause extends ArrayList> { + /** + * List of output targets like Table or UserVariable + */ + private final List dataItems; + private Keyword keyword; + + public ReturningClause(Keyword keyword, List> selectItems, + List dataItems) { + this.keyword = keyword; + this.addAll(selectItems); + this.dataItems = dataItems; + } + + public ReturningClause(String keyword, List> selectItems, + List dataItems) { + this(Keyword.from(keyword), selectItems, dataItems); + } + + public ReturningClause(Keyword keyword, List> selectItems) { + this(keyword, selectItems, null); + } + + public ReturningClause(String keyword, List> selectItems) { + this(Keyword.valueOf(keyword), selectItems, null); + } + + public Keyword getKeyword() { + return keyword; + } + + public ReturningClause setKeyword(Keyword keyword) { + this.keyword = keyword; + return this; + } + + public List getDataItems() { + return dataItems; + } + + public StringBuilder appendTo(StringBuilder builder) { + builder.append(" ").append(keyword).append(" "); + for (int i = 0; i < size(); i++) { + if (i > 0) { + builder.append(", "); + } + builder.append(get(i)); + } + + if (dataItems != null && !dataItems.isEmpty()) { + builder.append(" INTO "); + for (int i = 0; i < dataItems.size(); i++) { + if (i > 0) { + builder.append(" ,"); + } + builder.append(dataItems.get(i)); + } + } + return builder; + } + + @Override + public String toString() { + return appendTo(new StringBuilder()).toString(); + } + + public enum Keyword { + RETURN, RETURNING; + + public static Keyword from(String keyword) { + return Enum.valueOf(Keyword.class, keyword.toUpperCase()); + } + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/RollbackStatement.java b/src/main/java/net/sf/jsqlparser/statement/RollbackStatement.java new file mode 100644 index 0000000..0c6f667 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/RollbackStatement.java @@ -0,0 +1,114 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2021 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +/* + * Copyright (C) 2021 JSQLParser. + * + * This library is free software; you can redistribute it and/or modify it under the terms of the + * GNU Lesser General Public License as published by the Free Software Foundation; either version + * 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with this library; + * if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +package net.sf.jsqlparser.statement; + +/** + * @author are + */ +public class RollbackStatement implements Statement { + private boolean usingWorkKeyword = false; + private boolean usingSavepointKeyword = false; + private String savepointName = null; + private String forceDistributedTransactionIdentifier = null; + + public boolean isUsingWorkKeyword() { + return usingWorkKeyword; + } + + public void setUsingWorkKeyword(boolean usingWorkKeyword) { + this.usingWorkKeyword = usingWorkKeyword; + } + + public RollbackStatement withUsingWorkKeyword(boolean usingWorkKeyword) { + this.usingWorkKeyword = usingWorkKeyword; + return this; + } + + public boolean isUsingSavepointKeyword() { + return usingSavepointKeyword; + } + + public void setUsingSavepointKeyword(boolean usingSavepointKeyword) { + this.usingSavepointKeyword = usingSavepointKeyword; + } + + public RollbackStatement withUsingSavepointKeyword(boolean usingSavepointKeyword) { + this.usingSavepointKeyword = usingSavepointKeyword; + return this; + } + + public String getSavepointName() { + return savepointName; + } + + public void setSavepointName(String savepointName) { + this.savepointName = savepointName; + } + + public RollbackStatement withSavepointName(String savepointName) { + this.savepointName = savepointName; + return this; + } + + public String getForceDistributedTransactionIdentifier() { + return forceDistributedTransactionIdentifier; + } + + public void setForceDistributedTransactionIdentifier( + String forceDistributedTransactionIdentifier) { + this.forceDistributedTransactionIdentifier = forceDistributedTransactionIdentifier; + } + + public RollbackStatement withForceDistributedTransactionIdentifier( + String forceDistributedTransactionIdentifier) { + this.forceDistributedTransactionIdentifier = forceDistributedTransactionIdentifier; + return this; + } + + @Override + public String toString() { + return "ROLLBACK " + + (usingWorkKeyword + ? "WORK " + : "") + + (savepointName != null && !savepointName.trim().isEmpty() + ? "TO " + (usingSavepointKeyword + ? "SAVEPOINT " + : "") + savepointName + : forceDistributedTransactionIdentifier != null + && !forceDistributedTransactionIdentifier.trim().isEmpty() + ? "FORCE " + forceDistributedTransactionIdentifier + : "" + + ); + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/statement/SavepointStatement.java b/src/main/java/net/sf/jsqlparser/statement/SavepointStatement.java new file mode 100644 index 0000000..390c7f4 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/SavepointStatement.java @@ -0,0 +1,44 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2021 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ + +package net.sf.jsqlparser.statement; + +import java.util.Objects; + +/** + * @author Andreas Reichel + */ +public class SavepointStatement implements Statement { + private String savepointName; + + public SavepointStatement(String savepointName) { + this.savepointName = + Objects.requireNonNull(savepointName, "The Savepoint Name must not be NULL."); + } + + public String getSavepointName() { + return savepointName; + } + + public void setSavepointName(String savepointName) { + this.savepointName = + Objects.requireNonNull(savepointName, "The Savepoint Name must not be NULL."); + } + + @Override + public String toString() { + return "SAVEPOINT " + savepointName; + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/SetStatement.java b/src/main/java/net/sf/jsqlparser/statement/SetStatement.java new file mode 100644 index 0000000..16878f7 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/SetStatement.java @@ -0,0 +1,200 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.statement.select.PlainSelect; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +public final class SetStatement implements Statement { + + private final List values = new ArrayList<>(); + private String effectParameter; + + public SetStatement() { + // empty constructor + } + + public SetStatement(Object name, ExpressionList value) { + add(name, value, true); + } + + public void add(Object name, ExpressionList value, boolean useEqual) { + values.add(new NameExpr(name, value, useEqual)); + } + + public void remove(int idx) { + values.remove(idx); + } + + public int getCount() { + return values.size(); + } + + public boolean isUseEqual(int idx) { + return values.get(idx).useEqual; + } + + public boolean isUseEqual() { + return isUseEqual(0); + } + + public SetStatement setUseEqual(boolean useEqual) { + return setUseEqual(0, useEqual); + } + + public SetStatement withUseEqual(int idx, boolean useEqual) { + this.setUseEqual(idx, useEqual); + return this; + } + + public SetStatement setUseEqual(int idx, boolean useEqual) { + values.get(idx).useEqual = useEqual; + return this; + } + + public SetStatement withUseEqual(boolean useEqual) { + this.setUseEqual(useEqual); + return this; + } + + public Object getName() { + return getName(0); + } + + public void setName(String name) { + setName(0, name); + } + + public Object getName(int idx) { + return values.get(idx).name; + } + + public void setName(int idx, String name) { + values.get(idx).name = name; + } + + public List getExpressions(int idx) { + return values.get(idx).expressions; + } + + public List getExpressions() { + return getExpressions(0); + } + + public void setExpressions(ExpressionList expressions) { + setExpressions(0, expressions); + } + + public void setExpressions(int idx, ExpressionList expressions) { + values.get(idx).expressions = expressions; + } + + private String toString(NameExpr ne) { + return ne.name + (ne.useEqual ? " = " : " ") + + PlainSelect.getStringList(ne.expressions, true, false); + } + + @Override + public String toString() { + StringBuilder b = new StringBuilder("SET "); + if (effectParameter != null) { + b.append(effectParameter).append(" "); + } + boolean addComma = false; + for (NameExpr ne : values) { + if (addComma) { + b.append(", "); + } else { + addComma = true; + } + b.append(toString(ne)); + } + + return b.toString(); + } + + public List getKeyValuePairs() { + return values; + } + + public void addKeyValuePairs(Collection keyValuePairs) { + values.addAll(keyValuePairs); + } + + public void addKeyValuePairs(NameExpr... keyValuePairs) { + addKeyValuePairs(Arrays.asList(keyValuePairs)); + } + + public void clear() { + values.clear(); + effectParameter = null; + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + public String getEffectParameter() { + return effectParameter; + } + + public void setEffectParameter(String effectParameter) { + this.effectParameter = effectParameter; + } + + public SetStatement withEffectParameter(String effectParameter) { + this.effectParameter = effectParameter; + return this; + } + + static class NameExpr implements Serializable { + Object name; + ExpressionList expressions; + boolean useEqual; + + public NameExpr(Object name, ExpressionList expressions, boolean useEqual) { + this.name = name; + this.expressions = expressions; + this.useEqual = useEqual; + } + + public Object getName() { + return name; + } + + public void setName(Object name) { + this.name = name; + } + + public ExpressionList getExpressions() { + return expressions; + } + + public void setExpressions(ExpressionList expressions) { + this.expressions = expressions; + } + + public boolean isUseEqual() { + return useEqual; + } + + public void setUseEqual(boolean useEqual) { + this.useEqual = useEqual; + } + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/ShowColumnsStatement.java b/src/main/java/net/sf/jsqlparser/statement/ShowColumnsStatement.java new file mode 100644 index 0000000..51f1ed2 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/ShowColumnsStatement.java @@ -0,0 +1,46 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement; + +public class ShowColumnsStatement implements Statement { + + private String tableName; + + public ShowColumnsStatement() { + // empty constructor + } + + public ShowColumnsStatement(String tableName) { + this.tableName = tableName; + } + + public String getTableName() { + return tableName; + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + + @Override + public String toString() { + return "SHOW COLUMNS FROM " + tableName; + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + public ShowColumnsStatement withTableName(String tableName) { + this.setTableName(tableName); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/ShowStatement.java b/src/main/java/net/sf/jsqlparser/statement/ShowStatement.java new file mode 100644 index 0000000..c5f1729 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/ShowStatement.java @@ -0,0 +1,46 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement; + +public class ShowStatement implements Statement { + + private String name; + + public ShowStatement() { + // empty constructor + } + + public ShowStatement(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return "SHOW " + name; + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + public ShowStatement withName(String name) { + this.setName(name); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/Statement.java b/src/main/java/net/sf/jsqlparser/statement/Statement.java new file mode 100644 index 0000000..380092a --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/Statement.java @@ -0,0 +1,20 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement; + +import net.sf.jsqlparser.Model; + +public interface Statement extends Model { + T accept(StatementVisitor statementVisitor, S context); + + default void accept(StatementVisitor statementVisitor) { + accept(statementVisitor, null); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java b/src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java new file mode 100644 index 0000000..8625f65 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java @@ -0,0 +1,305 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement; + +import net.sf.jsqlparser.statement.alter.Alter; +import net.sf.jsqlparser.statement.alter.AlterSession; +import net.sf.jsqlparser.statement.alter.AlterSystemStatement; +import net.sf.jsqlparser.statement.alter.RenameTableStatement; +import net.sf.jsqlparser.statement.alter.sequence.AlterSequence; +import net.sf.jsqlparser.statement.analyze.Analyze; +import net.sf.jsqlparser.statement.comment.Comment; +import net.sf.jsqlparser.statement.create.index.CreateIndex; +import net.sf.jsqlparser.statement.create.schema.CreateSchema; +import net.sf.jsqlparser.statement.create.sequence.CreateSequence; +import net.sf.jsqlparser.statement.create.synonym.CreateSynonym; +import net.sf.jsqlparser.statement.create.table.CreateTable; +import net.sf.jsqlparser.statement.create.view.AlterView; +import net.sf.jsqlparser.statement.create.view.CreateView; +import net.sf.jsqlparser.statement.delete.Delete; +import net.sf.jsqlparser.statement.drop.Drop; +import net.sf.jsqlparser.statement.execute.Execute; +import net.sf.jsqlparser.statement.grant.Grant; +import net.sf.jsqlparser.statement.insert.Insert; +import net.sf.jsqlparser.statement.merge.Merge; +import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement; +import net.sf.jsqlparser.statement.select.Select; +import net.sf.jsqlparser.statement.show.ShowIndexStatement; +import net.sf.jsqlparser.statement.show.ShowTablesStatement; +import net.sf.jsqlparser.statement.truncate.Truncate; +import net.sf.jsqlparser.statement.update.Update; +import net.sf.jsqlparser.statement.upsert.Upsert; + +public interface StatementVisitor { + + T visit(Analyze analyze, S context); + + default void visit(Analyze analyze) { + this.visit(analyze, null); + } + + T visit(SavepointStatement savepointStatement, S context); + + default void visit(SavepointStatement savepointStatement) { + this.visit(savepointStatement, null); + } + + T visit(RollbackStatement rollbackStatement, S context); + + default void visit(RollbackStatement rollbackStatement) { + this.visit(rollbackStatement, null); + } + + T visit(Comment comment, S context); + + default void visit(Comment comment) { + this.visit(comment, null); + } + + T visit(Commit commit, S context); + + default void visit(Commit commit) { + this.visit(commit, null); + } + + T visit(Delete delete, S context); + + default void visit(Delete delete) { + this.visit(delete, null); + } + + T visit(Update update, S context); + + default void visit(Update update) { + this.visit(update, null); + } + + T visit(Insert insert, S context); + + default void visit(Insert insert) { + this.visit(insert, null); + } + + T visit(Drop drop, S context); + + default void visit(Drop drop) { + this.visit(drop, null); + } + + T visit(Truncate truncate, S context); + + default void visit(Truncate truncate) { + this.visit(truncate, null); + } + + T visit(CreateIndex createIndex, S context); + + default void visit(CreateIndex createIndex) { + this.visit(createIndex, null); + } + + T visit(CreateSchema createSchema, S context); + + default void visit(CreateSchema createSchema) { + this.visit(createSchema, null); + } + + T visit(CreateTable createTable, S context); + + default void visit(CreateTable createTable) { + this.visit(createTable, null); + } + + T visit(CreateView createView, S context); + + default void visit(CreateView createView) { + this.visit(createView, null); + } + + T visit(AlterView alterView, S context); + + default void visit(AlterView alterView) { + this.visit(alterView, null); + } + + T visit(RefreshMaterializedViewStatement materializedView, S context); + + default void visit(RefreshMaterializedViewStatement materializedView) { + this.visit(materializedView, null); + } + + T visit(Alter alter, S context); + + default void visit(Alter alter) { + this.visit(alter, null); + } + + T visit(Statements statements, S context); + + default void visit(Statements statements) { + this.visit(statements, null); + } + + T visit(Execute execute, S context); + + default void visit(Execute execute) { + this.visit(execute, null); + } + + T visit(SetStatement set, S context); + + default void visit(SetStatement set) { + this.visit(set, null); + } + + T visit(ResetStatement reset, S context); + + default void visit(ResetStatement reset) { + this.visit(reset, null); + } + + T visit(ShowColumnsStatement showColumns, S context); + + default void visit(ShowColumnsStatement showColumns) { + this.visit(showColumns, null); + } + + T visit(ShowIndexStatement showIndex, S context); + + default void visit(ShowIndexStatement showIndex) { + this.visit(showIndex, null); + } + + T visit(ShowTablesStatement showTables, S context); + + default void visit(ShowTablesStatement showTables) { + this.visit(showTables, null); + } + + T visit(Merge merge, S context); + + default void visit(Merge merge) { + this.visit(merge, null); + } + + T visit(Select select, S context); + + default void visit(Select select) { + this.visit(select, null); + } + + T visit(Upsert upsert, S context); + + default void visit(Upsert upsert) { + this.visit(upsert, null); + } + + T visit(UseStatement use, S context); + + default void visit(UseStatement use) { + this.visit(use, null); + } + + T visit(Block block, S context); + + default void visit(Block block) { + this.visit(block, null); + } + + T visit(DescribeStatement describe, S context); + + default void visit(DescribeStatement describe) { + this.visit(describe, null); + } + + T visit(ExplainStatement explainStatement, S context); + + default void visit(ExplainStatement explainStatement) { + this.visit(explainStatement, null); + } + + T visit(ShowStatement showStatement, S context); + + default void visit(ShowStatement showStatement) { + this.visit(showStatement, null); + } + + T visit(DeclareStatement declareStatement, S context); + + default void visit(DeclareStatement declareStatement) { + this.visit(declareStatement, null); + } + + T visit(Grant grant, S context); + + default void visit(Grant grant) { + this.visit(grant, null); + } + + T visit(CreateSequence createSequence, S context); + + default void visit(CreateSequence createSequence) { + this.visit(createSequence, null); + } + + T visit(AlterSequence alterSequence, S context); + + default void visit(AlterSequence alterSequence) { + this.visit(alterSequence, null); + } + + T visit(CreateFunctionalStatement createFunctionalStatement, S context); + + default void visit(CreateFunctionalStatement createFunctionalStatement) { + this.visit(createFunctionalStatement, null); + } + + T visit(CreateSynonym createSynonym, S context); + + default void visit(CreateSynonym createSynonym) { + this.visit(createSynonym, null); + } + + T visit(AlterSession alterSession, S context); + + default void visit(AlterSession alterSession) { + this.visit(alterSession, null); + } + + T visit(IfElseStatement ifElseStatement, S context); + + default void visit(IfElseStatement ifElseStatement) { + this.visit(ifElseStatement, null); + } + + T visit(RenameTableStatement renameTableStatement, S context); + + default void visit(RenameTableStatement renameTableStatement) { + this.visit(renameTableStatement, null); + } + + T visit(PurgeStatement purgeStatement, S context); + + default void visit(PurgeStatement purgeStatement) { + this.visit(purgeStatement, null); + } + + T visit(AlterSystemStatement alterSystemStatement, S context); + + default void visit(AlterSystemStatement alterSystemStatement) { + this.visit(alterSystemStatement, null); + } + + T visit(UnsupportedStatement unsupportedStatement, S context); + + default void visit(UnsupportedStatement unsupportedStatement) { + this.visit(unsupportedStatement, null); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java new file mode 100644 index 0000000..5ee05b8 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java @@ -0,0 +1,290 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement; + +import net.sf.jsqlparser.statement.alter.Alter; +import net.sf.jsqlparser.statement.alter.AlterSession; +import net.sf.jsqlparser.statement.alter.AlterSystemStatement; +import net.sf.jsqlparser.statement.alter.RenameTableStatement; +import net.sf.jsqlparser.statement.alter.sequence.AlterSequence; +import net.sf.jsqlparser.statement.analyze.Analyze; +import net.sf.jsqlparser.statement.comment.Comment; +import net.sf.jsqlparser.statement.create.index.CreateIndex; +import net.sf.jsqlparser.statement.create.schema.CreateSchema; +import net.sf.jsqlparser.statement.create.sequence.CreateSequence; +import net.sf.jsqlparser.statement.create.synonym.CreateSynonym; +import net.sf.jsqlparser.statement.create.table.CreateTable; +import net.sf.jsqlparser.statement.create.view.AlterView; +import net.sf.jsqlparser.statement.create.view.CreateView; +import net.sf.jsqlparser.statement.delete.Delete; +import net.sf.jsqlparser.statement.drop.Drop; +import net.sf.jsqlparser.statement.execute.Execute; +import net.sf.jsqlparser.statement.grant.Grant; +import net.sf.jsqlparser.statement.insert.Insert; +import net.sf.jsqlparser.statement.merge.Merge; +import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement; +import net.sf.jsqlparser.statement.select.Select; +import net.sf.jsqlparser.statement.show.ShowIndexStatement; +import net.sf.jsqlparser.statement.show.ShowTablesStatement; +import net.sf.jsqlparser.statement.truncate.Truncate; +import net.sf.jsqlparser.statement.update.Update; +import net.sf.jsqlparser.statement.upsert.Upsert; + +@SuppressWarnings({"PMD.UncommentedEmptyMethodBody"}) +public class StatementVisitorAdapter implements StatementVisitor { + + @Override + public T visit(Comment comment, S context) { + + return null; + } + + @Override + public T visit(Commit commit, S context) { + + return null; + } + + @Override + public T visit(Select select, S context) { + + return null; + } + + @Override + public T visit(Delete delete, S context) { + + return null; + } + + @Override + public T visit(Update update, S context) { + + return null; + } + + @Override + public T visit(Insert insert, S context) { + + return null; + } + + @Override + public T visit(Drop drop, S context) { + + return null; + } + + @Override + public T visit(Truncate truncate, S context) { + + return null; + } + + @Override + public T visit(CreateIndex createIndex, S context) { + + return null; + } + + @Override + public T visit(CreateSchema createSchema, S context) { + return null; + } + + @Override + public T visit(CreateTable createTable, S context) { + + return null; + } + + @Override + public T visit(CreateView createView, S context) { + + return null; + } + + @Override + public T visit(Alter alter, S context) { + + return null; + } + + @Override + public T visit(Statements statements, S context) { + for (Statement statement : statements) { + statement.accept(this, context); + } + return null; + } + + @Override + public T visit(Execute execute, S context) { + + return null; + } + + @Override + public T visit(SetStatement set, S context) { + + return null; + } + + @Override + public T visit(ResetStatement reset, S context) { + + return null; + } + + @Override + public T visit(Merge merge, S context) { + + return null; + } + + @Override + public T visit(AlterView alterView, S context) { + return null; + } + + @Override + public T visit(Upsert upsert, S context) { + return null; + } + + @Override + public T visit(UseStatement use, S context) { + return null; + } + + @Override + public T visit(Block block, S context) { + return null; + } + + @Override + public T visit(DescribeStatement describe, S context) { + return null; + } + + @Override + public T visit(ExplainStatement explainStatement, S context) { + return null; + } + + @Override + public T visit(ShowStatement showStatement, S context) { + return null; + } + + @Override + public T visit(ShowColumnsStatement showColumnsStatement, S context) { + return null; + } + + @Override + public T visit(ShowIndexStatement showIndexStatement, S context) { + return null; + } + + @Override + public T visit(ShowTablesStatement showTables, S context) { + return null; + } + + @Override + public T visit(DeclareStatement declareStatement, S context) { + return null; + } + + @Override + public T visit(Grant grant, S context) { + return null; + } + + @Override + public T visit(CreateSequence createSequence, S context) { + return null; + } + + @Override + public T visit(AlterSequence alterSequence, S context) { + return null; + } + + @Override + public T visit(CreateFunctionalStatement createFunctionalStatement, S context) { + return null; + } + + @Override + public T visit(CreateSynonym createSynonym, S context) { + return null; + } + + @Override + public T visit(Analyze analyze, S context) { + + return null; + } + + @Override + public T visit(SavepointStatement savepointStatement, S context) { + // @todo: do something usefully here + return null; + } + + @Override + public T visit(RollbackStatement rollbackStatement, S context) { + // @todo: do something usefully here + return null; + } + + @Override + public T visit(AlterSession alterSession, S context) { + // @todo: do something usefully here + return null; + } + + @Override + public T visit(IfElseStatement ifElseStatement, S context) { + ifElseStatement.getIfStatement().accept(this, context); + if (ifElseStatement.getElseStatement() != null) { + ifElseStatement.getElseStatement().accept(this, context); + } + return null; + } + + @Override + public T visit(RenameTableStatement renameTableStatement, S context) { + return null; + } + + @Override + public T visit(PurgeStatement purgeStatement, S context) { + return null; + } + + @Override + public T visit(AlterSystemStatement alterSystemStatement, S context) { + return null; + } + + @Override + public T visit(UnsupportedStatement unsupportedStatement, S context) { + + return null; + } + + @Override + public T visit(RefreshMaterializedViewStatement materializedView, S context) { + return null; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/Statements.java b/src/main/java/net/sf/jsqlparser/statement/Statements.java new file mode 100644 index 0000000..0c8571c --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/Statements.java @@ -0,0 +1,50 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +public class Statements extends ArrayList implements Serializable { + + @Deprecated + public List getStatements() { + return this; + } + + @Deprecated + public void setStatements(List statements) { + this.clear(); + this.addAll(statements); + } + + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + public E get(Class type, int index) { + return type.cast(get(index)); + } + + @Override + public String toString() { + StringBuilder b = new StringBuilder(); + for (Statement stmt : this) { + // IfElseStatements and Blocks control the Semicolons by themselves + if (stmt instanceof IfElseStatement || stmt instanceof Block) { + b.append(stmt).append("\n"); + } else { + b.append(stmt).append(";\n"); + } + } + return b.toString(); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/UnsupportedStatement.java b/src/main/java/net/sf/jsqlparser/statement/UnsupportedStatement.java new file mode 100644 index 0000000..f64a89f --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/UnsupportedStatement.java @@ -0,0 +1,63 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2021 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ + +package net.sf.jsqlparser.statement; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * @author Andreas Reichel + */ + +public class UnsupportedStatement implements Statement { + private List declarations; + + public UnsupportedStatement(List declarations) { + this.declarations = + Objects.requireNonNull(declarations, "The List of Tokens must not be null."); + } + + public UnsupportedStatement(String upfront, List declarations) { + this.declarations = new ArrayList<>(); + this.declarations.add(upfront); + this.declarations.addAll( + Objects.requireNonNull(declarations, "The List of Tokens must not be null.")); + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + @SuppressWarnings({"PMD.MissingBreakInSwitch", "PMD.SwitchStmtsShouldHaveDefault", + "PMD.CyclomaticComplexity"}) + public StringBuilder appendTo(StringBuilder builder) { + int i = 0; + for (String s : declarations) { + if (i > 0) { + builder.append(" "); + } + builder.append(s); + i++; + } + return builder; + } + + @Override + public String toString() { + return appendTo(new StringBuilder()).toString(); + } + + public boolean isEmpty() { + return declarations.isEmpty(); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/UseStatement.java b/src/main/java/net/sf/jsqlparser/statement/UseStatement.java new file mode 100644 index 0000000..b4b6068 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/UseStatement.java @@ -0,0 +1,60 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement; + +public class UseStatement implements Statement { + + private String name; + private boolean schemaKeyword; + + public UseStatement() { + // empty constructor + } + + public UseStatement(String name) { + this.name = name; + } + + public UseStatement(String name, boolean hasSchemaKeyword) { + this.name = name; + this.schemaKeyword = hasSchemaKeyword; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean hasSchemaKeyword() { + return schemaKeyword; + } + + public void setSchemaKeyword(boolean schemaKeyword) { + this.schemaKeyword = schemaKeyword; + } + + @Override + public String toString() { + return "USE " + (schemaKeyword ? "SCHEMA " : "") + name; + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + public UseStatement withName(String name) { + this.setName(name); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/alter/Alter.java b/src/main/java/net/sf/jsqlparser/statement/alter/Alter.java new file mode 100644 index 0000000..7c00077 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/alter/Alter.java @@ -0,0 +1,139 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.alter; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; + +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.StatementVisitor; + +public class Alter implements Statement { + + private Table table; + private boolean useOnly = false; + + private boolean useTableIfExists = false; + + private List alterExpressions; + + public Table getTable() { + return table; + } + + public void setTable(Table table) { + this.table = table; + } + + public boolean isUseOnly() { + return useOnly; + } + + public void setUseOnly(boolean useOnly) { + this.useOnly = useOnly; + } + + public boolean isUseTableIfExists() { + return useTableIfExists; + } + + public void setUseTableIfExists(boolean useTableIfExists) { + this.useTableIfExists = useTableIfExists; + } + + public Alter withUseTableIfExists(boolean useTableIfExists) { + this.useTableIfExists = useTableIfExists; + return this; + } + + public void addAlterExpression(AlterExpression alterExpression) { + if (alterExpressions == null) { + alterExpressions = new ArrayList(); + } + alterExpressions.add(alterExpression); + } + + public List getAlterExpressions() { + return alterExpressions; + } + + public void setAlterExpressions(List alterExpressions) { + this.alterExpressions = alterExpressions; + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + @Override + public String toString() { + + StringBuilder b = new StringBuilder(); + b.append("ALTER TABLE "); + if (useOnly) { + b.append("ONLY "); + } + + if (useTableIfExists) { + b.append("IF EXISTS "); + } + + b.append(table.getFullyQualifiedName()).append(" "); + + Iterator altIter = alterExpressions.iterator(); + + while (altIter.hasNext()) { + b.append(altIter.next().toString()); + + // Need to append whitespace after each ADD or DROP statement + // but not the last one + if (altIter.hasNext()) { + b.append(", "); + } + } + + return b.toString(); + } + + public Alter withTable(Table table) { + this.setTable(table); + return this; + } + + public Alter withUseOnly(boolean useOnly) { + this.setUseOnly(useOnly); + return this; + } + + public Alter withAlterExpressions(List alterExpressions) { + this.setAlterExpressions(alterExpressions); + return this; + } + + public Alter addAlterExpressions(AlterExpression... alterExpressions) { + List collection = + Optional.ofNullable(getAlterExpressions()).orElseGet(ArrayList::new); + Collections.addAll(collection, alterExpressions); + return this.withAlterExpressions(collection); + } + + public Alter addAlterExpressions(Collection alterExpressions) { + List collection = + Optional.ofNullable(getAlterExpressions()).orElseGet(ArrayList::new); + collection.addAll(alterExpressions); + return this.withAlterExpressions(collection); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java b/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java new file mode 100644 index 0000000..b13e835 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java @@ -0,0 +1,847 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.alter; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import net.sf.jsqlparser.statement.ReferentialAction; +import net.sf.jsqlparser.statement.ReferentialAction.Action; +import net.sf.jsqlparser.statement.ReferentialAction.Type; +import net.sf.jsqlparser.statement.create.table.ColDataType; +import net.sf.jsqlparser.statement.create.table.ColumnDefinition; +import net.sf.jsqlparser.statement.create.table.Index; +import net.sf.jsqlparser.statement.select.PlainSelect; + +@SuppressWarnings({"PMD.CyclomaticComplexity"}) +public class AlterExpression implements Serializable { + + private final Set referentialActions = new LinkedHashSet<>(2); + private AlterOperation operation; + private String optionalSpecifier; + private String newTableName; + private String columnName; + // private ColDataType dataType; + private String columnOldName; + private List colDataTypeList; + private List columnDropNotNullList; + private List columnDropDefaultList; + private List pkColumns; + private List ukColumns; + private String ukName; + private Index index = null; + private Index oldIndex = null; + private String constraintName; + private boolean usingIfExists; + private List fkColumns; + private String fkSourceSchema; + + private String fkSourceTable; + private List fkSourceColumns; + private boolean uk; + private boolean useEqual; + + private List constraints; + private List parameters; + private String commentText; + + private boolean hasColumn = false; + + + private boolean useBrackets = false; + + private String truncatePartitionName = null; + + private boolean useIfNotExists = false; + + public Index getOldIndex() { + return oldIndex; + } + + public void setOldIndex(Index oldIndex) { + this.oldIndex = oldIndex; + } + + public boolean hasColumn() { + return hasColumn; + } + + public boolean useBrackets() { + return useBrackets; + } + + public void useBrackets(boolean useBrackets) { + this.useBrackets = useBrackets; + } + + public void hasColumn(boolean hasColumn) { + this.hasColumn = hasColumn; + } + + public String getFkSourceSchema() { + return fkSourceSchema; + } + + public void setFkSourceSchema(String fkSourceSchema) { + this.fkSourceSchema = fkSourceSchema; + } + + public String getCommentText() { + return commentText; + } + + public void setCommentText(String commentText) { + this.commentText = commentText; + } + + public AlterOperation getOperation() { + return operation; + } + + public void setOperation(AlterOperation operation) { + this.operation = operation; + } + + public String getOptionalSpecifier() { + return optionalSpecifier; + } + + public void setOptionalSpecifier(String optionalSpecifier) { + this.optionalSpecifier = optionalSpecifier; + } + + /** + * @param type + * @param action + */ + public void setReferentialAction(Type type, Action action) { + setReferentialAction(type, action, true); + } + + public AlterExpression withReferentialAction(Type type, Action action) { + setReferentialAction(type, action); + return this; + } + + /** + * @param type + */ + public void removeReferentialAction(Type type) { + setReferentialAction(type, null, false); + } + + /** + * @param type + * @return + */ + public ReferentialAction getReferentialAction(Type type) { + return referentialActions.stream() + .filter(ra -> type.equals(ra.getType())) + .findFirst() + .orElse(null); + } + + private void setReferentialAction(Type type, Action action, boolean set) { + ReferentialAction found = getReferentialAction(type); + if (set) { + if (found == null) { + referentialActions.add(new ReferentialAction(type, action)); + } else { + found.setAction(action); + } + } else if (found != null) { + referentialActions.remove(found); + } + } + + /** + * @return + * @deprecated use {@link #getReferentialAction(ReferentialAction.Type)} + */ + @Deprecated + public boolean isOnDeleteCascade() { + ReferentialAction found = getReferentialAction(Type.DELETE); + return found != null && Action.CASCADE.equals(found.getAction()); + } + + /** + * @param onDeleteCascade + * @deprecated use + * {@link #setReferentialAction(ReferentialAction.Type, ReferentialAction.Action, boolean)} + */ + @Deprecated + public void setOnDeleteCascade(boolean onDeleteCascade) { + setReferentialAction(Type.DELETE, Action.CASCADE, onDeleteCascade); + } + + /** + * @return + * @deprecated use {@link #getReferentialAction(ReferentialAction.Type)} + */ + @Deprecated + public boolean isOnDeleteRestrict() { + ReferentialAction found = getReferentialAction(Type.DELETE); + return found != null && Action.RESTRICT.equals(found.getAction()); + } + + /** + * @param onDeleteRestrict + * @deprecated use + * {@link #setReferentialAction(ReferentialAction.Type, ReferentialAction.Action, boolean)} + */ + @Deprecated + public void setOnDeleteRestrict(boolean onDeleteRestrict) { + setReferentialAction(Type.DELETE, Action.RESTRICT, onDeleteRestrict); + } + + /** + * @return + * @deprecated use {@link #getReferentialAction(ReferentialAction.Type)} + */ + @Deprecated + public boolean isOnDeleteSetNull() { + ReferentialAction found = getReferentialAction(Type.DELETE); + return found != null && Action.SET_NULL.equals(found.getAction()); + } + + /** + * @param onDeleteSetNull + * @deprecated use + * {@link #setReferentialAction(ReferentialAction.Type, ReferentialAction.Action, boolean)} + */ + @Deprecated + public void setOnDeleteSetNull(boolean onDeleteSetNull) { + setReferentialAction(Type.DELETE, Action.SET_NULL, onDeleteSetNull); + } + + public List getFkColumns() { + return fkColumns; + } + + public void setFkColumns(List fkColumns) { + this.fkColumns = fkColumns; + } + + public String getFkSourceTable() { + return fkSourceTable; + } + + public void setFkSourceTable(String fkSourceTable) { + this.fkSourceTable = fkSourceTable; + } + + public List getColDataTypeList() { + return colDataTypeList; + } + + public void addColDataType(String columnName, ColDataType colDataType) { + addColDataType(new ColumnDataType(columnName, false, colDataType, null)); + } + + public void addColDataType(ColumnDataType columnDataType) { + if (colDataTypeList == null) { + colDataTypeList = new ArrayList<>(); + } + colDataTypeList.add(columnDataType); + } + + public void addColDropNotNull(ColumnDropNotNull columnDropNotNull) { + if (columnDropNotNullList == null) { + columnDropNotNullList = new ArrayList<>(); + } + columnDropNotNullList.add(columnDropNotNull); + } + + public void addColDropDefault(ColumnDropDefault columnDropDefault) { + if (columnDropDefaultList == null) { + columnDropDefaultList = new ArrayList<>(); + } + columnDropDefaultList.add(columnDropDefault); + } + + public List getFkSourceColumns() { + return fkSourceColumns; + } + + public void setFkSourceColumns(List fkSourceColumns) { + this.fkSourceColumns = fkSourceColumns; + } + + public String getNewTableName() { + return newTableName; + } + + public void setNewTableName(String newTableName) { + this.newTableName = newTableName; + } + + public String getColumnName() { + return columnName; + } + + public void setColumnName(String columnName) { + this.columnName = columnName; + } + + @Deprecated + public String getColOldName() { + return getColumnOldName(); + } + + @Deprecated + public void setColOldName(String columnOldName) { + setColumnOldName(columnOldName); + } + + public String getColumnOldName() { + return columnOldName; + } + + public void setColumnOldName(String columnOldName) { + this.columnOldName = columnOldName; + } + + public String getConstraintName() { + return this.constraintName; + } + + public void setConstraintName(final String constraintName) { + this.constraintName = constraintName; + } + + public boolean isUsingIfExists() { + return usingIfExists; + } + + public void setUsingIfExists(boolean usingIfExists) { + this.usingIfExists = usingIfExists; + } + + public List getPkColumns() { + return pkColumns; + } + + public void setPkColumns(List pkColumns) { + this.pkColumns = pkColumns; + } + + public List getUkColumns() { + return ukColumns; + } + + public void setUkColumns(List ukColumns) { + this.ukColumns = ukColumns; + } + + public String getUkName() { + return ukName; + } + + public void setUkName(String ukName) { + this.ukName = ukName; + } + + public Index getIndex() { + return index; + } + + public void setIndex(Index index) { + this.index = index; + } + + public List getConstraints() { + return constraints; + } + + public void setConstraints(List constraints) { + this.constraints = constraints; + } + + public List getColumnDropNotNullList() { + return columnDropNotNullList; + } + + public void addParameters(String... params) { + if (parameters == null) { + parameters = new ArrayList<>(); + } + parameters.addAll(Arrays.asList(params)); + } + + public List getParameters() { + return parameters; + } + + public boolean getUseEqual() { + return useEqual; + } + + public void setUseEqual(boolean useEqual) { + this.useEqual = useEqual; + } + + public boolean getUk() { + return uk; + } + + public void setUk(boolean uk) { + this.uk = uk; + } + + public String getTruncatePartitionName() { + return truncatePartitionName; + } + + public void setTruncatePartitionName(String truncatePartitionName) { + this.truncatePartitionName = truncatePartitionName; + } + + public AlterExpression withTruncatePartitionName(String truncatePartitionName) { + this.truncatePartitionName = truncatePartitionName; + return this; + } + + public boolean isUseIfNotExists() { + return useIfNotExists; + } + + public void setUseIfNotExists(boolean useIfNotExists) { + this.useIfNotExists = useIfNotExists; + } + + public AlterExpression withUserIfNotExists(boolean userIfNotExists) { + this.useIfNotExists = userIfNotExists; + return this; + } + + @Override + @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity", + "PMD.ExcessiveMethodLength", "PMD.SwitchStmtsShouldHaveDefault"}) + public String toString() { + + StringBuilder b = new StringBuilder(); + + if (operation == AlterOperation.UNSPECIFIC) { + b.append(optionalSpecifier); + } else if (getOldIndex() != null) { + b.append("RENAME"); + switch (operation) { + case RENAME_KEY: + b.append(" KEY "); + break; + case RENAME_INDEX: + b.append(" INDEX "); + break; + case RENAME_CONSTRAINT: + b.append(" CONSTRAINT "); + break; + } + b.append(getOldIndex().getName()).append(" TO ").append(getIndex().getName()); + } else if (operation == AlterOperation.RENAME_TABLE) { + + b.append("RENAME TO ").append(newTableName); + } else if (operation == AlterOperation.DROP_PRIMARY_KEY) { + + b.append("DROP PRIMARY KEY "); + } else if (operation == AlterOperation.DROP_UNIQUE) { + + b.append("DROP UNIQUE (").append(PlainSelect.getStringList(pkColumns)).append(')'); + } else if (operation == AlterOperation.DROP_FOREIGN_KEY) { + + b.append("DROP FOREIGN KEY (").append(PlainSelect.getStringList(pkColumns)).append(')'); + } else if (operation == AlterOperation.DROP && columnName == null && pkColumns != null + && !pkColumns.isEmpty()) { + // Oracle Multi Column Drop + b.append("DROP (").append(PlainSelect.getStringList(pkColumns)).append(')'); + } else if (operation == AlterOperation.TRUNCATE_PARTITION + && truncatePartitionName != null) { + b.append("TRUNCATE PARTITION ").append(truncatePartitionName); + } else { + if (operation == AlterOperation.COMMENT_WITH_EQUAL_SIGN) { + b.append("COMMENT =").append(" "); + } else { + b.append(operation).append(" "); + } + if (commentText != null) { + if (columnName != null) { + b.append(columnName).append(" COMMENT "); + } + b.append(commentText); + } else if (columnName != null) { + if (hasColumn) { + b.append("COLUMN "); + } + if (usingIfExists) { + b.append("IF EXISTS "); + } + if (operation == AlterOperation.RENAME) { + b.append(columnOldName).append(" TO "); + } + b.append(columnName); + } else if (getColDataTypeList() != null) { + if (operation == AlterOperation.CHANGE) { + if (optionalSpecifier != null) { + b.append(optionalSpecifier).append(" "); + } + b.append(columnOldName).append(" "); + } else if (colDataTypeList.size() > 1) { + b.append("("); + } else { + if (hasColumn) { + b.append("COLUMN "); + } + if (useIfNotExists + && operation == AlterOperation.ADD) { + b.append("IF NOT EXISTS "); + } + } + if (useBrackets && colDataTypeList.size() == 1) { + b.append(" ( "); + } + b.append(PlainSelect.getStringList(colDataTypeList)); + if (useBrackets && colDataTypeList.size() == 1) { + b.append(" ) "); + } + if (colDataTypeList.size() > 1) { + b.append(")"); + } + } else if (getColumnDropNotNullList() != null) { + b.append("COLUMN "); + b.append(PlainSelect.getStringList(columnDropNotNullList)); + } else if (columnDropDefaultList != null && !columnDropDefaultList.isEmpty()) { + b.append("COLUMN "); + b.append(PlainSelect.getStringList(columnDropDefaultList)); + } else if (constraintName != null) { + b.append("CONSTRAINT "); + if (usingIfExists) { + b.append("IF EXISTS "); + } + b.append(constraintName); + } else if (pkColumns != null) { + b.append("PRIMARY KEY (").append(PlainSelect.getStringList(pkColumns)).append(')'); + } else if (ukColumns != null) { + b.append("UNIQUE"); + if (ukName != null) { + if (getUk()) { + b.append(" KEY "); + } else { + b.append(" INDEX "); + } + b.append(ukName); + } + b.append(" (").append(PlainSelect.getStringList(ukColumns)).append(")"); + } else if (fkColumns != null) { + b.append("FOREIGN KEY (") + .append(PlainSelect.getStringList(fkColumns)) + .append(") REFERENCES ") + .append( + fkSourceSchema != null && fkSourceSchema.trim().length() > 0 + ? fkSourceSchema + "." + : "") + .append(fkSourceTable) + .append(" (") + .append(PlainSelect.getStringList(fkSourceColumns)) + .append(")"); + referentialActions.forEach(b::append); + } else if (index != null) { + b.append(index); + } + + + if (getConstraints() != null && !getConstraints().isEmpty()) { + b.append(' ').append(PlainSelect.getStringList(constraints, false, false)); + } + if (getUseEqual()) { + b.append('='); + } + } + + if (parameters != null && !parameters.isEmpty()) { + b.append(' ').append(PlainSelect.getStringList(parameters, false, false)); + } + + if (index != null && index.getCommentText() != null) { + // `USING` is a parameters + b.append(" COMMENT ").append(index.getCommentText()); + } + + return b.toString(); + } + + public AlterExpression withOperation(AlterOperation operation) { + this.setOperation(operation); + return this; + } + + public AlterExpression withOptionalSpecifier(String optionalSpecifier) { + this.setOptionalSpecifier(optionalSpecifier); + return this; + } + + public AlterExpression withColumnName(String columnName) { + this.setColumnName(columnName); + return this; + } + + public AlterExpression withPkColumns(List pkColumns) { + this.setPkColumns(pkColumns); + return this; + } + + public AlterExpression withUkColumns(List ukColumns) { + this.setUkColumns(ukColumns); + return this; + } + + public AlterExpression withUkName(String ukName) { + this.setUkName(ukName); + return this; + } + + public AlterExpression withIndex(Index index) { + this.setIndex(index); + return this; + } + + public AlterExpression withConstraintName(String constraintName) { + this.setConstraintName(constraintName); + return this; + } + + public AlterExpression withUsingIfExists(boolean usingIfExists) { + this.setUsingIfExists(usingIfExists); + return this; + } + + public AlterExpression withOnDeleteRestrict(boolean onDeleteRestrict) { + this.setOnDeleteRestrict(onDeleteRestrict); + return this; + } + + public AlterExpression withOnDeleteSetNull(boolean onDeleteSetNull) { + this.setOnDeleteSetNull(onDeleteSetNull); + return this; + } + + public AlterExpression withOnDeleteCascade(boolean onDeleteCascade) { + this.setOnDeleteCascade(onDeleteCascade); + return this; + } + + public AlterExpression withFkColumns(List fkColumns) { + this.setFkColumns(fkColumns); + return this; + } + + public AlterExpression withFkSourceSchema(String fkSourceSchema) { + this.setFkSourceTable(fkSourceSchema); + return this; + } + + public AlterExpression withFkSourceTable(String fkSourceTable) { + this.setFkSourceTable(fkSourceTable); + return this; + } + + public AlterExpression withFkSourceColumns(List fkSourceColumns) { + this.setFkSourceColumns(fkSourceColumns); + return this; + } + + public AlterExpression withUk(boolean uk) { + this.setUk(uk); + return this; + } + + public AlterExpression withUseEqual(boolean useEqual) { + this.setUseEqual(useEqual); + return this; + } + + public AlterExpression withConstraints(List constraints) { + this.setConstraints(constraints); + return this; + } + + public AlterExpression withCommentText(String commentText) { + this.setCommentText(commentText); + return this; + } + + public AlterExpression withColumnOldName(String columnOldName) { + setColumnOldName(columnOldName); + return this; + } + + public AlterExpression addPkColumns(String... pkColumns) { + List collection = Optional.ofNullable(getPkColumns()).orElseGet(ArrayList::new); + Collections.addAll(collection, pkColumns); + return this.withPkColumns(collection); + } + + public AlterExpression addPkColumns(Collection pkColumns) { + List collection = Optional.ofNullable(getPkColumns()).orElseGet(ArrayList::new); + collection.addAll(pkColumns); + return this.withPkColumns(collection); + } + + public AlterExpression addUkColumns(String... ukColumns) { + List collection = Optional.ofNullable(getUkColumns()).orElseGet(ArrayList::new); + Collections.addAll(collection, ukColumns); + return this.withUkColumns(collection); + } + + public AlterExpression addUkColumns(Collection ukColumns) { + List collection = Optional.ofNullable(getUkColumns()).orElseGet(ArrayList::new); + collection.addAll(ukColumns); + return this.withUkColumns(collection); + } + + public AlterExpression addFkColumns(String... fkColumns) { + List collection = Optional.ofNullable(getFkColumns()).orElseGet(ArrayList::new); + Collections.addAll(collection, fkColumns); + return this.withFkColumns(collection); + } + + public AlterExpression addFkColumns(Collection fkColumns) { + List collection = Optional.ofNullable(getFkColumns()).orElseGet(ArrayList::new); + collection.addAll(fkColumns); + return this.withFkColumns(collection); + } + + public AlterExpression addFkSourceColumns(String... fkSourceColumns) { + List collection = + Optional.ofNullable(getFkSourceColumns()).orElseGet(ArrayList::new); + Collections.addAll(collection, fkSourceColumns); + return this.withFkSourceColumns(collection); + } + + public AlterExpression addFkSourceColumns(Collection fkSourceColumns) { + List collection = + Optional.ofNullable(getFkSourceColumns()).orElseGet(ArrayList::new); + collection.addAll(fkSourceColumns); + return this.withFkSourceColumns(collection); + } + + public AlterExpression addConstraints(ConstraintState... constraints) { + List collection = + Optional.ofNullable(getConstraints()).orElseGet(ArrayList::new); + Collections.addAll(collection, constraints); + return this.withConstraints(collection); + } + + public AlterExpression addConstraints(Collection constraints) { + List collection = + Optional.ofNullable(getConstraints()).orElseGet(ArrayList::new); + collection.addAll(constraints); + return this.withConstraints(collection); + } + + public static final class ColumnDataType extends ColumnDefinition { + + private final boolean withType; + + public ColumnDataType(boolean withType) { + super(); + this.withType = withType; + } + + public ColumnDataType( + String columnName, boolean withType, ColDataType colDataType, + List columnSpecs) { + super(columnName, colDataType, columnSpecs); + this.withType = withType; + } + + @Override + public String toString() { + return getColumnName() + (withType ? " TYPE " : getColDataType() == null ? "" : " ") + + toStringDataTypeAndSpec(); + } + + @Override + public ColumnDataType withColDataType(ColDataType colDataType) { + return (ColumnDataType) super.withColDataType(colDataType); + } + + @Override + public ColumnDataType withColumnName(String columnName) { + return (ColumnDataType) super.withColumnName(columnName); + } + + @Override + public ColumnDataType addColumnSpecs(String... columnSpecs) { + return (ColumnDataType) super.addColumnSpecs(columnSpecs); + } + + @Override + public ColumnDataType addColumnSpecs(Collection columnSpecs) { + return (ColumnDataType) super.addColumnSpecs(columnSpecs); + } + + @Override + public ColumnDataType withColumnSpecs(List columnSpecs) { + return (ColumnDataType) super.withColumnSpecs(columnSpecs); + } + } + + public static final class ColumnDropNotNull implements Serializable { + + private final String columnName; + private final boolean withNot; + + public ColumnDropNotNull(String columnName) { + this(columnName, false); + } + + public ColumnDropNotNull(String columnName, boolean withNot) { + this.columnName = columnName; + this.withNot = withNot; + } + + public String getColumnName() { + return columnName; + } + + public boolean isWithNot() { + return withNot; + } + + @Override + public String toString() { + return columnName + " DROP" + (withNot ? " NOT " : " ") + "NULL"; + } + } + + public static final class ColumnDropDefault implements Serializable { + + private final String columnName; + + public ColumnDropDefault(String columnName) { + this.columnName = columnName; + } + + public String getColumnName() { + return columnName; + } + + @Override + public String toString() { + return columnName + " DROP DEFAULT"; + } + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/alter/AlterOperation.java b/src/main/java/net/sf/jsqlparser/statement/alter/AlterOperation.java new file mode 100644 index 0000000..3d0ead7 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/alter/AlterOperation.java @@ -0,0 +1,18 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.alter; + +public enum AlterOperation { + ADD, ALTER, DROP, DROP_PRIMARY_KEY, DROP_UNIQUE, DROP_FOREIGN_KEY, MODIFY, CHANGE, ALGORITHM, RENAME, RENAME_TABLE, RENAME_INDEX, RENAME_KEY, RENAME_CONSTRAINT, COMMENT, COMMENT_WITH_EQUAL_SIGN, UNSPECIFIC, TRUNCATE_PARTITION; + + public static AlterOperation from(String operation) { + return Enum.valueOf(AlterOperation.class, operation.toUpperCase()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/alter/AlterSession.java b/src/main/java/net/sf/jsqlparser/statement/alter/AlterSession.java new file mode 100644 index 0000000..e7e8308 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/alter/AlterSession.java @@ -0,0 +1,153 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2021 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ + +package net.sf.jsqlparser.statement.alter; + +import java.util.List; + +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.StatementVisitor; + +/** + * @author are + * @see ALTER + * SESSION + */ +public class AlterSession implements Statement { + private AlterSessionOperation operation; + private List parameters; + + public AlterSession(AlterSessionOperation operation, List parameters) { + this.operation = operation; + this.parameters = parameters; + } + + private static void appendParameters(StringBuilder builder, List parameters) { + for (String s : parameters) { + builder.append(" ").append(s); + } + } + + public AlterSessionOperation getOperation() { + return operation; + } + + public void setOperation(AlterSessionOperation operation) { + this.operation = operation; + } + + public List getParameters() { + return parameters; + } + + public void setParameters(List parameters) { + this.parameters = parameters; + } + + @Override + @SuppressWarnings({"PMD.ExcessiveMethodLength", "PMD.CyclomaticComplexity"}) + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("ALTER SESSION "); + switch (operation) { + case ADVISE_COMMIT: + builder.append("ADVISE COMMIT"); + break; + case ADVISE_ROLLBACK: + builder.append("ADVISE ROLLBACK"); + break; + case ADVISE_NOTHING: + builder.append("ADVISE NOTHING"); + break; + case CLOSE_DATABASE_LINK: + builder.append("CLOSE DATABASE LINK "); + appendParameters(builder, parameters); + break; + case ENABLE_COMMIT_IN_PROCEDURE: + builder.append("ENABLE COMMIT IN PROCEDURE"); + break; + case DISABLE_COMMIT_IN_PROCEDURE: + builder.append("DISABLE COMMIT IN PROCEDURE"); + break; + case ENABLE_GUARD: + builder.append("ENABLE GUARD"); + break; + case DISABLE_GUARD: + builder.append("DISABLE GUARD"); + break; + + case ENABLE_PARALLEL_DML: + builder.append("ENABLE PARALLEL DML"); + appendParameters(builder, parameters); + break; + + case DISABLE_PARALLEL_DML: + builder.append("DISABLE PARALLEL DML"); + appendParameters(builder, parameters); + break; + + case FORCE_PARALLEL_DML: + builder.append("FORCE PARALLEL DML"); + appendParameters(builder, parameters); + break; + + case ENABLE_PARALLEL_DDL: + builder.append("ENABLE PARALLEL DDL"); + appendParameters(builder, parameters); + break; + + case DISABLE_PARALLEL_DDL: + builder.append("DISABLE PARALLEL DDL"); + break; + + case FORCE_PARALLEL_DDL: + builder.append("FORCE PARALLEL DDL"); + appendParameters(builder, parameters); + break; + + case ENABLE_PARALLEL_QUERY: + builder.append("ENABLE PARALLEL QUERY"); + appendParameters(builder, parameters); + break; + + case DISABLE_PARALLEL_QUERY: + builder.append("DISABLE PARALLEL QUERY"); + break; + + case FORCE_PARALLEL_QUERY: + builder.append("FORCE PARALLEL QUERY"); + appendParameters(builder, parameters); + break; + + case ENABLE_RESUMABLE: + builder.append("ENABLE RESUMABLE"); + appendParameters(builder, parameters); + break; + + case DISABLE_RESUMABLE: + builder.append("DISABLE RESUMABLE"); + break; + + case SET: + builder.append("SET"); + appendParameters(builder, parameters); + break; + default: + // not going to happen + + } + return builder.toString(); + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/alter/AlterSessionOperation.java b/src/main/java/net/sf/jsqlparser/statement/alter/AlterSessionOperation.java new file mode 100644 index 0000000..1b8e4ec --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/alter/AlterSessionOperation.java @@ -0,0 +1,22 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2021 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ + +package net.sf.jsqlparser.statement.alter; + +/** + * @author are + */ +public enum AlterSessionOperation { + ADVISE_COMMIT, ADVISE_ROLLBACK, ADVISE_NOTHING, CLOSE_DATABASE_LINK, ENABLE_COMMIT_IN_PROCEDURE, DISABLE_COMMIT_IN_PROCEDURE, ENABLE_GUARD, DISABLE_GUARD, ENABLE_PARALLEL_DML, DISABLE_PARALLEL_DML, FORCE_PARALLEL_DML, ENABLE_PARALLEL_DDL, DISABLE_PARALLEL_DDL, FORCE_PARALLEL_DDL, ENABLE_PARALLEL_QUERY, DISABLE_PARALLEL_QUERY, FORCE_PARALLEL_QUERY, ENABLE_RESUMABLE, DISABLE_RESUMABLE, SET; + + public static AlterSessionOperation from(String operation) { + return Enum.valueOf(AlterSessionOperation.class, operation.toUpperCase()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/alter/AlterSystemOperation.java b/src/main/java/net/sf/jsqlparser/statement/alter/AlterSystemOperation.java new file mode 100644 index 0000000..e3a4dc5 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/alter/AlterSystemOperation.java @@ -0,0 +1,60 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2021 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ + +package net.sf.jsqlparser.statement.alter; + +/** + * @author Andreas Reichel + * @see ALTER + * SESSION + */ + +public enum AlterSystemOperation { + ARCHIVE_LOG("ARCHIVE LOG"), CHECKPOINT("CHECKPOINT"), CHECK_DATAFILES( + "CHECK DATAFILES"), DUMP_ACTIVE_SESSION_HISTORY( + "DUMP ACTIVE SESSION HISTORY"), ENABLE_DISTRIBUTED_RECOVERY( + "ENABLE DISTRIBUTED RECOVERY"), DISABLE_DISTRIBUTED_RECOVERY( + "DISABLE DISTRIBUTED RECOVERY"), ENABLE_RESTRICTED_SESSION( + "ENABLE RESTRICTED SESSION"), DISABLE_RESTRICTED_SESSION( + "DISABLE RESTRICTED SESSION"), FLUSH( + "FLUSH"), DISCONNECT_SESSION( + "DISCONNECT SESSION"), KILL_SESSION( + "KILL SESSION"), SWITCH( + "SWITCH"), SUSPEND( + "SUSPEND"), RESUME( + "RESUME"), QUIESCE( + "QUIESCE RESTRICTED"), UNQUIESCE( + "UNQUIESCE"), SHUTDOWN( + "SHUTDOWN"), REGISTER( + "REGISTER"), SET( + "SET"), RESET( + "RESET"); + + private final String label; + + AlterSystemOperation(String label) { + this.label = label; + } + + public static AlterSystemOperation from(String operation) { + // We can't use Enum.valueOf() since there White Space involved + for (AlterSystemOperation alterSystemOperation : values()) { + if (alterSystemOperation.toString().equals(operation)) { + return alterSystemOperation; + } + } + return null; + } + + @Override + public String toString() { + return label; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/alter/AlterSystemStatement.java b/src/main/java/net/sf/jsqlparser/statement/alter/AlterSystemStatement.java new file mode 100644 index 0000000..40ff46b --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/alter/AlterSystemStatement.java @@ -0,0 +1,65 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2021 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.alter; + +import java.util.List; +import java.util.Objects; + +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.StatementVisitor; + +/** + * @author Andreas Reichel + * @see ALTER + * SESSION + */ + +public class AlterSystemStatement implements Statement { + private final AlterSystemOperation operation; + private final List parameters; + + public AlterSystemStatement(AlterSystemOperation operation, List parameters) { + this.operation = + Objects.requireNonNull(operation, "The ALTER SYSTEM Operation must not be Null"); + this.parameters = Objects.requireNonNull(parameters, + "The PARAMETERS List must not be null although it can be empty."); + } + + private static void appendParameters(StringBuilder builder, List parameters) { + for (String s : parameters) { + builder.append(" ").append(s); + } + } + + public AlterSystemOperation getOperation() { + return operation; + } + + public List getParameters() { + return parameters; + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + public StringBuilder appendTo(StringBuilder builder) { + builder.append("ALTER SYSTEM ").append(operation); + appendParameters(builder, parameters); + return builder; + } + + @Override + public String toString() { + return appendTo(new StringBuilder()).toString(); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/statement/alter/ConstraintState.java b/src/main/java/net/sf/jsqlparser/statement/alter/ConstraintState.java new file mode 100644 index 0000000..432173a --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/alter/ConstraintState.java @@ -0,0 +1,13 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.alter; + +public interface ConstraintState { +} diff --git a/src/main/java/net/sf/jsqlparser/statement/alter/DeferrableConstraint.java b/src/main/java/net/sf/jsqlparser/statement/alter/DeferrableConstraint.java new file mode 100644 index 0000000..ee8b750 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/alter/DeferrableConstraint.java @@ -0,0 +1,46 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.alter; + +public class DeferrableConstraint implements ConstraintState { + + private boolean not; + + public DeferrableConstraint() { + // empty constructor + } + + public DeferrableConstraint(boolean not) { + this.not = not; + } + + public boolean isNot() { + return not; + } + + public void setNot(boolean not) { + this.not = not; + } + + @Override + public String toString() { + StringBuilder b = new StringBuilder(); + if (not) { + b.append("NOT "); + } + b.append("DEFERRABLE"); + return b.toString(); + } + + public DeferrableConstraint withNot(boolean not) { + this.setNot(not); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/alter/EnableConstraint.java b/src/main/java/net/sf/jsqlparser/statement/alter/EnableConstraint.java new file mode 100644 index 0000000..48a319e --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/alter/EnableConstraint.java @@ -0,0 +1,41 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.alter; + +public class EnableConstraint implements ConstraintState { + + private boolean disable; + + public EnableConstraint() { + // empty constructor + } + + public EnableConstraint(boolean disable) { + this.disable = disable; + } + + public boolean isDisable() { + return disable; + } + + public void setDisable(boolean disable) { + this.disable = disable; + } + + @Override + public String toString() { + return disable ? "DISABLE" : "ENABLE"; + } + + public EnableConstraint withDisable(boolean disable) { + this.setDisable(disable); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/alter/RenameTableStatement.java b/src/main/java/net/sf/jsqlparser/statement/alter/RenameTableStatement.java new file mode 100644 index 0000000..94eb39b --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/alter/RenameTableStatement.java @@ -0,0 +1,153 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2021 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ + +package net.sf.jsqlparser.statement.alter; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Set; + +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.StatementVisitor; + +/** + * @author are + * @see Rename + */ +public class RenameTableStatement implements Statement { + private final LinkedHashMap tableNames = new LinkedHashMap<>(); + + private boolean usingTableKeyword = false; + private boolean usingIfExistsKeyword = false; + + private String waitDirective = ""; + + public RenameTableStatement(Table oldName, Table newName) { + tableNames.put( + Objects.requireNonNull(oldName, + "The OLD NAME of the Rename Statement must not be null."), + Objects.requireNonNull(newName, + "The NEW NAME of the Rename Statement must not be null.")); + } + + public RenameTableStatement(Table oldName, Table newName, boolean usingTableKeyword, + boolean usingIfExistsKeyword, String waitDirective) { + tableNames.put( + Objects.requireNonNull(oldName, + "The OLD NAME of the Rename Statement must not be null."), + Objects.requireNonNull(newName, + "The NEW NAME of the Rename Statement must not be null.")); + + this.usingTableKeyword = usingTableKeyword; + this.usingIfExistsKeyword = usingIfExistsKeyword; + this.waitDirective = waitDirective; + } + + public void addTableNames(Table oldName, Table newName) { + tableNames.put( + Objects.requireNonNull(oldName, + "The OLD NAME of the Rename Statement must not be null."), + Objects.requireNonNull(newName, + "The NEW NAME of the Rename Statement must not be null.")); + } + + + public boolean isUsingTableKeyword() { + return usingTableKeyword; + } + + public void setUsingTableKeyword(boolean usingTableKeyword) { + this.usingTableKeyword = usingTableKeyword; + } + + public RenameTableStatement withUsingTableKeyword(boolean usingTableKeyword) { + this.usingTableKeyword = usingTableKeyword; + return this; + } + + public boolean isUsingIfExistsKeyword() { + return usingIfExistsKeyword; + } + + public void setUsingIfExistsKeyword(boolean usingIfExistsKeyword) { + this.usingIfExistsKeyword = usingIfExistsKeyword; + } + + public RenameTableStatement withUsingIfExistsKeyword(boolean usingIfExistsKeyword) { + this.usingIfExistsKeyword = usingIfExistsKeyword; + return this; + } + + public String getWaitDirective() { + return waitDirective; + } + + public void setWaitDirective(String waitDirective) { + this.waitDirective = waitDirective; + } + + public RenameTableStatement withWaitDirective(String waitDirective) { + this.waitDirective = waitDirective; + return this; + } + + public int getTableNamesSize() { + return tableNames.size(); + } + + public boolean isTableNamesEmpty() { + return tableNames.isEmpty(); + } + + public Set> getTableNames() { + return tableNames.entrySet(); + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + public StringBuilder appendTo(StringBuilder builder) { + int i = 0; + for (Entry e : tableNames.entrySet()) { + if (i == 0) { + builder + .append("RENAME") + .append(usingTableKeyword ? " TABLE " : " ") + .append(usingIfExistsKeyword ? " IF EXISTS " : " ") + .append(e.getKey()) + .append(waitDirective != null && waitDirective.length() > 0 + ? " " + waitDirective + : "") + .append(" TO ") + .append(e.getValue()); + } else { + builder + .append(", ") + .append(e.getKey()) + .append(" TO ") + .append(e.getValue()); + } + + i++; + } + return builder; + } + + @Override + public String toString() { + return appendTo(new StringBuilder()).toString(); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/alter/ValidateConstraint.java b/src/main/java/net/sf/jsqlparser/statement/alter/ValidateConstraint.java new file mode 100644 index 0000000..93f58be --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/alter/ValidateConstraint.java @@ -0,0 +1,41 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.alter; + +public class ValidateConstraint implements ConstraintState { + + private boolean not; + + public ValidateConstraint() { + // empty constructor + } + + public ValidateConstraint(boolean not) { + this.not = not; + } + + public boolean isNot() { + return not; + } + + public void setNot(boolean not) { + this.not = not; + } + + @Override + public String toString() { + return not ? "NOVALIDATE" : "VALIDATE"; + } + + public ValidateConstraint withNot(boolean not) { + this.setNot(not); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/alter/sequence/AlterSequence.java b/src/main/java/net/sf/jsqlparser/statement/alter/sequence/AlterSequence.java new file mode 100644 index 0000000..a9869f8 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/alter/sequence/AlterSequence.java @@ -0,0 +1,47 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.alter.sequence; + +import net.sf.jsqlparser.schema.Sequence; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.StatementVisitor; + +/** + * An {@code ALTER SEQUENCE} statement + */ +public class AlterSequence implements Statement { + + public Sequence sequence; + + public Sequence getSequence() { + return sequence; + } + + public void setSequence(Sequence sequence) { + this.sequence = sequence; + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + @Override + public String toString() { + String sql; + sql = "ALTER SEQUENCE " + sequence; + return sql; + } + + public AlterSequence withSequence(Sequence sequence) { + this.setSequence(sequence); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/analyze/Analyze.java b/src/main/java/net/sf/jsqlparser/statement/analyze/Analyze.java new file mode 100644 index 0000000..ab25cf3 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/analyze/Analyze.java @@ -0,0 +1,42 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2022 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.analyze; + +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.StatementVisitor; + +public class Analyze implements Statement { + + private Table table; + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + public Table getTable() { + return table; + } + + public void setTable(Table table) { + this.table = table; + } + + @Override + public String toString() { + return "ANALYZE " + table.toString(); + } + + public Analyze withTable(Table table) { + this.setTable(table); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/comment/Comment.java b/src/main/java/net/sf/jsqlparser/statement/comment/Comment.java new file mode 100644 index 0000000..d213aac --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/comment/Comment.java @@ -0,0 +1,90 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.comment; + +import net.sf.jsqlparser.expression.StringValue; +import net.sf.jsqlparser.schema.Column; +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.StatementVisitor; + +public class Comment implements Statement { + + private Table table; + private Column column; + private Table view; + private StringValue comment; + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + public Table getTable() { + return table; + } + + public void setTable(Table table) { + this.table = table; + } + + public Column getColumn() { + return column; + } + + public void setColumn(Column column) { + this.column = column; + } + + public Table getView() { + return view; + } + + public void setView(Table view) { + this.view = view; + } + + public StringValue getComment() { + return comment; + } + + public void setComment(StringValue comment) { + this.comment = comment; + } + + @Override + public String toString() { + String sql = "COMMENT ON "; + if (table != null) { + sql += "TABLE " + table + " "; + } else if (column != null) { + sql += "COLUMN " + column + " "; + } else if (view != null) { + sql += "VIEW " + view + " "; + } + sql += "IS " + comment; + return sql; + } + + public Comment withTable(Table table) { + this.setTable(table); + return this; + } + + public Comment withColumn(Column column) { + this.setColumn(column); + return this; + } + + public Comment withComment(StringValue comment) { + this.setComment(comment); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/create/function/CreateFunction.java b/src/main/java/net/sf/jsqlparser/statement/create/function/CreateFunction.java new file mode 100644 index 0000000..ac20c0e --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/create/function/CreateFunction.java @@ -0,0 +1,49 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.create.function; + +import java.util.Collection; +import java.util.List; + +import net.sf.jsqlparser.statement.CreateFunctionalStatement; + +/** + * A {@code CREATE PROCEDURE} statement + */ +public class CreateFunction extends CreateFunctionalStatement { + + public CreateFunction() { + super("FUNCTION"); + } + + public CreateFunction(List functionDeclarationParts) { + this(false, functionDeclarationParts); + } + + public CreateFunction(boolean orReplace, List functionDeclarationParts) { + super(orReplace, "FUNCTION", functionDeclarationParts); + } + + @Override + public CreateFunction withFunctionDeclarationParts(List functionDeclarationParts) { + return (CreateFunction) super.withFunctionDeclarationParts(functionDeclarationParts); + } + + @Override + public CreateFunction addFunctionDeclarationParts(String... functionDeclarationParts) { + return (CreateFunction) super.addFunctionDeclarationParts(functionDeclarationParts); + } + + @Override + public CreateFunction addFunctionDeclarationParts(Collection functionDeclarationParts) { + return (CreateFunction) super.addFunctionDeclarationParts(functionDeclarationParts); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/statement/create/index/CreateIndex.java b/src/main/java/net/sf/jsqlparser/statement/create/index/CreateIndex.java new file mode 100644 index 0000000..87e8f1e --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/create/index/CreateIndex.java @@ -0,0 +1,140 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.create.index; + +import static java.util.stream.Collectors.joining; + +import java.util.*; + +import net.sf.jsqlparser.schema.*; +import net.sf.jsqlparser.statement.*; +import net.sf.jsqlparser.statement.create.table.*; + +public class CreateIndex implements Statement { + + private Table table; + private Index index; + private List tailParameters; + private boolean indexTypeBeforeOn = false; + private boolean usingIfNotExists = false; + + public boolean isIndexTypeBeforeOn() { + return indexTypeBeforeOn; + } + + public void setIndexTypeBeforeOn(boolean indexTypeBeforeOn) { + this.indexTypeBeforeOn = indexTypeBeforeOn; + } + + public boolean isUsingIfNotExists() { + return usingIfNotExists; + } + + public CreateIndex setUsingIfNotExists(boolean usingIfNotExists) { + this.usingIfNotExists = usingIfNotExists; + return this; + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + public Index getIndex() { + return index; + } + + public void setIndex(Index index) { + this.index = index; + } + + public Table getTable() { + return table; + } + + public void setTable(Table table) { + this.table = table; + } + + public List getTailParameters() { + return tailParameters; + } + + public void setTailParameters(List tailParameters) { + this.tailParameters = tailParameters; + } + + @Override + public String toString() { + StringBuilder buffer = new StringBuilder(); + + buffer.append("CREATE "); + + if (index.getType() != null) { + buffer.append(index.getType()); + buffer.append(" "); + } + + buffer.append("INDEX "); + if (usingIfNotExists) { + buffer.append("IF NOT EXISTS "); + } + buffer.append(index.getName()); + + if (index.getUsing() != null && isIndexTypeBeforeOn()) { + buffer.append(" USING "); + buffer.append(index.getUsing()); + } + + buffer.append(" ON "); + buffer.append(table.getFullyQualifiedName()); + + if (index.getUsing() != null && !isIndexTypeBeforeOn()) { + buffer.append(" USING "); + buffer.append(index.getUsing()); + } + + if (index.getColumnsNames() != null) { + buffer.append(" ("); + + buffer.append( + index.getColumns().stream() + .map(cp -> cp.columnName + (cp.getParams() != null + ? " " + String.join(" ", cp.getParams()) + : "")) + .collect(joining(", "))); + + buffer.append(")"); + + if (tailParameters != null) { + for (String param : tailParameters) { + buffer.append(" ").append(param); + } + } + } + + return buffer.toString(); + } + + public CreateIndex withTable(Table table) { + this.setTable(table); + return this; + } + + public CreateIndex withIndex(Index index) { + this.setIndex(index); + return this; + } + + public CreateIndex withTailParameters(List tailParameters) { + this.setTailParameters(tailParameters); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/create/procedure/CreateProcedure.java b/src/main/java/net/sf/jsqlparser/statement/create/procedure/CreateProcedure.java new file mode 100644 index 0000000..77b3851 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/create/procedure/CreateProcedure.java @@ -0,0 +1,50 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.create.procedure; + +import java.util.Collection; +import java.util.List; + +import net.sf.jsqlparser.statement.CreateFunctionalStatement; + +/** + * A {@code CREATE PROCEDURE} statement + */ +public class CreateProcedure extends CreateFunctionalStatement { + + public CreateProcedure() { + super("PROCEDURE"); + } + + public CreateProcedure(List functionDeclarationParts) { + this(false, functionDeclarationParts); + } + + public CreateProcedure(boolean orReplace, List functionDeclarationParts) { + super(orReplace, "PROCEDURE", functionDeclarationParts); + } + + @Override + public CreateProcedure withFunctionDeclarationParts(List functionDeclarationParts) { + return (CreateProcedure) super.withFunctionDeclarationParts(functionDeclarationParts); + } + + @Override + public CreateProcedure addFunctionDeclarationParts(String... functionDeclarationParts) { + return (CreateProcedure) super.addFunctionDeclarationParts(functionDeclarationParts); + } + + @Override + public CreateProcedure addFunctionDeclarationParts( + Collection functionDeclarationParts) { + return (CreateProcedure) super.addFunctionDeclarationParts(functionDeclarationParts); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/statement/create/schema/CreateSchema.java b/src/main/java/net/sf/jsqlparser/statement/create/schema/CreateSchema.java new file mode 100644 index 0000000..0a17ddb --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/create/schema/CreateSchema.java @@ -0,0 +1,143 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.create.schema; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.StatementVisitor; + +public class CreateSchema implements Statement { + + private String authorization; + private String schemaName; + private List schemaPath; + private List statements = new ArrayList<>(); + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + /** + * Add a statement to the schema definition + * + * @param statement The statement to be added + * @return true if the operation was successful + */ + public boolean addStatement(Statement statement) { + return statements.add(statement); + } + + /** + * The owner of the schema. + * + * @return Owner name + */ + public String getAuthorization() { + return authorization; + } + + /** + * The owner of the schems. + * + * @param authorization Owner name + */ + public void setAuthorization(String authorization) { + this.authorization = authorization; + } + + /** + * The name of the schema + * + * @return Schema name + */ + public String getSchemaName() { + return schemaName; + } + + /** + * Set the name of the schema + * + * @param schemaName Schema name + */ + public void setSchemaName(String schemaName) { + this.schemaName = schemaName; + } + + /** + * The path of the schema + * + * @return Schema path + */ + public List getSchemaPath() { + return schemaPath; + } + + /** + * Set the path of the schema + * + * @param schemaPath Schema path + */ + public void setSchemaPath(List schemaPath) { + this.schemaPath = schemaPath; + } + + /** + * The statements executed as part of the schema creation + * + * @return the statements + */ + public List getStatements() { + return statements; + } + + public String toString() { + String sql = "CREATE SCHEMA"; + if (schemaName != null) { + sql += " " + schemaName; + } + if (authorization != null) { + sql += " AUTHORIZATION " + authorization; + } + return sql; + } + + public CreateSchema withAuthorization(String authorization) { + this.setAuthorization(authorization); + return this; + } + + public CreateSchema withSchemaName(String schemaName) { + this.setSchemaName(schemaName); + return this; + } + + public CreateSchema withSchemaPath(List schemaPath) { + this.setSchemaPath(schemaPath); + return this; + } + + public CreateSchema addSchemaPath(String... schemaPath) { + List collection = Optional.ofNullable(getSchemaPath()).orElseGet(ArrayList::new); + Collections.addAll(collection, schemaPath); + return this.withSchemaPath(collection); + } + + public CreateSchema addSchemaPath(Collection schemaPath) { + List collection = Optional.ofNullable(getSchemaPath()).orElseGet(ArrayList::new); + collection.addAll(schemaPath); + return this.withSchemaPath(collection); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/create/sequence/CreateSequence.java b/src/main/java/net/sf/jsqlparser/statement/create/sequence/CreateSequence.java new file mode 100644 index 0000000..7deff21 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/create/sequence/CreateSequence.java @@ -0,0 +1,42 @@ +/* + * #%L JSQLParser library %% Copyright (C) 2004 - 2020 JSQLParser %% Dual licensed under GNU LGPL + * 2.1 or Apache License 2.0 #L% + */ +package net.sf.jsqlparser.statement.create.sequence; + +import net.sf.jsqlparser.schema.Sequence; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.StatementVisitor; + +/** + * A {@code CREATE SEQUENCE} statement + */ +public class CreateSequence implements Statement { + + public Sequence sequence; + + public Sequence getSequence() { + return sequence; + } + + public void setSequence(Sequence sequence) { + this.sequence = sequence; + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + @Override + public String toString() { + String sql; + sql = "CREATE SEQUENCE " + sequence; + return sql; + } + + public CreateSequence withSequence(Sequence sequence) { + this.setSequence(sequence); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/create/synonym/CreateSynonym.java b/src/main/java/net/sf/jsqlparser/statement/create/synonym/CreateSynonym.java new file mode 100644 index 0000000..ac7eb26 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/create/synonym/CreateSynonym.java @@ -0,0 +1,94 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.create.synonym; + +import net.sf.jsqlparser.schema.Synonym; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.StatementVisitor; + +import java.util.ArrayList; +import java.util.List; + +public class CreateSynonym implements Statement { + + public Synonym synonym; + private boolean orReplace; + private boolean publicSynonym; + private List forList = new ArrayList<>(); + + public Synonym getSynonym() { + return synonym; + } + + public void setSynonym(Synonym synonym) { + this.synonym = synonym; + } + + public boolean isOrReplace() { + return orReplace; + } + + public void setOrReplace(boolean orReplace) { + this.orReplace = orReplace; + } + + public boolean isPublicSynonym() { + return publicSynonym; + } + + public void setPublicSynonym(boolean publicSynonym) { + this.publicSynonym = publicSynonym; + } + + public List getForList() { + return forList; + } + + public void setForList(List forList) { + this.forList = forList; + } + + public String getFor() { + StringBuilder b = new StringBuilder(); + for (String name : forList) { + if (b.length() > 0) { + b.append("."); + } + b.append(name); + } + return b.toString(); + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + @Override + public String toString() { + StringBuilder sqlBuilder = new StringBuilder(); + sqlBuilder.append("CREATE "); + if (orReplace) { + sqlBuilder.append("OR REPLACE "); + } + if (publicSynonym) { + sqlBuilder.append("PUBLIC "); + } + sqlBuilder.append("SYNONYM " + synonym); + sqlBuilder.append(' '); + sqlBuilder.append("FOR " + getFor()); + return sqlBuilder.toString(); + } + + public CreateSynonym withSynonym(Synonym synonym) { + this.setSynonym(synonym); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/create/table/CheckConstraint.java b/src/main/java/net/sf/jsqlparser/statement/create/table/CheckConstraint.java new file mode 100644 index 0000000..b81f713 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/create/table/CheckConstraint.java @@ -0,0 +1,104 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.create.table; + +import java.util.Collection; +import java.util.List; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.schema.Table; + +public class CheckConstraint extends NamedConstraint { + + private Table table; + + private Expression expression; + + public Table getTable() { + return table; + } + + public void setTable(Table table) { + this.table = table; + } + + public Expression getExpression() { + return expression; + } + + public void setExpression(Expression expression) { + this.expression = expression; + } + + @Override + public String toString() { + return "CONSTRAINT " + getName() + " CHECK (" + expression + ")"; + } + + public CheckConstraint withTable(Table table) { + this.setTable(table); + return this; + } + + public CheckConstraint withExpression(Expression expression) { + this.setExpression(expression); + return this; + } + + public E getExpression(Class type) { + return type.cast(getExpression()); + } + + @Override + public CheckConstraint withType(String type) { + return (CheckConstraint) super.withType(type); + } + + @Override + public CheckConstraint withUsing(String using) { + return (CheckConstraint) super.withUsing(using); + } + + @Override + public CheckConstraint withName(List name) { + return (CheckConstraint) super.withName(name); + } + + @Override + public CheckConstraint withName(String name) { + return (CheckConstraint) super.withName(name); + } + + @Override + public CheckConstraint withColumnsNames(List list) { + return (CheckConstraint) super.withColumnsNames(list); + } + + @Override + public CheckConstraint withColumns(List columns) { + return (CheckConstraint) super.withColumns(columns); + } + + @Override + public CheckConstraint addColumns(ColumnParams... functionDeclarationParts) { + return (CheckConstraint) super.addColumns(functionDeclarationParts); + } + + @Override + public CheckConstraint addColumns(Collection functionDeclarationParts) { + return (CheckConstraint) super.addColumns(functionDeclarationParts); + } + + @Override + public CheckConstraint withIndexSpec(List idxSpec) { + return (CheckConstraint) super.withIndexSpec(idxSpec); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/statement/create/table/ColDataType.java b/src/main/java/net/sf/jsqlparser/statement/create/table/ColDataType.java new file mode 100644 index 0000000..b7b7a9a --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/create/table/ColDataType.java @@ -0,0 +1,175 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.create.table; + +import net.sf.jsqlparser.statement.select.PlainSelect; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static java.util.stream.Collectors.joining; + +public class ColDataType implements Serializable { + + private String dataType; + private List argumentsStringList; + private String characterSet; + private List arrayData = new ArrayList(); + + public ColDataType() { + // empty constructor + } + + public ColDataType(String dataType, int precision, int scale) { + this.dataType = dataType; + + if (precision >= 0) { + this.dataType += " (" + (precision == Integer.MAX_VALUE ? "MAX" : precision); + if (scale >= 0) { + this.dataType += ", " + scale; + } + this.dataType += ")"; + } + } + + public ColDataType(String dataType) { + this.dataType = dataType; + } + + public List getArgumentsStringList() { + return argumentsStringList; + } + + public void setArgumentsStringList(List list) { + argumentsStringList = list; + } + + public String getDataType() { + return dataType; + } + + public void setDataType(String string) { + dataType = string; + } + + public void setDataType(List list) { + dataType = list.stream().collect(joining(".")); + } + + public String getCharacterSet() { + return characterSet; + } + + public void setCharacterSet(String characterSet) { + this.characterSet = characterSet; + } + + public List getArrayData() { + return arrayData; + } + + public void setArrayData(List arrayData) { + this.arrayData = arrayData; + } + + @Override + public String toString() { + StringBuilder arraySpec = new StringBuilder(); + for (Integer item : arrayData) { + arraySpec.append("["); + if (item != null) { + arraySpec.append(item); + } + arraySpec.append("]"); + } + return dataType + + (argumentsStringList != null + ? " " + PlainSelect.getStringList(argumentsStringList, true, true) + : "") + + arraySpec.toString() + + (characterSet != null ? " CHARACTER SET " + characterSet : ""); + } + + public ColDataType withDataType(String dataType) { + this.setDataType(dataType); + return this; + } + + public ColDataType withArgumentsStringList(List argumentsStringList) { + this.setArgumentsStringList(argumentsStringList); + return this; + } + + public ColDataType withCharacterSet(String characterSet) { + this.setCharacterSet(characterSet); + return this; + } + + public ColDataType withArrayData(List arrayData) { + this.setArrayData(arrayData); + return this; + } + + public ColDataType addArgumentsStringList(String... argumentsStringList) { + List collection = + Optional.ofNullable(getArgumentsStringList()).orElseGet(ArrayList::new); + Collections.addAll(collection, argumentsStringList); + return this.withArgumentsStringList(collection); + } + + public ColDataType addArgumentsStringList(Collection argumentsStringList) { + List collection = + Optional.ofNullable(getArgumentsStringList()).orElseGet(ArrayList::new); + collection.addAll(argumentsStringList); + return this.withArgumentsStringList(collection); + } + + public ColDataType addArrayData(Integer... arrayData) { + List collection = Optional.ofNullable(getArrayData()).orElseGet(ArrayList::new); + Collections.addAll(collection, arrayData); + return this.withArrayData(collection); + } + + public ColDataType addArrayData(Collection arrayData) { + List collection = Optional.ofNullable(getArrayData()).orElseGet(ArrayList::new); + collection.addAll(arrayData); + return this.withArrayData(collection); + } + + @Override + public final boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ColDataType)) { + return false; + } + + ColDataType that = (ColDataType) o; + return dataType.equalsIgnoreCase(that.dataType) + && Objects.equals(argumentsStringList, that.argumentsStringList) + && Objects.equals(characterSet, that.characterSet) + && Objects.equals(arrayData, that.arrayData); + } + + @Override + public int hashCode() { + int result = dataType.hashCode(); + result = 31 * result + Objects.hashCode(argumentsStringList); + result = 31 * result + Objects.hashCode(characterSet); + result = 31 * result + Objects.hashCode(arrayData); + return result; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/create/table/ColumnDefinition.java b/src/main/java/net/sf/jsqlparser/statement/create/table/ColumnDefinition.java new file mode 100644 index 0000000..90a0e0e --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/create/table/ColumnDefinition.java @@ -0,0 +1,104 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.create.table; + +import net.sf.jsqlparser.statement.select.PlainSelect; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +/** + * Globally used definition class for columns. + */ +public class ColumnDefinition implements Serializable { + + private String columnName; + private ColDataType colDataType; + private List columnSpecs; + + public ColumnDefinition() {} + + public ColumnDefinition(String columnName, ColDataType colDataType) { + this.columnName = columnName; + this.colDataType = colDataType; + } + + public ColumnDefinition(String columnName, ColDataType colDataType, List columnSpecs) { + this(columnName, colDataType); + this.columnSpecs = columnSpecs; + } + + public List getColumnSpecs() { + return columnSpecs; + } + + public void setColumnSpecs(List list) { + columnSpecs = list; + } + + public ColDataType getColDataType() { + return colDataType; + } + + public void setColDataType(ColDataType type) { + colDataType = type; + } + + public String getColumnName() { + return columnName; + } + + public void setColumnName(String string) { + columnName = string; + } + + @Override + public String toString() { + return columnName + " " + toStringDataTypeAndSpec(); + } + + public String toStringDataTypeAndSpec() { + return (colDataType == null ? "" : colDataType) + + (columnSpecs != null && !columnSpecs.isEmpty() + ? " " + PlainSelect.getStringList(columnSpecs, false, false) + : ""); + } + + public ColumnDefinition withColumnName(String columnName) { + this.setColumnName(columnName); + return this; + } + + public ColumnDefinition withColDataType(ColDataType colDataType) { + this.setColDataType(colDataType); + return this; + } + + public ColumnDefinition withColumnSpecs(List columnSpecs) { + this.setColumnSpecs(columnSpecs); + return this; + } + + public ColumnDefinition addColumnSpecs(String... columnSpecs) { + List collection = Optional.ofNullable(getColumnSpecs()).orElseGet(ArrayList::new); + Collections.addAll(collection, columnSpecs); + return this.withColumnSpecs(collection); + } + + public ColumnDefinition addColumnSpecs(Collection columnSpecs) { + List collection = Optional.ofNullable(getColumnSpecs()).orElseGet(ArrayList::new); + collection.addAll(columnSpecs); + return this.withColumnSpecs(collection); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/create/table/CreateTable.java b/src/main/java/net/sf/jsqlparser/statement/create/table/CreateTable.java new file mode 100644 index 0000000..390c337 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/create/table/CreateTable.java @@ -0,0 +1,329 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.create.table; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import net.sf.jsqlparser.expression.SpannerInterleaveIn; +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.StatementVisitor; +import net.sf.jsqlparser.statement.select.PlainSelect; +import net.sf.jsqlparser.statement.select.Select; + +public class CreateTable implements Statement { + + private Table table; + private boolean unlogged = false; + private List createOptionsStrings; + private List tableOptionsStrings; + private List columnDefinitions; + private List columns; + private List indexes; + private Select select; + private Table likeTable; + private boolean selectParenthesis; + private boolean ifNotExists = false; + private boolean orReplace = false; + + private RowMovement rowMovement; + + private SpannerInterleaveIn interleaveIn = null; + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + public Table getTable() { + return table; + } + + public void setTable(Table table) { + this.table = table; + } + + public boolean isUnlogged() { + return unlogged; + } + + public void setUnlogged(boolean unlogged) { + this.unlogged = unlogged; + } + + /** + * @return a list of {@link ColumnDefinition}s of this table. + */ + public List getColumnDefinitions() { + return columnDefinitions; + } + + public void setColumnDefinitions(List list) { + columnDefinitions = list; + } + + public List getColumns() { + return this.columns; + } + + public void setColumns(List columns) { + this.columns = columns; + } + + /** + * @return a list of options (as simple strings) of this table definition, as ("TYPE", "=", + * "MYISAM") + */ + public List getTableOptionsStrings() { + return tableOptionsStrings; + } + + public void setTableOptionsStrings(List tableOptionsStrings) { + this.tableOptionsStrings = tableOptionsStrings; + } + + public List getCreateOptionsStrings() { + return createOptionsStrings; + } + + public void setCreateOptionsStrings(List createOptionsStrings) { + this.createOptionsStrings = createOptionsStrings; + } + + /** + * @return a list of {@link Index}es (for example "PRIMARY KEY") of this table.
+ * Indexes created with column definitions (as in mycol INT PRIMARY KEY) are not + * inserted into this list. + */ + public List getIndexes() { + return indexes; + } + + public void setIndexes(List list) { + indexes = list; + } + + public Select getSelect() { + return select; + } + + public void setSelect(Select select, boolean parenthesis) { + this.select = select; + this.selectParenthesis = parenthesis; + } + + public Table getLikeTable() { + return likeTable; + } + + public void setLikeTable(Table likeTable, boolean parenthesis) { + this.likeTable = likeTable; + this.selectParenthesis = parenthesis; + } + + public boolean isIfNotExists() { + return ifNotExists; + } + + public void setIfNotExists(boolean ifNotExists) { + this.ifNotExists = ifNotExists; + } + + public boolean isOrReplace() { + return orReplace; + } + + public void setOrReplace(boolean orReplace) { + this.orReplace = orReplace; + } + + public boolean isSelectParenthesis() { + return selectParenthesis; + } + + public void setSelectParenthesis(boolean selectParenthesis) { + this.selectParenthesis = selectParenthesis; + } + + public RowMovement getRowMovement() { + return rowMovement; + } + + public void setRowMovement(RowMovement rowMovement) { + this.rowMovement = rowMovement; + } + + @Override + @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"}) + public String toString() { + String sql; + String createOps = PlainSelect.getStringList(createOptionsStrings, false, false); + + sql = "CREATE " + (unlogged ? "UNLOGGED " : "") + + (!"".equals(createOps) ? createOps + " " : "") + + (orReplace ? "OR REPLACE " : "") + + "TABLE " + (ifNotExists ? "IF NOT EXISTS " : "") + table; + + if (columns != null && !columns.isEmpty()) { + sql += " "; + sql += PlainSelect.getStringList(columns, true, true); + } + if (columnDefinitions != null && !columnDefinitions.isEmpty()) { + sql += " ("; + + sql += PlainSelect.getStringList(columnDefinitions, true, false); + if (indexes != null && !indexes.isEmpty()) { + sql += ", "; + sql += PlainSelect.getStringList(indexes); + } + sql += ")"; + } + String options = PlainSelect.getStringList(tableOptionsStrings, false, false); + if (options != null && options.length() > 0) { + sql += " " + options; + } + + if (rowMovement != null) { + sql += " " + rowMovement.getMode().toString() + " ROW MOVEMENT"; + } + if (select != null) { + sql += " AS " + (selectParenthesis ? "(" : "") + select.toString() + + (selectParenthesis ? ")" : ""); + } + if (likeTable != null) { + sql += " LIKE " + (selectParenthesis ? "(" : "") + likeTable.toString() + + (selectParenthesis ? ")" : ""); + } + if (interleaveIn != null) { + sql += ", " + interleaveIn; + } + return sql; + } + + public CreateTable withTable(Table table) { + this.setTable(table); + return this; + } + + public CreateTable withUnlogged(boolean unlogged) { + this.setUnlogged(unlogged); + return this; + } + + public CreateTable withCreateOptionsStrings(List createOptionsStrings) { + this.setCreateOptionsStrings(createOptionsStrings); + return this; + } + + public CreateTable withSelectParenthesis(boolean selectParenthesis) { + this.setSelectParenthesis(selectParenthesis); + return this; + } + + public CreateTable withIfNotExists(boolean ifNotExists) { + this.setIfNotExists(ifNotExists); + return this; + } + + public CreateTable withRowMovement(RowMovement rowMovement) { + this.setRowMovement(rowMovement); + return this; + } + + public CreateTable withTableOptionsStrings(List tableOptionsStrings) { + this.setTableOptionsStrings(tableOptionsStrings); + return this; + } + + public CreateTable withColumnDefinitions(List columnDefinitions) { + this.setColumnDefinitions(columnDefinitions); + return this; + } + + public CreateTable withColumns(List columns) { + this.setColumns(columns); + return this; + } + + public CreateTable withIndexes(List indexes) { + this.setIndexes(indexes); + return this; + } + + public CreateTable addCreateOptionsStrings(String... createOptionsStrings) { + List collection = + Optional.ofNullable(getCreateOptionsStrings()).orElseGet(ArrayList::new); + Collections.addAll(collection, createOptionsStrings); + return this.withCreateOptionsStrings(collection); + } + + public CreateTable addCreateOptionsStrings(Collection createOptionsStrings) { + List collection = + Optional.ofNullable(getCreateOptionsStrings()).orElseGet(ArrayList::new); + collection.addAll(createOptionsStrings); + return this.withCreateOptionsStrings(collection); + } + + public CreateTable addColumnDefinitions(ColumnDefinition... columnDefinitions) { + List collection = + Optional.ofNullable(getColumnDefinitions()).orElseGet(ArrayList::new); + Collections.addAll(collection, columnDefinitions); + return this.withColumnDefinitions(collection); + } + + public CreateTable addColumnDefinitions( + Collection columnDefinitions) { + List collection = + Optional.ofNullable(getColumnDefinitions()).orElseGet(ArrayList::new); + collection.addAll(columnDefinitions); + return this.withColumnDefinitions(collection); + } + + public CreateTable addColumns(String... columns) { + List collection = Optional.ofNullable(getColumns()).orElseGet(ArrayList::new); + Collections.addAll(collection, columns); + return this.withColumns(collection); + } + + public CreateTable addColumns(Collection columns) { + List collection = Optional.ofNullable(getColumns()).orElseGet(ArrayList::new); + collection.addAll(columns); + return this.withColumns(collection); + } + + public CreateTable addIndexes(Index... indexes) { + List collection = Optional.ofNullable(getIndexes()).orElseGet(ArrayList::new); + Collections.addAll(collection, indexes); + return this.withIndexes(collection); + } + + public CreateTable addIndexes(Collection indexes) { + List collection = Optional.ofNullable(getIndexes()).orElseGet(ArrayList::new); + collection.addAll(indexes); + return this.withIndexes(collection); + } + + public SpannerInterleaveIn getSpannerInterleaveIn() { + return interleaveIn; + } + + public void setSpannerInterleaveIn(SpannerInterleaveIn spannerInterleaveIn) { + this.interleaveIn = spannerInterleaveIn; + } + + public CreateTable withSpannerInterleaveIn(SpannerInterleaveIn spannerInterleaveIn) { + this.interleaveIn = spannerInterleaveIn; + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/create/table/ExcludeConstraint.java b/src/main/java/net/sf/jsqlparser/statement/create/table/ExcludeConstraint.java new file mode 100644 index 0000000..0ec34dd --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/create/table/ExcludeConstraint.java @@ -0,0 +1,93 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.create.table; + +import java.util.Collection; +import java.util.List; + +import net.sf.jsqlparser.expression.Expression; + +public class ExcludeConstraint extends Index { + + private Expression expression; + + public Expression getExpression() { + return expression; + } + + public void setExpression(Expression expression) { + this.expression = expression; + } + + @Override + public String toString() { + StringBuilder exclusionStatement = new StringBuilder("EXCLUDE WHERE "); + exclusionStatement.append("("); + exclusionStatement.append(expression); + exclusionStatement.append(")"); + return exclusionStatement.toString(); + } + + public ExcludeConstraint withExpression(Expression expression) { + this.setExpression(expression); + return this; + } + + public E getExpression(Class type) { + return type.cast(getExpression()); + } + + @Override + public ExcludeConstraint withName(List name) { + return (ExcludeConstraint) super.withName(name); + } + + @Override + public ExcludeConstraint withType(String type) { + return (ExcludeConstraint) super.withType(type); + } + + @Override + public ExcludeConstraint withUsing(String using) { + return (ExcludeConstraint) super.withUsing(using); + } + + @Override + public ExcludeConstraint withColumnsNames(List list) { + return (ExcludeConstraint) super.withColumnsNames(list); + } + + @Override + public ExcludeConstraint withColumns(List columns) { + return (ExcludeConstraint) super.withColumns(columns); + } + + @Override + public ExcludeConstraint addColumns(ColumnParams... functionDeclarationParts) { + return (ExcludeConstraint) super.addColumns(functionDeclarationParts); + } + + @Override + public ExcludeConstraint addColumns( + Collection functionDeclarationParts) { + return (ExcludeConstraint) super.addColumns(functionDeclarationParts); + } + + @Override + public ExcludeConstraint withIndexSpec(List idxSpec) { + return (ExcludeConstraint) super.withIndexSpec(idxSpec); + } + + @Override + public ExcludeConstraint withName(String name) { + return (ExcludeConstraint) super.withName(name); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/statement/create/table/ForeignKeyIndex.java b/src/main/java/net/sf/jsqlparser/statement/create/table/ForeignKeyIndex.java new file mode 100644 index 0000000..eb9f20c --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/create/table/ForeignKeyIndex.java @@ -0,0 +1,207 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.create.table; + +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.ReferentialAction; +import net.sf.jsqlparser.statement.ReferentialAction.Action; +import net.sf.jsqlparser.statement.ReferentialAction.Type; +import net.sf.jsqlparser.statement.select.PlainSelect; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +public class ForeignKeyIndex extends NamedConstraint { + + private Table table; + private List referencedColumnNames; + private Set referentialActions = new LinkedHashSet<>(2); + + public Table getTable() { + return table; + } + + public void setTable(Table table) { + this.table = table; + } + + public List getReferencedColumnNames() { + return referencedColumnNames; + } + + public void setReferencedColumnNames(List referencedColumnNames) { + this.referencedColumnNames = referencedColumnNames; + } + + /** + * @param type + * @param action + */ + public void setReferentialAction(Type type, Action action) { + setReferentialAction(type, action, true); + } + + public ForeignKeyIndex withReferentialAction(Type type, Action action) { + setReferentialAction(type, action); + return this; + } + + /** + * @param type + */ + public void removeReferentialAction(Type type) { + setReferentialAction(type, null, false); + } + + /** + * @param type + * @return + */ + public ReferentialAction getReferentialAction(Type type) { + return referentialActions.stream().filter(ra -> type.equals(ra.getType())).findFirst() + .orElse(null); + } + + private void setReferentialAction(Type type, Action action, boolean set) { + ReferentialAction found = getReferentialAction(type); + if (set) { + if (found == null) { + referentialActions.add(new ReferentialAction(type, action)); + } else { + found.setAction(action); + } + } else if (found != null) { + referentialActions.remove(found); + } + } + + @Deprecated + public String getOnDeleteReferenceOption() { + ReferentialAction a = getReferentialAction(Type.DELETE); + return a == null ? null : a.getAction().getAction(); + } + + @Deprecated + public void setOnDeleteReferenceOption(String onDeleteReferenceOption) { + if (onDeleteReferenceOption == null) { + removeReferentialAction(Type.DELETE); + } else { + setReferentialAction(Type.DELETE, Action.from(onDeleteReferenceOption)); + } + } + + @Deprecated + public String getOnUpdateReferenceOption() { + ReferentialAction a = getReferentialAction(Type.UPDATE); + return a == null ? null : a.getAction().getAction(); + } + + @Deprecated + public void setOnUpdateReferenceOption(String onUpdateReferenceOption) { + if (onUpdateReferenceOption == null) { + removeReferentialAction(Type.UPDATE); + } else { + setReferentialAction(Type.UPDATE, Action.from(onUpdateReferenceOption)); + } + } + + @Override + public String toString() { + StringBuilder b = new StringBuilder(super.toString()).append(" REFERENCES ").append(table) + .append(PlainSelect.getStringList(getReferencedColumnNames(), true, true)); + referentialActions.forEach(b::append); + return b.toString(); + } + + public ForeignKeyIndex withTable(Table table) { + this.setTable(table); + return this; + } + + public ForeignKeyIndex withReferencedColumnNames(List referencedColumnNames) { + this.setReferencedColumnNames(referencedColumnNames); + return this; + } + + public ForeignKeyIndex withOnDeleteReferenceOption(String onDeleteReferenceOption) { + this.setOnDeleteReferenceOption(onDeleteReferenceOption); + return this; + } + + public ForeignKeyIndex withOnUpdateReferenceOption(String onUpdateReferenceOption) { + this.setOnUpdateReferenceOption(onUpdateReferenceOption); + return this; + } + + public ForeignKeyIndex addReferencedColumnNames(String... referencedColumnNames) { + List collection = + Optional.ofNullable(getReferencedColumnNames()).orElseGet(ArrayList::new); + Collections.addAll(collection, referencedColumnNames); + return this.withReferencedColumnNames(collection); + } + + public ForeignKeyIndex addReferencedColumnNames(Collection referencedColumnNames) { + List collection = + Optional.ofNullable(getReferencedColumnNames()).orElseGet(ArrayList::new); + collection.addAll(referencedColumnNames); + return this.withReferencedColumnNames(collection); + } + + @Override + public ForeignKeyIndex withType(String type) { + return (ForeignKeyIndex) super.withType(type); + } + + @Override + public ForeignKeyIndex withUsing(String using) { + return (ForeignKeyIndex) super.withUsing(using); + } + + @Override + public ForeignKeyIndex withName(List name) { + return (ForeignKeyIndex) super.withName(name); + } + + @Override + public ForeignKeyIndex withName(String name) { + return (ForeignKeyIndex) super.withName(name); + } + + @Override + public ForeignKeyIndex withColumnsNames(List list) { + return (ForeignKeyIndex) super.withColumnsNames(list); + } + + @Override + public ForeignKeyIndex withColumns(List columns) { + return (ForeignKeyIndex) super.withColumns(columns); + } + + @Override + public ForeignKeyIndex addColumns(ColumnParams... functionDeclarationParts) { + return (ForeignKeyIndex) super.addColumns(functionDeclarationParts); + } + + @Override + public ForeignKeyIndex addColumns(Collection functionDeclarationParts) { + return (ForeignKeyIndex) super.addColumns(functionDeclarationParts); + } + + @Override + public ForeignKeyIndex withIndexSpec(List idxSpec) { + return (ForeignKeyIndex) super.withIndexSpec(idxSpec); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/statement/create/table/Index.java b/src/main/java/net/sf/jsqlparser/statement/create/table/Index.java new file mode 100644 index 0000000..ec7a2d6 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/create/table/Index.java @@ -0,0 +1,205 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.create.table; + +import static java.util.stream.Collectors.toList; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import net.sf.jsqlparser.statement.select.PlainSelect; + +public class Index implements Serializable { + + private final List name = new ArrayList<>(); + private String type; + private String using; + private List columns; + private List idxSpec; + private String commentText; + + public List getColumnsNames() { + return columns.stream() + .map(col -> col.columnName) + .collect(toList()); + } + + public void setColumnsNames(List list) { + columns = list.stream().map(ColumnParams::new).collect(toList()); + } + + @Deprecated + public List getColumnWithParams() { + return getColumns(); + } + + @Deprecated + public void setColumnNamesWithParams(List list) { + setColumns(list); + } + + public List getColumns() { + return columns; + } + + public void setColumns(List columns) { + this.columns = columns; + } + + public Index withColumns(List columns) { + setColumns(columns); + return this; + } + + public Index addColumns(ColumnParams... functionDeclarationParts) { + List collection = Optional.ofNullable(getColumns()).orElseGet(ArrayList::new); + Collections.addAll(collection, functionDeclarationParts); + return this.withColumns(collection); + } + + public Index addColumns(Collection functionDeclarationParts) { + List collection = Optional.ofNullable(getColumns()).orElseGet(ArrayList::new); + collection.addAll(functionDeclarationParts); + return this.withColumns(collection); + } + + public String getName() { + return name.isEmpty() ? null : String.join(".", name); + } + + public void setName(String name) { + this.name.clear(); + this.name.add(name); + } + + public void setName(List name) { + this.name.clear(); + this.name.addAll(name); + } + + public List getNameParts() { + return Collections.unmodifiableList(name); + } + + public String getType() { + return type; + } + + public void setType(String string) { + type = string; + } + + public Index withColumnsNames(List list) { + setColumnsNames(list); + return this; + } + + public String getUsing() { + return using; + } + + /** + * In postgresql, the index type (Btree, GIST, etc.) is indicated with a USING clause. Please + * note that: Oracle - the type might be BITMAP, indicating a bitmap kind of index MySQL - the + * type might be FULLTEXT or SPATIAL + * + * @param using + */ + public void setUsing(String using) { + this.using = using; + } + + public List getIndexSpec() { + return idxSpec; + } + + public void setIndexSpec(List idxSpec) { + this.idxSpec = idxSpec; + } + + public Index withIndexSpec(List idxSpec) { + setIndexSpec(idxSpec); + return this; + } + + @Override + public String toString() { + String idxSpecText = PlainSelect.getStringList(idxSpec, false, false); + String head = (type != null ? type : "") + (!name.isEmpty() ? " " + getName() : ""); + String tail = PlainSelect.getStringList(columns, true, true) + + (!"".equals(idxSpecText) ? " " + idxSpecText : ""); + + if ("".equals(tail)) { + return head; + } + + return head + " " + tail; + } + + public Index withType(String type) { + this.setType(type); + return this; + } + + public Index withUsing(String using) { + this.setUsing(using); + return this; + } + + public Index withName(List name) { + this.setName(name); + return this; + } + + public Index withName(String name) { + this.setName(name); + return this; + } + + public String getCommentText() { + return commentText; + } + + public void setCommentText(String commentText) { + this.commentText = commentText; + } + + public static class ColumnParams implements Serializable { + public final String columnName; + public final List params; + + public ColumnParams(String columnName) { + this.columnName = columnName; + this.params = null; + } + + public ColumnParams(String columnName, List params) { + this.columnName = columnName; + this.params = params; + } + + public String getColumnName() { + return columnName; + } + + public List getParams() { + return params; + } + + @Override + public String toString() { + return columnName + (params != null ? " " + String.join(" ", params) : ""); + } + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/create/table/NamedConstraint.java b/src/main/java/net/sf/jsqlparser/statement/create/table/NamedConstraint.java new file mode 100644 index 0000000..026d566 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/create/table/NamedConstraint.java @@ -0,0 +1,73 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.create.table; + +import java.util.Collection; +import java.util.List; + +import net.sf.jsqlparser.statement.select.PlainSelect; + +public class NamedConstraint extends Index { + + @Override + public String toString() { + String idxSpecText = PlainSelect.getStringList(getIndexSpec(), false, false); + String head = getName() != null ? "CONSTRAINT " + getName() + " " : ""; + String tail = getType() + " " + PlainSelect.getStringList(getColumnsNames(), true, true) + + (!"".equals(idxSpecText) ? " " + idxSpecText : ""); + return head + tail; + } + + @Override + public NamedConstraint withName(List name) { + return (NamedConstraint) super.withName(name); + } + + @Override + public NamedConstraint withName(String name) { + return (NamedConstraint) super.withName(name); + } + + @Override + public NamedConstraint withType(String type) { + return (NamedConstraint) super.withType(type); + } + + @Override + public NamedConstraint withUsing(String using) { + return (NamedConstraint) super.withUsing(using); + } + + @Override + public NamedConstraint withColumnsNames(List list) { + return (NamedConstraint) super.withColumnsNames(list); + } + + @Override + public NamedConstraint withColumns(List columns) { + return (NamedConstraint) super.withColumns(columns); + } + + @Override + public NamedConstraint addColumns(ColumnParams... functionDeclarationParts) { + return (NamedConstraint) super.addColumns(functionDeclarationParts); + } + + @Override + public NamedConstraint addColumns(Collection functionDeclarationParts) { + return (NamedConstraint) super.addColumns(functionDeclarationParts); + } + + @Override + public NamedConstraint withIndexSpec(List idxSpec) { + return (NamedConstraint) super.withIndexSpec(idxSpec); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/statement/create/table/RowMovement.java b/src/main/java/net/sf/jsqlparser/statement/create/table/RowMovement.java new file mode 100644 index 0000000..cf6bbe2 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/create/table/RowMovement.java @@ -0,0 +1,34 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.create.table; + +import java.io.Serializable; + +/** + * Holds data for the {@code row_movement} clause: + * https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_7002.htm#i2204697 + */ +public class RowMovement implements Serializable { + + private RowMovementMode mode; + + public RowMovementMode getMode() { + return mode; + } + + public void setMode(RowMovementMode mode) { + this.mode = mode; + } + + public RowMovement withMode(RowMovementMode mode) { + this.setMode(mode); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/create/table/RowMovementMode.java b/src/main/java/net/sf/jsqlparser/statement/create/table/RowMovementMode.java new file mode 100644 index 0000000..9d6066f --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/create/table/RowMovementMode.java @@ -0,0 +1,19 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.create.table; + +public enum RowMovementMode { + ENABLE, DISABLE; + + public static RowMovementMode from(String mode) { + return Enum.valueOf(RowMovementMode.class, mode.toUpperCase()); + } +} + diff --git a/src/main/java/net/sf/jsqlparser/statement/create/view/AlterView.java b/src/main/java/net/sf/jsqlparser/statement/create/view/AlterView.java new file mode 100644 index 0000000..079f70b --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/create/view/AlterView.java @@ -0,0 +1,120 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.create.view; + +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.StatementVisitor; +import net.sf.jsqlparser.statement.select.PlainSelect; +import net.sf.jsqlparser.statement.select.Select; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +public class AlterView implements Statement { + + private Table view; + private Select select; + private boolean useReplace = false; + private List columnNames = null; + + public Table getView() { + return view; + } + + public void setView(Table view) { + this.view = view; + } + + public Select getSelect() { + return select; + } + + public void setSelect(Select select) { + this.select = select; + } + + public List getColumnNames() { + return columnNames; + } + + public void setColumnNames(List columnNames) { + this.columnNames = columnNames; + } + + public boolean isUseReplace() { + return useReplace; + } + + public void setUseReplace(boolean useReplace) { + this.useReplace = useReplace; + } + + @Override + public String toString() { + StringBuilder sql; + if (useReplace) { + sql = new StringBuilder("REPLACE "); + } else { + sql = new StringBuilder("ALTER "); + } + sql.append("VIEW "); + sql.append(view); + if (columnNames != null) { + sql.append(PlainSelect.getStringList(columnNames, true, true)); + } + sql.append(" AS ").append(select); + return sql.toString(); + } + + public AlterView withView(Table view) { + this.setView(view); + return this; + } + + public AlterView withSelect(Select select) { + this.setSelect(select); + return this; + } + + public AlterView withUseReplace(boolean useReplace) { + this.setUseReplace(useReplace); + return this; + } + + public AlterView withColumnNames(List columnNames) { + this.setColumnNames(columnNames); + return this; + } + + public AlterView addColumnNames(String... columnNames) { + List collection = Optional.ofNullable(getColumnNames()).orElseGet(ArrayList::new); + Collections.addAll(collection, columnNames); + return this.withColumnNames(collection); + } + + public AlterView addColumnNames(Collection columnNames) { + List collection = Optional.ofNullable(getColumnNames()).orElseGet(ArrayList::new); + collection.addAll(columnNames); + return this.withColumnNames(collection); + } + + public E getSelectBody(Class type) { + return type.cast(getSelect()); + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/create/view/AutoRefreshOption.java b/src/main/java/net/sf/jsqlparser/statement/create/view/AutoRefreshOption.java new file mode 100644 index 0000000..69e354d --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/create/view/AutoRefreshOption.java @@ -0,0 +1,18 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.create.view; + +public enum AutoRefreshOption { + NONE, YES, NO; + + public static AutoRefreshOption from(String option) { + return Enum.valueOf(AutoRefreshOption.class, option.toUpperCase()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/create/view/CreateView.java b/src/main/java/net/sf/jsqlparser/statement/create/view/CreateView.java new file mode 100644 index 0000000..0d76797 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/create/view/CreateView.java @@ -0,0 +1,229 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.create.view; + +import java.util.List; + +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.schema.Column; +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.StatementVisitor; +import net.sf.jsqlparser.statement.select.PlainSelect; +import net.sf.jsqlparser.statement.select.Select; + +public class CreateView implements Statement { + + private Table view; + private Select select; + private boolean orReplace = false; + private ExpressionList columnNames = null; + private boolean materialized = false; + private ForceOption force = ForceOption.NONE; + private boolean secure = false; + private TemporaryOption temp = TemporaryOption.NONE; + private AutoRefreshOption autoRefresh = AutoRefreshOption.NONE; + private boolean withReadOnly = false; + private boolean ifNotExists = false; + private List viewCommentOptions = null; + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + public Table getView() { + return view; + } + + public void setView(Table view) { + this.view = view; + } + + public boolean isOrReplace() { + return orReplace; + } + + /** + * @param orReplace was "OR REPLACE" specified? + */ + public void setOrReplace(boolean orReplace) { + this.orReplace = orReplace; + } + + public Select getSelect() { + return select; + } + + public void setSelect(Select select) { + this.select = select; + } + + public ExpressionList getColumnNames() { + return columnNames; + } + + public void setColumnNames(ExpressionList columnNames) { + this.columnNames = columnNames; + } + + public boolean isMaterialized() { + return materialized; + } + + public void setMaterialized(boolean materialized) { + this.materialized = materialized; + } + + public ForceOption getForce() { + return force; + } + + public void setForce(ForceOption force) { + this.force = force; + } + + public boolean isSecure() { + return secure; + } + + public void setSecure(boolean secure) { + this.secure = secure; + } + + public TemporaryOption getTemporary() { + return temp; + } + + public void setTemporary(TemporaryOption temp) { + this.temp = temp; + } + + public AutoRefreshOption getAutoRefresh() { + return autoRefresh; + } + + public void setAutoRefresh(AutoRefreshOption autoRefresh) { + this.autoRefresh = autoRefresh; + } + + public boolean isWithReadOnly() { + return withReadOnly; + } + + public void setWithReadOnly(boolean withReadOnly) { + this.withReadOnly = withReadOnly; + } + + public boolean isIfNotExists() { + return ifNotExists; + } + + public void setIfNotExists(boolean ifNotExists) { + this.ifNotExists = ifNotExists; + } + + @Override + public String toString() { + StringBuilder sql = new StringBuilder("CREATE "); + if (isOrReplace()) { + sql.append("OR REPLACE "); + } + appendForceOptionIfApplicable(sql); + if (secure) { + sql.append("SECURE "); + } + + if (temp != TemporaryOption.NONE) { + sql.append(temp.name()).append(" "); + } + + if (isMaterialized()) { + sql.append("MATERIALIZED "); + } + sql.append("VIEW "); + sql.append(view); + if (ifNotExists) { + sql.append(" IF NOT EXISTS"); + } + if (autoRefresh != AutoRefreshOption.NONE) { + sql.append(" AUTO REFRESH ").append(autoRefresh.name()); + } + if (columnNames != null) { + sql.append("("); + sql.append(columnNames); + sql.append(")"); + } + if (viewCommentOptions != null) { + sql.append(PlainSelect.getStringList(viewCommentOptions, false, false)); + } + sql.append(" AS ").append(select); + if (isWithReadOnly()) { + sql.append(" WITH READ ONLY"); + } + return sql.toString(); + } + + private void appendForceOptionIfApplicable(StringBuilder sql) { + switch (force) { + case FORCE: + sql.append("FORCE "); + break; + case NO_FORCE: + sql.append("NO FORCE "); + break; + default: + // nothing + } + } + + public CreateView withView(Table view) { + this.setView(view); + return this; + } + + public CreateView withSelect(Select select) { + this.setSelect(select); + return this; + } + + public CreateView withOrReplace(boolean orReplace) { + this.setOrReplace(orReplace); + return this; + } + + public CreateView withColumnNames(ExpressionList columnNames) { + this.setColumnNames(columnNames); + return this; + } + + public CreateView withMaterialized(boolean materialized) { + this.setMaterialized(materialized); + return this; + } + + public CreateView withForce(ForceOption force) { + this.setForce(force); + return this; + } + + public CreateView withWithReadOnly(boolean withReadOnly) { + this.setWithReadOnly(withReadOnly); + return this; + } + + public List getViewCommentOptions() { + return viewCommentOptions; + } + + public void setViewCommentOptions(List viewCommentOptions) { + this.viewCommentOptions = viewCommentOptions; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/create/view/ForceOption.java b/src/main/java/net/sf/jsqlparser/statement/create/view/ForceOption.java new file mode 100644 index 0000000..379690b --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/create/view/ForceOption.java @@ -0,0 +1,18 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.create.view; + +public enum ForceOption { + NONE, FORCE, NO_FORCE; + + public static ForceOption from(String option) { + return Enum.valueOf(ForceOption.class, option.toUpperCase()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/create/view/TemporaryOption.java b/src/main/java/net/sf/jsqlparser/statement/create/view/TemporaryOption.java new file mode 100644 index 0000000..c543af9 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/create/view/TemporaryOption.java @@ -0,0 +1,18 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.create.view; + +public enum TemporaryOption { + NONE, TEMP, TEMPORARY, VOLATILE; + + public static TemporaryOption from(String option) { + return Enum.valueOf(TemporaryOption.class, option.toUpperCase()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/delete/Delete.java b/src/main/java/net/sf/jsqlparser/statement/delete/Delete.java new file mode 100644 index 0000000..931d486 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/delete/Delete.java @@ -0,0 +1,389 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.delete; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.OracleHint; +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.OutputClause; +import net.sf.jsqlparser.statement.ReturningClause; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.StatementVisitor; +import net.sf.jsqlparser.statement.select.Join; +import net.sf.jsqlparser.statement.select.Limit; +import net.sf.jsqlparser.statement.select.OrderByElement; +import net.sf.jsqlparser.statement.select.PlainSelect; +import net.sf.jsqlparser.statement.select.WithItem; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; + +import static java.util.stream.Collectors.joining; + +public class Delete implements Statement { + + private List withItemsList; + private Table table; + private OracleHint oracleHint = null; + private List tables; + private List
usingList; + private List joins; + private Expression where; + private Limit limit; + private List orderByElements; + private boolean hasFrom = true; + private DeleteModifierPriority modifierPriority; + private boolean modifierIgnore; + private boolean modifierQuick; + + private ReturningClause returningClause; + private OutputClause outputClause; + + public OutputClause getOutputClause() { + return outputClause; + } + + public void setOutputClause(OutputClause outputClause) { + this.outputClause = outputClause; + } + + public ReturningClause getReturningClause() { + return returningClause; + } + + public Delete setReturningClause(ReturningClause returningClause) { + this.returningClause = returningClause; + return this; + } + + public List getWithItemsList() { + return withItemsList; + } + + public void setWithItemsList(List withItemsList) { + this.withItemsList = withItemsList; + } + + public Delete withWithItemsList(List withItemsList) { + this.setWithItemsList(withItemsList); + return this; + } + + public Delete addWithItemsList(WithItem... withItemsList) { + List collection = + Optional.ofNullable(getWithItemsList()).orElseGet(ArrayList::new); + Collections.addAll(collection, withItemsList); + return this.withWithItemsList(collection); + } + + public Delete addWithItemsList(Collection withItemsList) { + List collection = + Optional.ofNullable(getWithItemsList()).orElseGet(ArrayList::new); + collection.addAll(withItemsList); + return this.withWithItemsList(collection); + } + + public List getOrderByElements() { + return orderByElements; + } + + public void setOrderByElements(List orderByElements) { + this.orderByElements = orderByElements; + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + public Table getTable() { + return table; + } + + public void setTable(Table name) { + table = name; + } + + public Expression getWhere() { + return where; + } + + public void setWhere(Expression expression) { + where = expression; + } + + public OracleHint getOracleHint() { + return oracleHint; + } + + public void setOracleHint(OracleHint oracleHint) { + this.oracleHint = oracleHint; + } + + public Limit getLimit() { + return limit; + } + + public void setLimit(Limit limit) { + this.limit = limit; + } + + public List
getTables() { + return tables; + } + + public void setTables(List
tables) { + this.tables = tables; + } + + public List
getUsingList() { + return usingList; + } + + public void setUsingList(List
usingList) { + this.usingList = usingList; + } + + public List getJoins() { + return joins; + } + + public void setJoins(List joins) { + this.joins = joins; + } + + public boolean isHasFrom() { + return this.hasFrom; + } + + public void setHasFrom(boolean hasFrom) { + this.hasFrom = hasFrom; + } + + @Override + @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"}) + public String toString() { + StringBuilder b = new StringBuilder(); + if (withItemsList != null && !withItemsList.isEmpty()) { + b.append("WITH "); + for (Iterator iter = withItemsList.iterator(); iter.hasNext();) { + WithItem withItem = iter.next(); + b.append(withItem); + if (iter.hasNext()) { + b.append(","); + } + b.append(" "); + } + } + + b.append("DELETE"); + if (oracleHint != null) { + b.append(oracleHint).append(" "); + } + if (modifierPriority != null) { + b.append(" ").append(modifierPriority.name()); + } + if (modifierQuick) { + b.append(" QUICK"); + } + if (modifierIgnore) { + b.append(" IGNORE"); + } + + if (tables != null && tables.size() > 0) { + b.append(" "); + b.append(tables.stream() + .map(Table::toString) + .collect(joining(", "))); + } + + if (outputClause != null) { + outputClause.appendTo(b); + } + + + if (hasFrom) { + b.append(" FROM"); + } + b.append(" ").append(table); + + if (usingList != null && usingList.size() > 0) { + b.append(" USING "); + b.append(usingList.stream() + .map(Table::toString) + .collect(joining(", "))); + } + + if (joins != null) { + for (Join join : joins) { + if (join.isSimple()) { + b.append(", ").append(join); + } else { + b.append(" ").append(join); + } + } + } + + if (where != null) { + b.append(" WHERE ").append(where); + } + + if (orderByElements != null) { + b.append(PlainSelect.orderByToString(orderByElements)); + } + + if (limit != null) { + b.append(limit); + } + + if (returningClause != null) { + returningClause.appendTo(b); + } + + return b.toString(); + } + + public Delete withTables(List
tables) { + this.setTables(tables); + return this; + } + + public Delete withUsingList(List
usingList) { + this.setUsingList(usingList); + return this; + } + + public Delete withJoins(List joins) { + this.setJoins(joins); + return this; + } + + public Delete withLimit(Limit limit) { + this.setLimit(limit); + return this; + } + + public Delete withOrderByElements(List orderByElements) { + this.setOrderByElements(orderByElements); + return this; + } + + public Delete withTable(Table table) { + this.setTable(table); + return this; + } + + public Delete withWhere(Expression where) { + this.setWhere(where); + return this; + } + + public Delete withHasFrom(boolean hasFrom) { + this.setHasFrom(hasFrom); + return this; + } + + public Delete withModifierPriority(DeleteModifierPriority modifierPriority) { + this.setModifierPriority(modifierPriority); + return this; + } + + public Delete withModifierIgnore(boolean modifierIgnore) { + this.setModifierIgnore(modifierIgnore); + return this; + } + + public Delete withModifierQuick(boolean modifierQuick) { + this.setModifierQuick(modifierQuick); + return this; + } + + public DeleteModifierPriority getModifierPriority() { + return modifierPriority; + } + + public void setModifierPriority(DeleteModifierPriority modifierPriority) { + this.modifierPriority = modifierPriority; + } + + public boolean isModifierIgnore() { + return modifierIgnore; + } + + public void setModifierIgnore(boolean modifierIgnore) { + this.modifierIgnore = modifierIgnore; + } + + public boolean isModifierQuick() { + return modifierQuick; + } + + public void setModifierQuick(boolean modifierQuick) { + this.modifierQuick = modifierQuick; + } + + public Delete addTables(Table... tables) { + List
collection = Optional.ofNullable(getTables()).orElseGet(ArrayList::new); + Collections.addAll(collection, tables); + return this.withTables(collection); + } + + public Delete addTables(Collection tables) { + List
collection = Optional.ofNullable(getTables()).orElseGet(ArrayList::new); + collection.addAll(tables); + return this.withTables(collection); + } + + public Delete addUsingList(Table... usingList) { + List
collection = Optional.ofNullable(getUsingList()).orElseGet(ArrayList::new); + Collections.addAll(collection, usingList); + return this.withUsingList(collection); + } + + public Delete addUsingList(Collection usingList) { + List
collection = Optional.ofNullable(getUsingList()).orElseGet(ArrayList::new); + collection.addAll(usingList); + return this.withUsingList(collection); + } + + public Delete addJoins(Join... joins) { + List collection = Optional.ofNullable(getJoins()).orElseGet(ArrayList::new); + Collections.addAll(collection, joins); + return this.withJoins(collection); + } + + public Delete addJoins(Collection joins) { + List collection = Optional.ofNullable(getJoins()).orElseGet(ArrayList::new); + collection.addAll(joins); + return this.withJoins(collection); + } + + public Delete addOrderByElements(OrderByElement... orderByElements) { + List collection = + Optional.ofNullable(getOrderByElements()).orElseGet(ArrayList::new); + Collections.addAll(collection, orderByElements); + return this.withOrderByElements(collection); + } + + public Delete addOrderByElements(Collection orderByElements) { + List collection = + Optional.ofNullable(getOrderByElements()).orElseGet(ArrayList::new); + collection.addAll(orderByElements); + return this.withOrderByElements(collection); + } + + public E getWhere(Class type) { + return type.cast(getWhere()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/delete/DeleteModifierPriority.java b/src/main/java/net/sf/jsqlparser/statement/delete/DeleteModifierPriority.java new file mode 100644 index 0000000..f027722 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/delete/DeleteModifierPriority.java @@ -0,0 +1,18 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.delete; + +public enum DeleteModifierPriority { + LOW_PRIORITY; + + public static DeleteModifierPriority from(String priority) { + return Enum.valueOf(DeleteModifierPriority.class, priority.toUpperCase()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/drop/Drop.java b/src/main/java/net/sf/jsqlparser/statement/drop/Drop.java new file mode 100644 index 0000000..a7ac362 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/drop/Drop.java @@ -0,0 +1,168 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.drop; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.StatementVisitor; +import net.sf.jsqlparser.statement.select.PlainSelect; + +public class Drop implements Statement { + + private String type; + private Table name; + private List parameters; + private Map> typeToParameters = new HashMap<>(); + private boolean ifExists = false; + private boolean materialized = false; + + private boolean isUsingTemporary; + + public static String formatFuncParams(List params) { + if (params == null) { + return ""; + } + return params.isEmpty() ? "()" : PlainSelect.getStringList(params, true, true); + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + public Table getName() { + return name; + } + + public void setName(Table string) { + name = string; + } + + public List getParameters() { + return parameters; + } + + public void setParameters(List list) { + parameters = list; + } + + public String getType() { + return type; + } + + public void setType(String string) { + type = string; + } + + public boolean isIfExists() { + return ifExists; + } + + public void setIfExists(boolean ifExists) { + this.ifExists = ifExists; + } + + public boolean isUsingTemporary() { + return isUsingTemporary; + } + + public void setUsingTemporary(boolean useTemporary) { + this.isUsingTemporary = useTemporary; + } + + public Drop withUsingTemporary(boolean useTemporary) { + setUsingTemporary(useTemporary); + return this; + } + + public boolean isMaterialized() { + return materialized; + } + + public void setMaterialized(boolean materialized) { + this.materialized = materialized; + } + + public Map> getTypeToParameters() { + return typeToParameters; + } + + public void setTypeToParameters(Map> typeToParameters) { + this.typeToParameters = typeToParameters; + } + + @Override + public String toString() { + String sql = "DROP " + + (isUsingTemporary ? "TEMPORARY " : "") + + (materialized ? "MATERIALIZED " : "") + + type + " " + + (ifExists ? "IF EXISTS " : "") + name.toString(); + + if (type.equals("FUNCTION")) { + sql += formatFuncParams(getParamsByType("FUNCTION")); + } + + if (parameters != null && !parameters.isEmpty()) { + sql += " " + PlainSelect.getStringList(parameters, false, false); + } + + return sql; + } + + public List getParamsByType(String type) { + return typeToParameters.get(type); + } + + public Drop withIfExists(boolean ifExists) { + this.setIfExists(ifExists); + return this; + } + + public Drop withMaterialized(boolean materialized) { + this.setMaterialized(materialized); + return this; + } + + public Drop withType(String type) { + this.setType(type); + return this; + } + + public Drop withName(Table name) { + this.setName(name); + return this; + } + + public Drop withParameters(List parameters) { + this.setParameters(parameters); + return this; + } + + public Drop addParameters(String... parameters) { + List collection = Optional.ofNullable(getParameters()).orElseGet(ArrayList::new); + Collections.addAll(collection, parameters); + return this.withParameters(collection); + } + + public Drop addParameters(Collection parameters) { + List collection = Optional.ofNullable(getParameters()).orElseGet(ArrayList::new); + collection.addAll(parameters); + return this.withParameters(collection); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/execute/Execute.java b/src/main/java/net/sf/jsqlparser/statement/execute/Execute.java new file mode 100644 index 0000000..e0d5977 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/execute/Execute.java @@ -0,0 +1,101 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.execute; + +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.StatementVisitor; +import net.sf.jsqlparser.statement.select.PlainSelect; + +import java.util.List; + +public class Execute implements Statement { + + private ExecType execType = ExecType.EXECUTE; + private String name; + private ExpressionList exprList; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public void setName(List names) { + for (String item : names) { + if (this.name != null) { + this.name = this.name + "." + item; + } else { + this.name = item; + } + } + } + + public ExpressionList getExprList() { + return exprList; + } + + public void setExprList(ExpressionList exprList) { + this.exprList = exprList; + } + + public ExecType getExecType() { + return execType; + } + + public void setExecType(ExecType execType) { + this.execType = execType; + } + + @Deprecated + public boolean isParenthesis() { + return exprList instanceof ParenthesedExpressionList; + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + @Override + public String toString() { + return execType.name() + " " + name + + (exprList != null + ? " " + PlainSelect.getStringList(exprList, true, + exprList instanceof ParenthesedExpressionList) + : ""); + } + + public Execute withExecType(ExecType execType) { + this.setExecType(execType); + return this; + } + + public Execute withName(String name) { + this.setName(name); + return this; + } + + public Execute withExprList(ExpressionList exprList) { + this.setExprList(exprList); + return this; + } + + public enum ExecType { + EXECUTE, EXEC, CALL; + + public static ExecType from(String type) { + return Enum.valueOf(ExecType.class, type.toUpperCase()); + } + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/grant/Grant.java b/src/main/java/net/sf/jsqlparser/statement/grant/Grant.java new file mode 100644 index 0000000..ba46cb3 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/grant/Grant.java @@ -0,0 +1,155 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.grant; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import static java.util.stream.Collectors.joining; + +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.StatementVisitor; + +public class Grant implements Statement { + + private String role; + private List privileges; + private List objectName = new ArrayList<>(); + private List users; + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + public String getRole() { + return role; + } + + public void setRole(String role) { + this.role = role; + } + + public List getPrivileges() { + return privileges; + } + + public void setPrivileges(List privileges) { + this.privileges = privileges; + } + + public String getObjectName() { + return objectName.isEmpty() ? null + : objectName.stream() + .map(part -> part == null ? "" : part) + .collect(joining(".")); + } + + public void setObjectName(String objectName) { + this.objectName.clear(); + this.objectName.add(objectName); + } + + public void setObjectName(List objectName) { + this.objectName.clear(); + this.objectName.addAll(objectName); + } + + public List getObjectNameParts() { + return objectName; + } + + public List getUsers() { + return users; + } + + public void setUsers(List users) { + this.users = users; + } + + @Override + public String toString() { + StringBuilder buffer = new StringBuilder(); + + buffer.append("GRANT "); + if (role != null) { + buffer.append(role); + } else { + for (int i = 0; i < getPrivileges().size(); i++) { + if (i != 0) { + buffer.append(", "); + } + buffer.append(privileges.get(i)); + } + buffer.append(" ON "); + buffer.append(getObjectName()); + } + buffer.append(" TO "); + for (int i = 0; i < getUsers().size(); i++) { + if (i != 0) { + buffer.append(", "); + } + buffer.append(users.get(i)); + } + return buffer.toString(); + } + + public Grant withRole(String role) { + this.setRole(role); + return this; + } + + public Grant withPrivileges(List privileges) { + this.setPrivileges(privileges); + return this; + } + + public Grant withObjectName(String objectName) { + this.setObjectName(objectName); + return this; + } + + public Grant withObjectName(List objectName) { + this.setObjectName(objectName); + return this; + } + + public Grant withUsers(List users) { + this.setUsers(users); + return this; + } + + public Grant addPrivileges(String... privileges) { + List collection = Optional.ofNullable(getPrivileges()).orElseGet(ArrayList::new); + Collections.addAll(collection, privileges); + return this.withPrivileges(collection); + } + + public Grant addPrivileges(Collection privileges) { + List collection = Optional.ofNullable(getPrivileges()).orElseGet(ArrayList::new); + collection.addAll(privileges); + return this.withPrivileges(collection); + } + + public Grant addUsers(String... users) { + List collection = Optional.ofNullable(getUsers()).orElseGet(ArrayList::new); + Collections.addAll(collection, users); + return this.withUsers(collection); + } + + public Grant addUsers(Collection users) { + List collection = Optional.ofNullable(getUsers()).orElseGet(ArrayList::new); + collection.addAll(users); + return this.withUsers(collection); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/insert/ConflictActionType.java b/src/main/java/net/sf/jsqlparser/statement/insert/ConflictActionType.java new file mode 100644 index 0000000..69d6532 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/insert/ConflictActionType.java @@ -0,0 +1,18 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2022 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.insert; + +public enum ConflictActionType { + DO_NOTHING, DO_UPDATE; + + public static ConflictActionType from(String type) { + return Enum.valueOf(ConflictActionType.class, type.toUpperCase()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/insert/Insert.java b/src/main/java/net/sf/jsqlparser/statement/insert/Insert.java new file mode 100644 index 0000000..cdb16be --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/insert/Insert.java @@ -0,0 +1,319 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.insert; + +import net.sf.jsqlparser.expression.OracleHint; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.schema.Column; +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.OutputClause; +import net.sf.jsqlparser.statement.ReturningClause; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.StatementVisitor; +import net.sf.jsqlparser.statement.select.PlainSelect; +import net.sf.jsqlparser.statement.select.Select; +import net.sf.jsqlparser.statement.select.SetOperationList; +import net.sf.jsqlparser.statement.select.Values; +import net.sf.jsqlparser.statement.select.WithItem; +import net.sf.jsqlparser.statement.update.UpdateSet; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; + +@SuppressWarnings({"PMD.CyclomaticComplexity"}) +public class Insert implements Statement { + + private Table table; + private OracleHint oracleHint = null; + private ExpressionList columns; + private Select select; + private List duplicateUpdateSets = null; + private InsertModifierPriority modifierPriority = null; + private boolean modifierIgnore = false; + private ReturningClause returningClause; + private List setUpdateSets = null; + private List withItemsList; + private OutputClause outputClause; + private InsertConflictTarget conflictTarget; + private InsertConflictAction conflictAction; + + public List getDuplicateUpdateSets() { + return duplicateUpdateSets; + } + + public List getSetUpdateSets() { + return setUpdateSets; + } + + public Insert withDuplicateUpdateSets(List duplicateUpdateSets) { + this.duplicateUpdateSets = duplicateUpdateSets; + return this; + } + + public Insert withSetUpdateSets(List setUpdateSets) { + this.setUpdateSets = setUpdateSets; + return this; + } + + public OutputClause getOutputClause() { + return outputClause; + } + + public void setOutputClause(OutputClause outputClause) { + this.outputClause = outputClause; + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + public Table getTable() { + return table; + } + + public void setTable(Table name) { + table = name; + } + + public OracleHint getOracleHint() { + return oracleHint; + } + + public void setOracleHint(OracleHint oracleHint) { + this.oracleHint = oracleHint; + } + + public ExpressionList getColumns() { + return columns; + } + + public void setColumns(ExpressionList list) { + columns = list; + } + + @Deprecated + public boolean isUseValues() { + return select != null && select instanceof Values; + } + + public ReturningClause getReturningClause() { + return returningClause; + } + + public Insert setReturningClause(ReturningClause returningClause) { + this.returningClause = returningClause; + return this; + } + + public Select getSelect() { + return select; + } + + public void setSelect(Select select) { + this.select = select; + } + + public Values getValues() { + return select.getValues(); + } + + public PlainSelect getPlainSelect() { + return select.getPlainSelect(); + } + + public SetOperationList getSetOperationList() { + return select.getSetOperationList(); + } + + @Deprecated + public boolean isUseSelectBrackets() { + return false; + } + + @Deprecated + public boolean isUseDuplicate() { + return duplicateUpdateSets != null && !duplicateUpdateSets.isEmpty(); + } + + public InsertModifierPriority getModifierPriority() { + return modifierPriority; + } + + public void setModifierPriority(InsertModifierPriority modifierPriority) { + this.modifierPriority = modifierPriority; + } + + public boolean isModifierIgnore() { + return modifierIgnore; + } + + public void setModifierIgnore(boolean modifierIgnore) { + this.modifierIgnore = modifierIgnore; + } + + + @Deprecated + public boolean isUseSet() { + return setUpdateSets != null && !setUpdateSets.isEmpty(); + } + + public List getWithItemsList() { + return withItemsList; + } + + public void setWithItemsList(List withItemsList) { + this.withItemsList = withItemsList; + } + + public InsertConflictTarget getConflictTarget() { + return conflictTarget; + } + + public void setConflictTarget(InsertConflictTarget conflictTarget) { + this.conflictTarget = conflictTarget; + } + + public Insert withConflictTarget(InsertConflictTarget conflictTarget) { + setConflictTarget(conflictTarget); + return this; + } + + public InsertConflictAction getConflictAction() { + return conflictAction; + } + + public void setConflictAction(InsertConflictAction conflictAction) { + this.conflictAction = conflictAction; + } + + public Insert withConflictAction(InsertConflictAction conflictAction) { + setConflictAction(conflictAction); + return this; + } + + @Override + @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"}) + public String toString() { + StringBuilder sql = new StringBuilder(); + if (withItemsList != null && !withItemsList.isEmpty()) { + sql.append("WITH "); + for (Iterator iter = withItemsList.iterator(); iter.hasNext();) { + WithItem withItem = iter.next(); + sql.append(withItem); + if (iter.hasNext()) { + sql.append(","); + } + sql.append(" "); + } + } + sql.append("INSERT "); + if (oracleHint != null) { + sql.append(oracleHint).append(" "); + } + if (modifierPriority != null) { + sql.append(modifierPriority.name()).append(" "); + } + if (modifierIgnore) { + sql.append("IGNORE "); + } + sql.append("INTO "); + sql.append(table).append(" "); + + if (columns != null) { + sql.append("("); + for (int i = 0; i < columns.size(); i++) { + if (i > 0) { + sql.append(", "); + } + // only plain names, but not fully qualified names allowed + sql.append(columns.get(i).getColumnName()); + } + sql.append(") "); + } + + if (outputClause != null) { + sql.append(outputClause); + } + + if (select != null) { + sql.append(select); + } + + if (setUpdateSets != null && !setUpdateSets.isEmpty()) { + sql.append("SET "); + sql = UpdateSet.appendUpdateSetsTo(sql, setUpdateSets); + } + + if (duplicateUpdateSets != null && !duplicateUpdateSets.isEmpty()) { + sql.append(" ON DUPLICATE KEY UPDATE "); + sql = UpdateSet.appendUpdateSetsTo(sql, duplicateUpdateSets); + } + + if (conflictAction != null) { + sql.append(" ON CONFLICT"); + + if (conflictTarget != null) { + conflictTarget.appendTo(sql); + } + conflictAction.appendTo(sql); + } + + if (returningClause != null) { + returningClause.appendTo(sql); + } + + return sql.toString(); + } + + public Insert withWithItemsList(List withList) { + this.withItemsList = withList; + return this; + } + + public Insert withSelect(Select select) { + this.setSelect(select); + return this; + } + + public Insert withModifierPriority(InsertModifierPriority modifierPriority) { + this.setModifierPriority(modifierPriority); + return this; + } + + public Insert withModifierIgnore(boolean modifierIgnore) { + this.setModifierIgnore(modifierIgnore); + return this; + } + + public Insert withTable(Table table) { + this.setTable(table); + return this; + } + + public Insert withColumns(ExpressionList columns) { + this.setColumns(columns); + return this; + } + + public Insert addColumns(Column... columns) { + return addColumns(Arrays.asList(columns)); + } + + public Insert addColumns(Collection columns) { + ExpressionList collection = + Optional.ofNullable(getColumns()).orElseGet(ExpressionList::new); + collection.addAll(columns); + return this.withColumns(collection); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/insert/InsertConflictAction.java b/src/main/java/net/sf/jsqlparser/statement/insert/InsertConflictAction.java new file mode 100644 index 0000000..76781bb --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/insert/InsertConflictAction.java @@ -0,0 +1,126 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2022 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.insert; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.schema.Column; +import net.sf.jsqlparser.statement.update.UpdateSet; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +/** + * https://www.postgresql.org/docs/current/sql-insert.html + * + *
+ * conflict_action is one of:
+ *
+ *     DO NOTHING
+ *     DO UPDATE SET { column_name = { expression | DEFAULT } |
+ *                     ( column_name [, ...] ) = [ ROW ] ( { expression | DEFAULT } [, ...] ) |
+ *                     ( column_name [, ...] ) = ( sub-SELECT )
+ *                   } [, ...]
+ *               [ WHERE condition ]
+ * 
+ */ + +public class InsertConflictAction implements Serializable { + ConflictActionType conflictActionType; + Expression whereExpression; + private List updateSets; + + public InsertConflictAction(ConflictActionType conflictActionType) { + this.conflictActionType = Objects.requireNonNull(conflictActionType, + "The Conflict Action Type is mandatory and must not be Null."); + } + + public List getUpdateSets() { + return updateSets; + } + + public void setUpdateSets(List updateSets) { + this.updateSets = updateSets; + } + + public InsertConflictAction withUpdateSets(List updateSets) { + this.setUpdateSets(updateSets); + return this; + } + + public ConflictActionType getConflictActionType() { + return conflictActionType; + } + + public void setConflictActionType(ConflictActionType conflictActionType) { + this.conflictActionType = Objects.requireNonNull(conflictActionType, + "The Conflict Action Type is mandatory and must not be Null."); + } + + public InsertConflictAction withConflictActionType(ConflictActionType conflictActionType) { + setConflictActionType(conflictActionType); + return this; + } + + public InsertConflictAction addUpdateSet(Column column, Expression expression) { + return this.addUpdateSet(new UpdateSet()); + } + + public InsertConflictAction addUpdateSet(UpdateSet updateSet) { + if (updateSets == null) { + updateSets = new ArrayList<>(); + } + this.updateSets.add(updateSet); + return this; + } + + public InsertConflictAction withUpdateSets(Collection updateSets) { + this.setUpdateSets(new ArrayList<>(updateSets)); + return this; + } + + public Expression getWhereExpression() { + return whereExpression; + } + + public void setWhereExpression(Expression whereExpression) { + this.whereExpression = whereExpression; + } + + public InsertConflictAction withWhereExpression(Expression whereExpression) { + setWhereExpression(whereExpression); + return this; + } + + @SuppressWarnings("PMD.SwitchStmtsShouldHaveDefault") + public StringBuilder appendTo(StringBuilder builder) { + switch (conflictActionType) { + case DO_NOTHING: + builder.append(" DO NOTHING"); + break; + case DO_UPDATE: + builder.append(" DO UPDATE SET "); + UpdateSet.appendUpdateSetsTo(builder, updateSets); + + if (whereExpression != null) { + builder.append(" WHERE ").append(whereExpression); + } + break; + } + return builder; + } + + @Override + public String toString() { + return appendTo(new StringBuilder()).toString(); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/insert/InsertConflictTarget.java b/src/main/java/net/sf/jsqlparser/statement/insert/InsertConflictTarget.java new file mode 100644 index 0000000..59c2d62 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/insert/InsertConflictTarget.java @@ -0,0 +1,156 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2022 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.insert; + +import net.sf.jsqlparser.expression.Expression; + +import java.io.Serializable; +import java.util.*; + +/** + * https://www.postgresql.org/docs/current/sql-insert.html + * + *
+ * conflict_target can be one of:
+ *
+ *     ( { index_column_name | ( index_expression ) } [ COLLATE collation ] [ opclass ] [, ...] ) [ WHERE index_predicate ]
+ *     ON CONSTRAINT constraint_name
+ * 
+ *

+ * Currently, COLLATE is not supported yet. + */ +public class InsertConflictTarget implements Serializable { + + ArrayList indexColumnNames = new ArrayList<>(); + Expression indexExpression; + Expression whereExpression; + String constraintName; + + public InsertConflictTarget(String indexColumnName, Expression indexExpression, + Expression whereExpression, String constraintName) { + this.indexColumnNames.add(indexColumnName); + this.indexExpression = indexExpression; + + this.whereExpression = whereExpression; + this.constraintName = constraintName; + } + + public InsertConflictTarget(Collection indexColumnName, Expression indexExpression, + Expression whereExpression, String constraintName) { + this.indexColumnNames.addAll(indexColumnName); + this.indexExpression = indexExpression; + + this.whereExpression = whereExpression; + this.constraintName = constraintName; + } + + public List getIndexColumnNames() { + return indexColumnNames; + } + + @Deprecated + public String getIndexColumnName() { + return indexColumnNames.isEmpty() ? null : indexColumnNames.get(0); + } + + public String getIndexColumnName(int index) { + return indexColumnNames.size() > index ? indexColumnNames.get(index) : null; + } + + public boolean addIndexColumnName(String indexColumnName) { + this.indexExpression = null; + return this.indexColumnNames.add(indexColumnName); + } + + public InsertConflictTarget withIndexColumnName(String indexColumnName) { + this.indexExpression = null; + this.indexColumnNames.add(indexColumnName); + return this; + } + + public boolean addAllIndexColumnNames(Collection indexColumnName) { + this.indexExpression = null; + return this.indexColumnNames.addAll(indexColumnName); + } + + + public Expression getIndexExpression() { + return indexExpression; + } + + public void setIndexExpression(Expression indexExpression) { + this.indexExpression = indexExpression; + this.indexColumnNames.clear(); + } + + public InsertConflictTarget withIndexExpression(Expression indexExpression) { + setIndexExpression(indexExpression); + return this; + } + + public Expression getWhereExpression() { + return whereExpression; + } + + public void setWhereExpression(Expression whereExpression) { + this.whereExpression = whereExpression; + } + + public InsertConflictTarget withWhereExpression(Expression whereExpression) { + setWhereExpression(whereExpression); + return this; + } + + public String getConstraintName() { + return constraintName; + } + + public void setConstraintName(String constraintName) { + this.constraintName = constraintName; + } + + public InsertConflictTarget withConstraintName(String constraintName) { + setConstraintName(constraintName); + return this; + } + + public StringBuilder appendTo(StringBuilder builder) { + if (constraintName == null) { + builder.append(" ( "); + + // @todo: Index Expression is not supported yet + if (!indexColumnNames.isEmpty()) { + boolean insertComma = false; + for (String s : indexColumnNames) { + builder.append(insertComma ? ", " : " ").append(s); + insertComma |= true; + } + } else { + builder.append(" ( ").append(indexExpression).append(" )"); + } + builder.append(" "); + + // @todo: Collate is not supported yet + + builder.append(") "); + + if (whereExpression != null) { + builder.append(" WHERE ").append(whereExpression); + } + } else { + builder.append(" ON CONSTRAINT ").append(constraintName); + } + return builder; + } + + public String toString() { + return appendTo(new StringBuilder()).toString(); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/insert/InsertModifierPriority.java b/src/main/java/net/sf/jsqlparser/statement/insert/InsertModifierPriority.java new file mode 100644 index 0000000..1e13289 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/insert/InsertModifierPriority.java @@ -0,0 +1,18 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.insert; + +public enum InsertModifierPriority { + LOW_PRIORITY, DELAYED, HIGH_PRIORITY, IGNORE; + + public final static InsertModifierPriority from(String priority) { + return Enum.valueOf(InsertModifierPriority.class, priority.toUpperCase()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/merge/Merge.java b/src/main/java/net/sf/jsqlparser/statement/merge/Merge.java new file mode 100644 index 0000000..689bd5d --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/merge/Merge.java @@ -0,0 +1,300 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.merge; + +import net.sf.jsqlparser.expression.Alias; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.OracleHint; +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.OutputClause; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.StatementVisitor; +import net.sf.jsqlparser.statement.select.FromItem; +import net.sf.jsqlparser.statement.select.ParenthesedSelect; +import net.sf.jsqlparser.statement.select.WithItem; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +public class Merge implements Statement { + + private List withItemsList; + private Table table; + private OracleHint oracleHint = null; + private FromItem fromItem; + private Expression onCondition; + private MergeInsert mergeInsert; + private MergeUpdate mergeUpdate; + private boolean insertFirst = false; + private List operations; + + private OutputClause outputClause; + + private void deriveOperationsFromStandardClauses() { + List operations = new ArrayList<>(); + if (insertFirst) { + Optional.ofNullable(mergeInsert).ifPresent(operations::add); + Optional.ofNullable(mergeUpdate).ifPresent(operations::add); + } else { + Optional.ofNullable(mergeUpdate).ifPresent(operations::add); + Optional.ofNullable(mergeInsert).ifPresent(operations::add); + } + this.operations = operations; + } + + private void deriveStandardClausesFromOperations() { + List applicableOperations = + Optional.ofNullable(operations).orElse(Collections.emptyList()).stream() + .filter(o -> o instanceof MergeUpdate || o instanceof MergeInsert) + .collect(Collectors.toList()); + mergeUpdate = applicableOperations.stream() + .filter(o -> o instanceof MergeUpdate) + .map(MergeUpdate.class::cast) + .findFirst() + .orElse(null); + mergeInsert = applicableOperations.stream() + .filter(o -> o instanceof MergeInsert) + .map(MergeInsert.class::cast) + .findFirst() + .orElse(null); + insertFirst = applicableOperations.stream() + .findFirst() + .map(o -> o instanceof MergeInsert) + .orElse(false); + } + + public List getWithItemsList() { + return withItemsList; + } + + public void setWithItemsList(List withItemsList) { + this.withItemsList = withItemsList; + } + + public Merge withWithItemsList(List withItemsList) { + this.setWithItemsList(withItemsList); + return this; + } + + public Merge addWithItemsList(WithItem... withItemsList) { + List collection = + Optional.ofNullable(getWithItemsList()).orElseGet(ArrayList::new); + Collections.addAll(collection, withItemsList); + return this.withWithItemsList(collection); + } + + public Merge addWithItemsList(Collection withItemsList) { + List collection = + Optional.ofNullable(getWithItemsList()).orElseGet(ArrayList::new); + collection.addAll(withItemsList); + return this.withWithItemsList(collection); + } + + public Table getTable() { + return table; + } + + public void setTable(Table name) { + table = name; + } + + public OracleHint getOracleHint() { + return oracleHint; + } + + public void setOracleHint(OracleHint oracleHint) { + this.oracleHint = oracleHint; + } + + @Deprecated + public Table getUsingTable() { + return fromItem instanceof Table ? (Table) fromItem : null; + } + + @Deprecated + public void setUsingTable(Table usingTable) { + this.fromItem = usingTable; + } + + @Deprecated + public void setUsingSelect(ParenthesedSelect usingSelect) { + this.fromItem = usingSelect; + } + + @Deprecated + public Alias getUsingAlias() { + return fromItem.getAlias(); + } + + @Deprecated + public void setUsingAlias(Alias usingAlias) { + this.fromItem.setAlias(usingAlias); + } + + public FromItem getFromItem() { + return fromItem; + } + + public void setFromItem(FromItem fromItem) { + this.fromItem = fromItem; + } + + public Merge withFromItem(FromItem fromItem) { + this.setFromItem(fromItem); + return this; + } + + public Expression getOnCondition() { + return onCondition; + } + + public void setOnCondition(Expression onCondition) { + this.onCondition = onCondition; + } + + public List getOperations() { + return operations; + } + + public void setOperations(List operations) { + this.operations = operations; + deriveStandardClausesFromOperations(); + } + + public MergeInsert getMergeInsert() { + return mergeInsert; + } + + public void setMergeInsert(MergeInsert mergeInsert) { + this.mergeInsert = mergeInsert; + deriveOperationsFromStandardClauses(); + } + + public MergeUpdate getMergeUpdate() { + return mergeUpdate; + } + + public void setMergeUpdate(MergeUpdate mergeUpdate) { + this.mergeUpdate = mergeUpdate; + deriveOperationsFromStandardClauses(); + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + public boolean isInsertFirst() { + return insertFirst; + } + + public void setInsertFirst(boolean insertFirst) { + this.insertFirst = insertFirst; + deriveOperationsFromStandardClauses(); + } + + public OutputClause getOutputClause() { + return outputClause; + } + + public Merge setOutputClause(OutputClause outputClause) { + this.outputClause = outputClause; + return this; + } + + @Override + @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"}) + public String toString() { + StringBuilder b = new StringBuilder(); + if (withItemsList != null && !withItemsList.isEmpty()) { + b.append("WITH "); + for (Iterator iter = withItemsList.iterator(); iter.hasNext();) { + WithItem withItem = iter.next(); + b.append(withItem); + if (iter.hasNext()) { + b.append(","); + } + b.append(" "); + } + } + b.append("MERGE "); + if (oracleHint != null) { + b.append(oracleHint).append(" "); + } + b.append("INTO "); + b.append(table); + b.append(" USING "); + b.append(fromItem); + b.append(" ON "); + b.append(onCondition); + + if (operations != null && !operations.isEmpty()) { + operations.forEach(b::append); + } + + if (outputClause != null) { + b.append(outputClause); + } + + return b.toString(); + } + + @Deprecated + public Merge withUsingTable(Table usingTable) { + this.setUsingTable(usingTable); + return this; + } + + @Deprecated + public Merge withUsingSelect(ParenthesedSelect usingSelect) { + this.setUsingSelect(usingSelect); + return this; + } + + @Deprecated + public Merge withUsingAlias(Alias usingAlias) { + this.setUsingAlias(usingAlias); + return this; + } + + public Merge withOnCondition(Expression onCondition) { + this.setOnCondition(onCondition); + return this; + } + + public Merge withMergeUpdate(MergeUpdate mergeUpdate) { + this.setMergeUpdate(mergeUpdate); + return this; + } + + public Merge withInsertFirst(boolean insertFirst) { + this.setInsertFirst(insertFirst); + return this; + } + + public Merge withTable(Table table) { + this.setTable(table); + return this; + } + + public Merge withMergeInsert(MergeInsert mergeInsert) { + this.setMergeInsert(mergeInsert); + return this; + } + + public E getOnCondition(Class type) { + return type.cast(getOnCondition()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/merge/MergeDelete.java b/src/main/java/net/sf/jsqlparser/statement/merge/MergeDelete.java new file mode 100644 index 0000000..bfacef1 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/merge/MergeDelete.java @@ -0,0 +1,47 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2024 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.merge; + +import net.sf.jsqlparser.expression.Expression; + +import java.io.Serializable; + +public class MergeDelete implements Serializable, MergeOperation { + private Expression andPredicate; + + public Expression getAndPredicate() { + return andPredicate; + } + + public void setAndPredicate(Expression andPredicate) { + this.andPredicate = andPredicate; + } + + public MergeDelete withAndPredicate(Expression andPredicate) { + this.setAndPredicate(andPredicate); + return this; + } + + @Override + public T accept(MergeOperationVisitor mergeOperationVisitor, S context) { + return mergeOperationVisitor.visit(this, context); + } + + @Override + public String toString() { + StringBuilder b = new StringBuilder(); + b.append(" WHEN MATCHED"); + if (andPredicate != null) { + b.append(" AND ").append(andPredicate.toString()); + } + b.append(" THEN DELETE"); + return b.toString(); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/merge/MergeInsert.java b/src/main/java/net/sf/jsqlparser/statement/merge/MergeInsert.java new file mode 100644 index 0000000..be0d4bb --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/merge/MergeInsert.java @@ -0,0 +1,132 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.merge; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.schema.Column; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collection; +import java.util.Optional; + +public class MergeInsert implements Serializable, MergeOperation { + + private Expression andPredicate; + private ExpressionList columns; + private ExpressionList values; + private Expression whereCondition; + + public Expression getAndPredicate() { + return andPredicate; + } + + public void setAndPredicate(Expression andPredicate) { + this.andPredicate = andPredicate; + } + + public ExpressionList getColumns() { + return columns; + } + + public void setColumns(ExpressionList columns) { + this.columns = columns; + } + + public ExpressionList getValues() { + return values; + } + + public void setValues(ExpressionList values) { + this.values = values; + } + + public Expression getWhereCondition() { + return whereCondition; + } + + public void setWhereCondition(Expression whereCondition) { + this.whereCondition = whereCondition; + } + + @Override + public T accept(MergeOperationVisitor mergeOperationVisitor, S context) { + return mergeOperationVisitor.visit(this, context); + } + + @Override + public String toString() { + StringBuilder b = new StringBuilder(); + b.append(" WHEN NOT MATCHED"); + if (andPredicate != null) { + b.append(" AND ").append(andPredicate.toString()); + } + b.append(" THEN INSERT "); + if (columns != null) { + b.append(columns.toString()); + } + b.append(" VALUES ").append(values.toString()); + if (whereCondition != null) { + b.append(" WHERE ").append(whereCondition.toString()); + } + return b.toString(); + } + + public MergeInsert withAndPredicate(Expression andPredicate) { + this.setAndPredicate(andPredicate); + return this; + } + + public MergeInsert withColumns(ExpressionList columns) { + this.setColumns(columns); + return this; + } + + public MergeInsert withValues(ExpressionList values) { + this.setValues(values); + return this; + } + + public MergeInsert addColumns(Column... columns) { + return this.addColumns(Arrays.asList(columns)); + } + + public MergeInsert addColumns(Collection columns) { + ExpressionList collection = + Optional.ofNullable(getColumns()).orElseGet(ExpressionList::new); + collection.addAll(columns); + return this.withColumns(collection); + } + + public MergeInsert addValues(Expression... values) { + return this.addValues(Arrays.asList(values)); + } + + public MergeInsert addValues(Collection values) { + ExpressionList collection = + Optional.ofNullable(getValues()).orElseGet(ExpressionList::new); + collection.addAll(values); + return this.withValues(collection); + } + + public MergeInsert withWhereCondition(Expression whereCondition) { + this.setWhereCondition(whereCondition); + return this; + } + + public E getAndPredicate(Class type) { + return type.cast(getAndPredicate()); + } + + public E getWhereCondition(Class type) { + return type.cast(getWhereCondition()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/merge/MergeOperation.java b/src/main/java/net/sf/jsqlparser/statement/merge/MergeOperation.java new file mode 100644 index 0000000..71447d2 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/merge/MergeOperation.java @@ -0,0 +1,17 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2024 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.merge; + +/** + * Marker interface to cover {@link MergeDelete}, {@link MergeUpdate} and {@link MergeInsert} + */ +public interface MergeOperation { + T accept(MergeOperationVisitor mergeOperationVisitor, S context); +} diff --git a/src/main/java/net/sf/jsqlparser/statement/merge/MergeOperationVisitor.java b/src/main/java/net/sf/jsqlparser/statement/merge/MergeOperationVisitor.java new file mode 100644 index 0000000..fef9682 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/merge/MergeOperationVisitor.java @@ -0,0 +1,19 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2024 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.merge; + +public interface MergeOperationVisitor { + + T visit(MergeDelete mergeDelete, S context); + + T visit(MergeUpdate mergeUpdate, S context); + + T visit(MergeInsert mergeInsert, S context); +} diff --git a/src/main/java/net/sf/jsqlparser/statement/merge/MergeOperationVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/statement/merge/MergeOperationVisitorAdapter.java new file mode 100644 index 0000000..546b446 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/merge/MergeOperationVisitorAdapter.java @@ -0,0 +1,28 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2024 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.merge; + +@SuppressWarnings({"PMD.UncommentedEmptyMethodBody"}) +public class MergeOperationVisitorAdapter implements MergeOperationVisitor { + @Override + public T visit(MergeDelete mergeDelete, S context) { + return null; + } + + @Override + public T visit(MergeUpdate mergeUpdate, S context) { + return null; + } + + @Override + public T visit(MergeInsert mergeInsert, S context) { + return null; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/merge/MergeUpdate.java b/src/main/java/net/sf/jsqlparser/statement/merge/MergeUpdate.java new file mode 100644 index 0000000..2cae3e1 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/merge/MergeUpdate.java @@ -0,0 +1,114 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.merge; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.statement.update.UpdateSet; + +import java.io.Serializable; +import java.util.List; + +public class MergeUpdate implements Serializable, MergeOperation { + + private List updateSets; + private Expression andPredicate; + private Expression whereCondition; + private Expression deleteWhereCondition; + + public MergeUpdate() {} + + public MergeUpdate(List updateSets) { + this.updateSets = updateSets; + } + + public List getUpdateSets() { + return updateSets; + } + + public MergeUpdate setUpdateSets(List updateSets) { + this.updateSets = updateSets; + return this; + } + + public Expression getAndPredicate() { + return andPredicate; + } + + public void setAndPredicate(Expression andPredicate) { + this.andPredicate = andPredicate; + } + + public Expression getWhereCondition() { + return whereCondition; + } + + public void setWhereCondition(Expression whereCondition) { + this.whereCondition = whereCondition; + } + + public Expression getDeleteWhereCondition() { + return deleteWhereCondition; + } + + public void setDeleteWhereCondition(Expression deleteWhereCondition) { + this.deleteWhereCondition = deleteWhereCondition; + } + + @Override + public T accept(MergeOperationVisitor mergeOperationVisitor, S context) { + return mergeOperationVisitor.visit(this, context); + } + + @Override + public String toString() { + StringBuilder b = new StringBuilder(); + b.append(" WHEN MATCHED"); + if (andPredicate != null) { + b.append(" AND ").append(andPredicate.toString()); + } + b.append(" THEN UPDATE SET "); + UpdateSet.appendUpdateSetsTo(b, updateSets); + + if (whereCondition != null) { + b.append(" WHERE ").append(whereCondition.toString()); + } + if (deleteWhereCondition != null) { + b.append(" DELETE WHERE ").append(deleteWhereCondition.toString()); + } + return b.toString(); + } + + public MergeUpdate withAndPredicate(Expression andPredicate) { + this.setAndPredicate(andPredicate); + return this; + } + + public MergeUpdate withWhereCondition(Expression whereCondition) { + this.setWhereCondition(whereCondition); + return this; + } + + public MergeUpdate withDeleteWhereCondition(Expression deleteWhereCondition) { + this.setDeleteWhereCondition(deleteWhereCondition); + return this; + } + + public E getAndPredicate(Class type) { + return type.cast(getAndPredicate()); + } + + public E getWhereCondition(Class type) { + return type.cast(getWhereCondition()); + } + + public E getDeleteWhereCondition(Class type) { + return type.cast(getDeleteWhereCondition()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/refresh/RefreshMaterializedViewStatement.java b/src/main/java/net/sf/jsqlparser/statement/refresh/RefreshMaterializedViewStatement.java new file mode 100644 index 0000000..6519cfe --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/refresh/RefreshMaterializedViewStatement.java @@ -0,0 +1,107 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.refresh; + +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.StatementVisitor; + +/** + * REFRESH MATERIALIZED VIEW [ CONCURRENTLY ] name [ WITH [ NO ] DATA ] + *

+ * https://www.postgresql.org/docs/16/sql-refreshmaterializedview.html + * + * @author jxnu-liguobin + */ + +public class RefreshMaterializedViewStatement implements Statement { + + private Table view; + private RefreshMode refreshMode; + private boolean concurrently = false; + + public RefreshMaterializedViewStatement() {} + + public RefreshMaterializedViewStatement(Table view, boolean concurrently, + RefreshMode refreshMode) { + this.refreshMode = refreshMode; + this.concurrently = concurrently; + this.view = view; + } + + public Table getView() { + return view; + } + + public void setView(Table view) { + this.view = view; + } + + public RefreshMode getRefreshMode() { + return refreshMode; + } + + public void setRefreshMode(RefreshMode refreshMode) { + this.refreshMode = refreshMode; + } + + public boolean isConcurrently() { + return concurrently; + } + + public void setConcurrently(boolean concurrently) { + this.concurrently = concurrently; + } + + @SuppressWarnings("PMD.SwitchStmtsShouldHaveDefault") + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("REFRESH MATERIALIZED VIEW "); + if (this.refreshMode == null) { + if (concurrently) { + builder.append("CONCURRENTLY "); + } + builder.append(view); + return builder.toString(); + } + switch (this.refreshMode) { + case WITH_DATA: + if (concurrently) { + builder.append("CONCURRENTLY "); + } + builder.append(view); + builder.append(" WITH DATA"); + break; + case WITH_NO_DATA: + builder.append(view); + if (!concurrently) { + builder.append(" WITH NO DATA"); + } + break; + } + return builder.toString(); + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + public RefreshMaterializedViewStatement withTableName(Table view) { + this.setView(view); + return this; + } + + public RefreshMaterializedViewStatement withConcurrently(boolean concurrently) { + this.setConcurrently(concurrently); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/refresh/RefreshMode.java b/src/main/java/net/sf/jsqlparser/statement/refresh/RefreshMode.java new file mode 100644 index 0000000..fb78fac --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/refresh/RefreshMode.java @@ -0,0 +1,19 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2022 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.refresh; + +public enum RefreshMode { + + DEFAULT, WITH_DATA, WITH_NO_DATA; + + public static RefreshMode from(String type) { + return Enum.valueOf(RefreshMode.class, type.toUpperCase()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/AllColumns.java b/src/main/java/net/sf/jsqlparser/statement/select/AllColumns.java new file mode 100644 index 0000000..a1b16da --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/AllColumns.java @@ -0,0 +1,115 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; +import net.sf.jsqlparser.schema.Column; + +import java.util.ArrayList; +import java.util.List; + +public class AllColumns extends ASTNodeAccessImpl implements Expression { + protected ExpressionList exceptColumns; + protected List> replaceExpressions; + private String exceptKeyword; + + public AllColumns(ExpressionList exceptColumns, + List> replaceExpressions) { + this.exceptColumns = exceptColumns; + this.replaceExpressions = replaceExpressions; + this.exceptKeyword = exceptColumns != null ? "Except" : null; + } + + public AllColumns(ExpressionList exceptColumns, + List> replaceExpressions, String exceptKeyword) { + this.exceptColumns = exceptColumns; + this.replaceExpressions = replaceExpressions; + this.exceptKeyword = exceptKeyword; + } + + public AllColumns() { + this(null, null); + } + + public ExpressionList getExceptColumns() { + return exceptColumns; + } + + public AllColumns setExceptColumns(ExpressionList exceptColumns) { + this.exceptColumns = exceptColumns; + return this; + } + + public ExpressionList addExceptColumn(Column column) { + if (exceptColumns == null) { + exceptColumns = new ExpressionList<>(); + } + exceptColumns.add(column); + return exceptColumns; + } + + public List> getReplaceExpressions() { + return replaceExpressions; + } + + public AllColumns setReplaceExpressions(List> replaceExpressions) { + this.replaceExpressions = replaceExpressions; + return this; + } + + public List> addReplaceExpression(SelectItem selectItem) { + if (replaceExpressions == null) { + replaceExpressions = new ArrayList<>(); + } + replaceExpressions.add(selectItem); + return replaceExpressions; + } + + public String getExceptKeyword() { + return exceptKeyword; + } + + public AllColumns setExceptKeyword(String exceptKeyword) { + this.exceptKeyword = exceptKeyword; + return this; + } + + public StringBuilder appendTo(StringBuilder builder) { + builder.append("*"); + if (exceptColumns != null && !exceptColumns.isEmpty()) { + builder.append(" ").append(exceptKeyword).append("( "); + exceptColumns.appendTo(builder); + builder.append(" )"); + } + if (replaceExpressions != null && !replaceExpressions.isEmpty()) { + builder.append(" Replace("); + int i = 0; + for (SelectItem selectItem : replaceExpressions) { + builder.append(i++ > 0 ? ", " : " "); + selectItem.appendTo(builder); + } + builder.append(" )"); + } + return builder; + } + + @Override + public String toString() { + return appendTo(new StringBuilder()).toString(); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/AllTableColumns.java b/src/main/java/net/sf/jsqlparser/statement/select/AllTableColumns.java new file mode 100644 index 0000000..d5c41e7 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/AllTableColumns.java @@ -0,0 +1,65 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.expression.ExpressionVisitor; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.schema.Column; +import net.sf.jsqlparser.schema.Table; + +import java.util.List; + +public class AllTableColumns extends AllColumns { + + private Table table; + + public AllTableColumns(Table table, ExpressionList exceptColumns, + List> replaceExpressions, String exceptKeyword) { + super(exceptColumns, replaceExpressions, exceptKeyword); + this.table = table; + } + + public AllTableColumns(Table table, ExpressionList exceptColumns, + List> replaceExpressions) { + this(table, exceptColumns, replaceExpressions, "EXCEPT"); + } + + public AllTableColumns(Table table) { + this(table, null, null); + } + + public AllTableColumns(Table table, AllColumns allColumns) { + this(table, allColumns.exceptColumns, allColumns.replaceExpressions, + allColumns.getExceptKeyword()); + } + + public Table getTable() { + return table; + } + + public void setTable(Table table) { + this.table = table; + } + + public AllTableColumns withTable(Table table) { + this.setTable(table); + return this; + } + + @Override + public StringBuilder appendTo(StringBuilder builder) { + return super.appendTo(table.appendTo(builder).append(".")); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/Distinct.java b/src/main/java/net/sf/jsqlparser/statement/select/Distinct.java new file mode 100644 index 0000000..37f64ad --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/Distinct.java @@ -0,0 +1,80 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +public class Distinct implements Serializable { + + private List> onSelectItems; + private boolean useUnique = false; + + public Distinct() {} + + public Distinct(boolean useUnique) { + this.useUnique = useUnique; + } + + public List> getOnSelectItems() { + return onSelectItems; + } + + public void setOnSelectItems(List> list) { + onSelectItems = list; + } + + public boolean isUseUnique() { + return useUnique; + } + + public void setUseUnique(boolean useUnique) { + this.useUnique = useUnique; + } + + @Override + public String toString() { + String sql = useUnique ? "UNIQUE" : "DISTINCT"; + + if (onSelectItems != null && !onSelectItems.isEmpty()) { + sql += " ON (" + PlainSelect.getStringList(onSelectItems) + ")"; + } + + return sql; + } + + public Distinct withOnSelectItems(List> onSelectItems) { + this.setOnSelectItems(onSelectItems); + return this; + } + + public Distinct withUseUnique(boolean useUnique) { + this.setUseUnique(useUnique); + return this; + } + + public Distinct addOnSelectItems(SelectItem... onSelectItems) { + List> collection = + Optional.ofNullable(getOnSelectItems()).orElseGet(ArrayList::new); + Collections.addAll(collection, onSelectItems); + return this.withOnSelectItems(collection); + } + + public Distinct addOnSelectItems(Collection> onSelectItems) { + List> collection = + Optional.ofNullable(getOnSelectItems()).orElseGet(ArrayList::new); + collection.addAll(onSelectItems); + return this.withOnSelectItems(collection); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/ExceptOp.java b/src/main/java/net/sf/jsqlparser/statement/select/ExceptOp.java new file mode 100644 index 0000000..f6b11a7 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/ExceptOp.java @@ -0,0 +1,19 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.statement.select.SetOperationList.SetOperationType; + +public class ExceptOp extends SetOperation { + + public ExceptOp() { + super(SetOperationType.EXCEPT); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/Fetch.java b/src/main/java/net/sf/jsqlparser/statement/select/Fetch.java new file mode 100644 index 0000000..4e8fd9d --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/Fetch.java @@ -0,0 +1,133 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.JdbcParameter; +import net.sf.jsqlparser.expression.LongValue; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class Fetch implements Serializable { + private final List fetchParameters = new ArrayList<>(); + private Expression expression = null; + private boolean isFetchParamFirst = false; + + @Deprecated + public long getRowCount() { + return expression instanceof LongValue ? ((LongValue) expression).getValue() : null; + } + + @Deprecated + public void setRowCount(long l) { + setExpression(new LongValue(l)); + } + + public Expression getExpression() { + return expression; + } + + public void setExpression(Expression expression) { + this.expression = expression; + } + + public Fetch withExpression(Expression expression) { + this.setExpression(expression); + return this; + } + + @Deprecated + public JdbcParameter getFetchJdbcParameter() { + return expression instanceof JdbcParameter ? (JdbcParameter) expression : null; + } + + @Deprecated + public void setFetchJdbcParameter(JdbcParameter jdbc) { + this.setExpression(jdbc); + } + + public Fetch addFetchParameter(String parameter) { + fetchParameters.add(parameter); + return this; + } + + public List getFetchParameters() { + return this.fetchParameters; + } + + @Deprecated + public String getFetchParam() { + String parameterStr = ""; + for (String p : fetchParameters) { + parameterStr += " " + p; + } + return parameterStr.trim(); + } + + @Deprecated + public void setFetchParam(String s) { + fetchParameters.clear(); + if (s != null) { + fetchParameters.addAll(Arrays.asList(s.trim().split("\\s+"))); + } + } + + public boolean isFetchParamFirst() { + return isFetchParamFirst; + } + + public void setFetchParamFirst(boolean b) { + this.isFetchParamFirst = b; + } + + public StringBuilder appendTo(StringBuilder builder) { + builder.append(" FETCH"); + if (isFetchParamFirst) { + builder.append(" FIRST"); + } else { + builder.append(" NEXT"); + } + + if (expression != null) { + builder.append(" ").append(expression); + } + + for (String s : fetchParameters) { + builder.append(" ").append(s); + } + return builder; + } + + @Override + public String toString() { + return appendTo(new StringBuilder()).toString(); + } + + @Deprecated + public Fetch withRowCount(long rowCount) { + this.setRowCount(rowCount); + return this; + } + + @Deprecated + public Fetch withFetchJdbcParameter(JdbcParameter fetchJdbcParameter) { + this.setFetchJdbcParameter(fetchJdbcParameter); + return this; + } + + @Deprecated + public Fetch withFetchParam(String fetchParam) { + this.setFetchParam(fetchParam); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/First.java b/src/main/java/net/sf/jsqlparser/statement/select/First.java new file mode 100644 index 0000000..3744e30 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/First.java @@ -0,0 +1,97 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.expression.JdbcParameter; + +import java.io.Serializable; + +public class First implements Serializable { + + private Keyword keyword; + private Long rowCount; + private JdbcParameter jdbcParameter; + private String variable; + + public Long getRowCount() { + return rowCount; + } + + public void setRowCount(Long rowCount) { + this.rowCount = rowCount; + } + + public JdbcParameter getJdbcParameter() { + return jdbcParameter; + } + + public void setJdbcParameter(JdbcParameter jdbcParameter) { + this.jdbcParameter = jdbcParameter; + } + + public Keyword getKeyword() { + return keyword; + } + + public void setKeyword(Keyword keyword) { + this.keyword = keyword; + } + + public String getVariable() { + return variable; + } + + public void setVariable(String variable) { + this.variable = variable; + } + + @Override + public String toString() { + String result = keyword.name() + " "; + + if (rowCount != null) { + result += rowCount; + } else if (jdbcParameter != null) { + result += jdbcParameter.toString(); + } else if (variable != null) { + result += variable; + } + + return result; + } + + public First withKeyword(Keyword keyword) { + this.setKeyword(keyword); + return this; + } + + public First withRowCount(Long rowCount) { + this.setRowCount(rowCount); + return this; + } + + public First withJdbcParameter(JdbcParameter jdbcParameter) { + this.setJdbcParameter(jdbcParameter); + return this; + } + + public First withVariable(String variable) { + this.setVariable(variable); + return this; + } + + public enum Keyword { + FIRST, LIMIT; + + public static Keyword from(String keyword) { + return Enum.valueOf(Keyword.class, keyword.toUpperCase()); + } + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/ForClause.java b/src/main/java/net/sf/jsqlparser/statement/select/ForClause.java new file mode 100644 index 0000000..4604425 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/ForClause.java @@ -0,0 +1,38 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2023 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +public class ForClause extends ASTNodeAccessImpl { + private ForOption forOption; + + public ForOption getForOption() { + return forOption; + } + + public ForClause setForOption(String forOption) { + this.forOption = ForOption.from(forOption); + return this; + } + + @Override + public String toString() { + return appendTo(new StringBuilder()).toString(); + } + + public enum ForOption { + BROWSE, XML, JSON; + + public static ForOption from(String option) { + return Enum.valueOf(ForOption.class, option.toUpperCase()); + } + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/ForMode.java b/src/main/java/net/sf/jsqlparser/statement/select/ForMode.java new file mode 100644 index 0000000..846faf9 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/ForMode.java @@ -0,0 +1,34 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2023 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +/** + * @author jxnu-liguobin + */ +public enum ForMode { + + UPDATE("UPDATE"), + + SHARE("SHARE"), + + NO_KEY_UPDATE("NO KEY UPDATE"), + + KEY_SHARE("KEY SHARE"); + + private final String value; + + ForMode(String s) { + this.value = s; + } + + public String getValue() { + return value; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/FromItem.java b/src/main/java/net/sf/jsqlparser/statement/select/FromItem.java new file mode 100644 index 0000000..6673314 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/FromItem.java @@ -0,0 +1,51 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.expression.Alias; +import net.sf.jsqlparser.parser.ASTNodeAccess; + +public interface FromItem extends ASTNodeAccess { + + T accept(FromItemVisitor fromItemVisitor, S context); + + default void accept(FromItemVisitor fromItemVisitor) { + this.accept(fromItemVisitor, null); + } + + Alias getAlias(); + + void setAlias(Alias alias); + + default FromItem withAlias(Alias alias) { + setAlias(alias); + return this; + } + + Pivot getPivot(); + + void setPivot(Pivot pivot); + + default FromItem withPivot(Pivot pivot) { + setPivot(pivot); + return this; + } + + UnPivot getUnPivot(); + + void setUnPivot(UnPivot unpivot); + + default FromItem withUnPivot(UnPivot unpivot) { + setUnPivot(unpivot); + return this; + } + + +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/FromItemVisitor.java b/src/main/java/net/sf/jsqlparser/statement/select/FromItemVisitor.java new file mode 100644 index 0000000..13b7820 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/FromItemVisitor.java @@ -0,0 +1,51 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.schema.Table; + +public interface FromItemVisitor { + + T visit(Table tableName, S context); + + default void visit(Table tableName) { + this.visit(tableName, null); + } + + T visit(ParenthesedSelect selectBody, S context); + + default void visit(ParenthesedSelect selectBody) { + this.visit(selectBody, null); + } + + T visit(LateralSubSelect lateralSubSelect, S context); + + default void visit(LateralSubSelect lateralSubSelect) { + this.visit(lateralSubSelect, null); + } + + T visit(TableFunction tableFunction, S context); + + default void visit(TableFunction tableFunction) { + this.visit(tableFunction, null); + } + + T visit(ParenthesedFromItem parenthesedFromItem, S context); + + default void visit(ParenthesedFromItem parenthesedFromItem) { + this.visit(parenthesedFromItem, null); + } + + T visit(Values values, S context); + + default void visit(Values values) { + this.visit(values, null); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/FromItemVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/statement/select/FromItemVisitorAdapter.java new file mode 100644 index 0000000..7daad1c --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/FromItemVisitorAdapter.java @@ -0,0 +1,52 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.schema.Table; + +@SuppressWarnings({"PMD.UncommentedEmptyMethodBody"}) +public class FromItemVisitorAdapter implements FromItemVisitor { + + @Override + public T visit(Table table, S context) { + + return null; + } + + @Override + public T visit(ParenthesedSelect select, S context) { + + return null; + } + + @Override + public T visit(LateralSubSelect lateralSubSelect, S context) { + + return null; + } + + @Override + public T visit(TableFunction tableFunction, S context) { + + return null; + } + + @Override + public T visit(ParenthesedFromItem fromItem, S context) { + + return null; + } + + @Override + public T visit(Values values, S context) { + + return null; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/GroupByElement.java b/src/main/java/net/sf/jsqlparser/statement/select/GroupByElement.java new file mode 100644 index 0000000..a4517cf --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/GroupByElement.java @@ -0,0 +1,144 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList; + +public class GroupByElement implements Serializable { + private ExpressionList groupByExpressions = new ExpressionList(); + private List groupingSets = new ArrayList<>(); + // postgres rollup is an ExpressionList + private boolean mysqlWithRollup = false; + + public boolean isUsingBrackets() { + return groupByExpressions.isUsingBrackets(); + } + + public T accept(GroupByVisitor groupByVisitor, S context) { + return groupByVisitor.visit(this, context); + } + + public ExpressionList getGroupByExpressionList() { + return groupByExpressions; + } + + @Deprecated + public ExpressionList getGroupByExpressions() { + return groupByExpressions; + } + + public void setGroupByExpressions(ExpressionList groupByExpressions) { + this.groupByExpressions = groupByExpressions; + } + + @Deprecated + public void addGroupByExpression(Expression groupByExpression) { + if (groupByExpressions.getExpressions() == null) { + groupByExpressions.setExpressions(new ArrayList()); + } + groupByExpressions.getExpressions().add(groupByExpression); + } + + public List getGroupingSets() { + return groupingSets; + } + + public void setGroupingSets(List groupingSets) { + this.groupingSets = groupingSets; + } + + public void addGroupingSet(ExpressionList list) { + this.groupingSets.add(list); + } + + @Override + @SuppressWarnings({"PMD.CyclomaticComplexity"}) + public String toString() { + StringBuilder b = new StringBuilder(); + b.append("GROUP BY "); + + if (groupByExpressions != null) { + b.append(groupByExpressions.toString()); + } + + int i = 0; + if (groupingSets.size() > 0) { + if (b.charAt(b.length() - 1) != ' ') { + b.append(' '); + } + b.append("GROUPING SETS ("); + for (ExpressionList expressionList : groupingSets) { + b.append(i++ > 0 ? ", " : "").append(Select.getStringList( + expressionList, + true, expressionList instanceof ParenthesedExpressionList)); + } + b.append(")"); + } + + if (isMysqlWithRollup()) { + b.append(" WITH ROLLUP"); + } + + return b.toString(); + } + + public GroupByElement withGroupByExpressions(ExpressionList groupByExpressions) { + this.setGroupByExpressions(groupByExpressions); + return this; + } + + public GroupByElement withGroupingSets(List groupingSets) { + this.setGroupingSets(groupingSets); + return this; + } + + public GroupByElement addGroupByExpressions(Expression... groupByExpressions) { + return this.addGroupByExpressions(Arrays.asList(groupByExpressions)); + } + + public GroupByElement addGroupByExpressions( + Collection groupByExpressions) { + ExpressionList collection = + Optional.ofNullable(getGroupByExpressions()).orElseGet(ExpressionList::new); + Collections.addAll(collection, groupByExpressions); + return this.withGroupByExpressions(collection); + } + + public GroupByElement addGroupingSets(Object... groupingSets) { + List collection = Optional.ofNullable(getGroupingSets()).orElseGet(ArrayList::new); + Collections.addAll(collection, groupingSets); + return this.withGroupingSets(collection); + } + + public GroupByElement addGroupingSets(Collection groupingSets) { + List collection = Optional.ofNullable(getGroupingSets()).orElseGet(ArrayList::new); + collection.addAll(groupingSets); + return this.withGroupingSets(collection); + } + + public boolean isMysqlWithRollup() { + return mysqlWithRollup; + } + + public GroupByElement setMysqlWithRollup(boolean mysqlWithRollup) { + this.mysqlWithRollup = mysqlWithRollup; + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/GroupByVisitor.java b/src/main/java/net/sf/jsqlparser/statement/select/GroupByVisitor.java new file mode 100644 index 0000000..24ade13 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/GroupByVisitor.java @@ -0,0 +1,19 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +public interface GroupByVisitor { + + T visit(GroupByElement groupBy, S context); + + default void visit(GroupByElement groupBy) { + this.visit(groupBy, null); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/IntersectOp.java b/src/main/java/net/sf/jsqlparser/statement/select/IntersectOp.java new file mode 100644 index 0000000..f2248ca --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/IntersectOp.java @@ -0,0 +1,19 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.statement.select.SetOperationList.SetOperationType; + +public class IntersectOp extends SetOperation { + + public IntersectOp() { + super(SetOperationType.INTERSECT); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/IntoTableVisitor.java b/src/main/java/net/sf/jsqlparser/statement/select/IntoTableVisitor.java new file mode 100644 index 0000000..40be045 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/IntoTableVisitor.java @@ -0,0 +1,21 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.schema.Table; + +public interface IntoTableVisitor { + + T visit(Table tableName, S context); + + default void visit(Table tableName) { + this.visit(tableName, null); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/IntoTableVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/statement/select/IntoTableVisitorAdapter.java new file mode 100644 index 0000000..44393be --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/IntoTableVisitorAdapter.java @@ -0,0 +1,21 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.schema.Table; + +@SuppressWarnings({"PMD.UncommentedEmptyMethodBody"}) +public class IntoTableVisitorAdapter implements IntoTableVisitor { + + @Override + public T visit(Table tableName, S context) { + return null; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/Join.java b/src/main/java/net/sf/jsqlparser/statement/select/Join.java new file mode 100644 index 0000000..bbd32df --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/Join.java @@ -0,0 +1,456 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; +import net.sf.jsqlparser.schema.Column; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; + +@SuppressWarnings({"PMD.CyclomaticComplexity"}) +public class Join extends ASTNodeAccessImpl { + + private final LinkedList onExpressions = new LinkedList<>(); + private final LinkedList usingColumns = new LinkedList<>(); + private boolean outer = false; + private boolean right = false; + private boolean left = false; + private boolean natural = false; + private boolean global = false; + private boolean full = false; + private boolean inner = false; + private boolean simple = false; + private boolean cross = false; + private boolean semi = false; + private boolean straight = false; + private boolean apply = false; + private FromItem fromItem; + private KSQLJoinWindow joinWindow; + + private JoinHint joinHint = null; + + public boolean isSimple() { + return simple; + } + + public void setSimple(boolean b) { + simple = b; + } + + public Join withSimple(boolean b) { + this.setSimple(b); + return this; + } + + /** + * A JOIN means INNER when the INNER keyword is set or when no other qualifier has been set. + * + * @return Tells, if a JOIN means a qualified INNER JOIN. + */ + public boolean isInnerJoin() { + return inner + || !( + /* Qualified Joins */ + left || right || full || outer + + /* Cross Join */ + || cross + + /* Natural Join */ + || natural); + } + + /** + * @return Tells, if the INNER keyword has been set. + */ + public boolean isInner() { + return inner; + } + + /** + * Sets the INNER keyword and switches off any contradicting qualifiers automatically. + */ + public void setInner(boolean b) { + if (b) { + left = false; + right = false; + outer = false; + cross = false; + natural = false; + } + inner = b; + } + + public Join withInner(boolean b) { + this.setInner(b); + return this; + } + + public boolean isStraight() { + return straight; + } + + public void setStraight(boolean b) { + straight = b; + } + + public Join withStraight(boolean b) { + this.setStraight(b); + return this; + } + + /** + * Whether is a "OUTER" join + * + * @return true if is a "OUTER" join + */ + public boolean isOuter() { + return outer; + } + + /** + * Sets the OUTER keyword and switches off any contradicting qualifiers automatically. + */ + public void setOuter(boolean b) { + if (b) { + inner = false; + } + outer = b; + } + + public Join withOuter(boolean b) { + this.setOuter(b); + return this; + } + + public boolean isApply() { + return apply; + } + + public void setApply(boolean apply) { + this.apply = apply; + } + + public Join withApply(boolean apply) { + this.setApply(apply); + return this; + } + + /** + * Whether is a "SEMI" join + * + * @return true if is a "SEMI" join + */ + public boolean isSemi() { + return semi; + } + + public void setSemi(boolean b) { + semi = b; + } + + public Join withSemi(boolean b) { + this.setSemi(b); + return this; + } + + /** + * Whether is a "LEFT" join + * + * @return true if is a "LEFT" join + */ + public boolean isLeft() { + return left; + } + + /** + * Sets the LEFT keyword and switches off any contradicting qualifiers automatically. + */ + public void setLeft(boolean b) { + if (b) { + inner = false; + right = false; + } + left = b; + } + + public Join withLeft(boolean b) { + this.setLeft(b); + return this; + } + + /** + * Whether is a "RIGHT" join + * + * @return true if is a "RIGHT" join + */ + public boolean isRight() { + return right; + } + + /** + * Sets the RIGHT keyword and switches off any contradicting qualifiers automatically. + */ + public void setRight(boolean b) { + if (b) { + inner = false; + left = false; + } + right = b; + } + + public Join withRight(boolean b) { + this.setRight(b); + return this; + } + + /** + * Whether is a "NATURAL" join + * + * @return true if is a "NATURAL" join + */ + public boolean isNatural() { + return natural; + } + + public void setNatural(boolean b) { + natural = b; + } + + public boolean isGlobal() { + return global; + } + + public void setGlobal(boolean b) { + global = b; + } + + public Join withNatural(boolean b) { + this.setNatural(b); + return this; + } + + /** + * Whether is a "FULL" join + * + * @return true if is a "FULL" join + */ + public boolean isFull() { + return full; + } + + public void setFull(boolean b) { + full = b; + } + + public Join withFull(boolean b) { + this.setFull(b); + return this; + } + + public boolean isCross() { + return cross; + } + + public void setCross(boolean cross) { + this.cross = cross; + } + + public Join withCross(boolean cross) { + this.setCross(cross); + return this; + } + + /** + * Returns the right item of the join + */ + public FromItem getRightItem() { + return fromItem; + } + + public void setRightItem(FromItem item) { + fromItem = item; + } + + @Deprecated + public Join withRightItem(FromItem item) { + this.setFromItem(item); + return this; + } + + public FromItem getFromItem() { + return fromItem; + } + + public Join setFromItem(FromItem fromItem) { + this.fromItem = fromItem; + return this; + } + + /** + * Returns the "ON" expression (if any) + */ + @Deprecated + public Expression getOnExpression() { + return onExpressions.get(0); + } + + @Deprecated + public void setOnExpression(Expression expression) { + onExpressions.add(0, expression); + } + + public Collection getOnExpressions() { + return onExpressions; + } + + public Join setOnExpressions(Collection expressions) { + onExpressions.clear(); + onExpressions.addAll(expressions); + return this; + } + + @Deprecated + public Join withOnExpression(Expression expression) { + this.setOnExpression(expression); + return this; + } + + public Join addOnExpression(Expression expression) { + onExpressions.add(expression); + return this; + } + + /** + * Returns the "USING" list of {@link net.sf.jsqlparser.schema.Column}s (if any) + */ + public List getUsingColumns() { + return usingColumns; + } + + public void setUsingColumns(List list) { + usingColumns.clear(); + usingColumns.addAll(list); + } + + public Join withUsingColumns(List list) { + this.setUsingColumns(list); + return this; + } + + public boolean isWindowJoin() { + return joinWindow != null; + } + + /** + * Return the "WITHIN" join window (if any) + * + * @return + */ + public KSQLJoinWindow getJoinWindow() { + return joinWindow; + } + + public void setJoinWindow(KSQLJoinWindow joinWindow) { + this.joinWindow = joinWindow; + } + + public Join withJoinWindow(KSQLJoinWindow joinWindow) { + this.setJoinWindow(joinWindow); + return this; + } + + public JoinHint getJoinHint() { + return joinHint; + } + + public Join setJoinHint(JoinHint joinHint) { + this.joinHint = joinHint; + return this; + } + + @Override + @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"}) + public String toString() { + StringBuilder builder = new StringBuilder(); + + if (isGlobal()) { + builder.append("GLOBAL "); + } + + if (isSimple() && isOuter()) { + builder.append("OUTER ").append(fromItem); + } else if (isSimple()) { + builder.append(fromItem); + } else { + if (isNatural()) { + builder.append("NATURAL "); + } + + if (isRight()) { + builder.append("RIGHT "); + } else if (isFull()) { + builder.append("FULL "); + } else if (isLeft()) { + builder.append("LEFT "); + } else if (isCross()) { + builder.append("CROSS "); + } + + if (isOuter()) { + builder.append("OUTER "); + } else if (isInner()) { + builder.append("INNER "); + } else if (isSemi()) { + builder.append("SEMI "); + } + + if (isStraight()) { + builder.append("STRAIGHT_JOIN "); + } else if (isApply()) { + builder.append("APPLY "); + } else { + if (joinHint != null) { + builder.append(joinHint).append(" "); + } + builder.append("JOIN "); + } + + builder.append(fromItem).append((joinWindow != null) ? " WITHIN " + joinWindow : ""); + } + + for (Expression onExpression : onExpressions) { + builder.append(" ON ").append(onExpression); + } + if (usingColumns.size() > 0) { + builder.append(PlainSelect.getFormattedList(usingColumns, "USING", true, true)); + } + + return builder.toString(); + } + + public Join addUsingColumns(Column... usingColumns) { + List collection = Optional.ofNullable(getUsingColumns()).orElseGet(ArrayList::new); + Collections.addAll(collection, usingColumns); + return this.withUsingColumns(collection); + } + + public Join addUsingColumns(Collection usingColumns) { + List collection = Optional.ofNullable(getUsingColumns()).orElseGet(ArrayList::new); + collection.addAll(usingColumns); + return this.withUsingColumns(collection); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/JoinHint.java b/src/main/java/net/sf/jsqlparser/statement/select/JoinHint.java new file mode 100644 index 0000000..099f730 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/JoinHint.java @@ -0,0 +1,31 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2023 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +/** + * Hints (Transact-SQL) - Join + * + * @link Hints + * (Transact-SQL) - Join + */ + +public class JoinHint { + private final String keyword; + + public JoinHint(String keyword) { + this.keyword = keyword; + } + + @Override + public String toString() { + return keyword; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/KSQLJoinWindow.java b/src/main/java/net/sf/jsqlparser/statement/select/KSQLJoinWindow.java new file mode 100644 index 0000000..4bcc6a0 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/KSQLJoinWindow.java @@ -0,0 +1,124 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +import static net.sf.jsqlparser.statement.select.KSQLWindow.TimeUnit; + +public class KSQLJoinWindow extends ASTNodeAccessImpl { + + private boolean beforeAfter; + private long duration; + private TimeUnit timeUnit; + private long beforeDuration; + private TimeUnit beforeTimeUnit; + private long afterDuration; + private TimeUnit afterTimeUnit; + + public final static TimeUnit from(String timeUnitStr) { + return Enum.valueOf(TimeUnit.class, timeUnitStr.toUpperCase()); + } + + public boolean isBeforeAfterWindow() { + return beforeAfter; + } + + public void setBeforeAfterWindow(boolean beforeAfter) { + this.beforeAfter = beforeAfter; + } + + public long getDuration() { + return duration; + } + + public void setDuration(long duration) { + this.duration = duration; + } + + public TimeUnit getTimeUnit() { + return timeUnit; + } + + public void setTimeUnit(TimeUnit timeUnit) { + this.timeUnit = timeUnit; + } + + public long getBeforeDuration() { + return beforeDuration; + } + + public void setBeforeDuration(long beforeDuration) { + this.beforeDuration = beforeDuration; + } + + public TimeUnit getBeforeTimeUnit() { + return beforeTimeUnit; + } + + public void setBeforeTimeUnit(TimeUnit beforeTimeUnit) { + this.beforeTimeUnit = beforeTimeUnit; + } + + public long getAfterDuration() { + return afterDuration; + } + + public void setAfterDuration(long afterDuration) { + this.afterDuration = afterDuration; + } + + public TimeUnit getAfterTimeUnit() { + return afterTimeUnit; + } + + public void setAfterTimeUnit(TimeUnit afterTimeUnit) { + this.afterTimeUnit = afterTimeUnit; + } + + @Override + public String toString() { + if (isBeforeAfterWindow()) { + return "(" + beforeDuration + " " + beforeTimeUnit + ", " + afterDuration + " " + + afterTimeUnit + ")"; + } + return "(" + duration + " " + timeUnit + ")"; + } + + public KSQLJoinWindow withDuration(long duration) { + this.setDuration(duration); + return this; + } + + public KSQLJoinWindow withTimeUnit(TimeUnit timeUnit) { + this.setTimeUnit(timeUnit); + return this; + } + + public KSQLJoinWindow withBeforeDuration(long beforeDuration) { + this.setBeforeDuration(beforeDuration); + return this; + } + + public KSQLJoinWindow withBeforeTimeUnit(TimeUnit beforeTimeUnit) { + this.setBeforeTimeUnit(beforeTimeUnit); + return this; + } + + public KSQLJoinWindow withAfterDuration(long afterDuration) { + this.setAfterDuration(afterDuration); + return this; + } + + public KSQLJoinWindow withAfterTimeUnit(TimeUnit afterTimeUnit) { + this.setAfterTimeUnit(afterTimeUnit); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/KSQLWindow.java b/src/main/java/net/sf/jsqlparser/statement/select/KSQLWindow.java new file mode 100644 index 0000000..12b98d0 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/KSQLWindow.java @@ -0,0 +1,140 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +public class KSQLWindow extends ASTNodeAccessImpl { + + private boolean hopping; + private boolean tumbling; + private boolean session; + private long sizeDuration; + private TimeUnit sizeTimeUnit; + private long advanceDuration; + private TimeUnit advanceTimeUnit; + + public KSQLWindow() {} + + public boolean isHoppingWindow() { + return hopping; + } + + public void setHoppingWindow(boolean hopping) { + this.hopping = hopping; + } + + public boolean isTumblingWindow() { + return tumbling; + } + + public void setTumblingWindow(boolean tumbling) { + this.tumbling = tumbling; + } + + public boolean isSessionWindow() { + return session; + } + + public void setSessionWindow(boolean session) { + this.session = session; + } + + public long getSizeDuration() { + return sizeDuration; + } + + public void setSizeDuration(long sizeDuration) { + this.sizeDuration = sizeDuration; + } + + public TimeUnit getSizeTimeUnit() { + return sizeTimeUnit; + } + + public void setSizeTimeUnit(TimeUnit sizeTimeUnit) { + this.sizeTimeUnit = sizeTimeUnit; + } + + public long getAdvanceDuration() { + return advanceDuration; + } + + public void setAdvanceDuration(long advanceDuration) { + this.advanceDuration = advanceDuration; + } + + public TimeUnit getAdvanceTimeUnit() { + return advanceTimeUnit; + } + + public void setAdvanceTimeUnit(TimeUnit advanceTimeUnit) { + this.advanceTimeUnit = advanceTimeUnit; + } + + @Override + public String toString() { + if (isHoppingWindow()) { + return "HOPPING (" + "SIZE " + sizeDuration + " " + sizeTimeUnit + ", " + + "ADVANCE BY " + advanceDuration + " " + advanceTimeUnit + ")"; + } else if (isSessionWindow()) { + return "SESSION (" + sizeDuration + " " + sizeTimeUnit + ")"; + } else { + return "TUMBLING (" + "SIZE " + sizeDuration + " " + sizeTimeUnit + ")"; + } + } + + public KSQLWindow withSizeDuration(long sizeDuration) { + this.setSizeDuration(sizeDuration); + return this; + } + + public KSQLWindow withSizeTimeUnit(TimeUnit sizeTimeUnit) { + this.setSizeTimeUnit(sizeTimeUnit); + return this; + } + + public KSQLWindow withAdvanceDuration(long advanceDuration) { + this.setAdvanceDuration(advanceDuration); + return this; + } + + public KSQLWindow withAdvanceTimeUnit(TimeUnit advanceTimeUnit) { + this.setAdvanceTimeUnit(advanceTimeUnit); + return this; + } + + public enum TimeUnit { + DAY, HOUR, MINUTE, SECOND, MILLISECOND, DAYS, HOURS, MINUTES, SECONDS, MILLISECONDS; + + public static TimeUnit from(String unit) { + return Enum.valueOf(TimeUnit.class, unit.toUpperCase()); + } + } + + public enum WindowType { + HOPPING("HOPPING"), SESSION("SESSION"), TUMBLING("TUMBLING"); + + private String windowType; + + WindowType(String windowType) { + this.windowType = windowType; + } + + public static WindowType from(String type) { + return Enum.valueOf(WindowType.class, type.toUpperCase()); + } + + public String getWindowType() { + return windowType; + } + } + +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/LateralSubSelect.java b/src/main/java/net/sf/jsqlparser/statement/select/LateralSubSelect.java new file mode 100644 index 0000000..736a6c8 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/LateralSubSelect.java @@ -0,0 +1,80 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.expression.Alias; + +/** + * lateral sub select + * + * @author tobens + */ +public class LateralSubSelect extends ParenthesedSelect { + private String prefix; + + public LateralSubSelect() { + this("LATERAL"); + } + + public LateralSubSelect(String prefix) { + this(prefix, null, null); + } + + public LateralSubSelect(String prefix, Select select) { + this(prefix, select, null); + } + + public LateralSubSelect(Select select, Alias alias) { + this("LATERAL", select, alias); + } + + public LateralSubSelect(String prefix, Select select, Alias alias) { + this.prefix = prefix; + this.select = select; + this.alias = alias; + } + + public String getPrefix() { + return prefix; + } + + public void setPrefix(String prefix) { + this.prefix = prefix; + } + + public LateralSubSelect withPrefix(String prefix) { + this.setPrefix(prefix); + return this; + } + + public LateralSubSelect withSelect(Select select) { + setSelect(select); + return this; + } + + public LateralSubSelect withAlias(Alias alias) { + setAlias(alias); + return this; + } + + public String toString() { + return prefix + super.toString(); + } + + @Override + public T accept(SelectVisitor selectVisitor, S context) { + return selectVisitor.visit(this, context); + } + + @Override + public T accept(FromItemVisitor fromItemVisitor, S context) { + return fromItemVisitor.visit(this, context); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/LateralView.java b/src/main/java/net/sf/jsqlparser/statement/select/LateralView.java new file mode 100644 index 0000000..0336d40 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/LateralView.java @@ -0,0 +1,106 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2023 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.expression.Alias; +import net.sf.jsqlparser.expression.Function; + +import java.io.Serializable; + +public class LateralView implements Serializable { + private boolean isUsingOuter = false; + private Function generatorFunction; + private Alias tableAlias = null; + private Alias columnAlias; + + public LateralView(boolean useOuter, Function generatorFunction, Alias tableAlias, + Alias columnAlias) { + this.isUsingOuter = useOuter; + this.generatorFunction = generatorFunction; + this.tableAlias = tableAlias; + this.columnAlias = columnAlias; + } + + public boolean isUsingOuter() { + return isUsingOuter; + } + + public void setUsingOuter(boolean useOuter) { + this.isUsingOuter = useOuter; + } + + public LateralView withOuter(boolean useOuter) { + this.setUsingOuter(useOuter); + return this; + } + + public Function getGeneratorFunction() { + return generatorFunction; + } + + public void setGeneratorFunction(Function generatorFunction) { + this.generatorFunction = generatorFunction; + } + + public LateralView withGeneratorFunction(Function generatorFunction) { + this.setGeneratorFunction(generatorFunction); + return this; + } + + public Alias getTableAlias() { + return tableAlias; + } + + public void setTableAlias(Alias tableAlias) { + this.tableAlias = tableAlias; + } + + public LateralView withTableAlias(Alias tableAlias) { + // "AS" is not allowed here, so overwrite hard + this.setTableAlias(tableAlias != null ? tableAlias.withUseAs(false) : null); + return this; + } + + public Alias getColumnAlias() { + return columnAlias; + } + + public void setColumnAlias(Alias columnAlias) { + this.columnAlias = columnAlias; + } + + public LateralView withColumnAlias(Alias columnAlias) { + // "AS" is required here, so overwrite + this.setColumnAlias(columnAlias.withUseAs(true)); + return this; + } + + public StringBuilder appendTo(StringBuilder builder) { + builder.append("LATERAL VIEW"); + + if (isUsingOuter) { + builder.append(" OUTER"); + } + + builder.append(" ").append(generatorFunction); + if (tableAlias != null) { + builder.append(" ").append(tableAlias); + } + + builder.append(" ").append(columnAlias); + + return builder; + } + + @Override + public String toString() { + return appendTo(new StringBuilder()).toString(); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/Limit.java b/src/main/java/net/sf/jsqlparser/statement/select/Limit.java new file mode 100644 index 0000000..9257daa --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/Limit.java @@ -0,0 +1,155 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.expression.AllValue; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.NullValue; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +import java.util.Arrays; + +public class Limit extends ASTNodeAccessImpl { + + private Expression rowCount; + private Expression offset; + + /** + * A query with the LIMIT n BY expressions clause selects the first n rows for each distinct + * value of expressions. The key for LIMIT BY can contain any number of expressions. + * + * @see ClickHouse + * LIMIT BY Clause + */ + private ExpressionList byExpressions; + + public Expression getOffset() { + return offset; + } + + public void setOffset(Expression l) { + offset = l; + } + + public Expression getRowCount() { + return rowCount; + } + + public void setRowCount(Expression l) { + rowCount = l; + } + + @Deprecated + public boolean isLimitAll() { + return rowCount instanceof AllValue; + } + + @Deprecated + public void setLimitAll(boolean b) { + if (b) { + rowCount = new AllValue(); + } + } + + @Deprecated + public boolean isLimitNull() { + return rowCount instanceof NullValue; + } + + @Deprecated + public void setLimitNull(boolean b) { + if (b) { + rowCount = new NullValue(); + } + } + + @Override + public String toString() { + String retVal = " LIMIT "; + + if (rowCount instanceof AllValue || rowCount instanceof NullValue) { + // no offset allowed + retVal += rowCount; + } else { + if (null != offset) { + retVal += offset + ", "; + } + if (null != rowCount) { + retVal += rowCount; + } + } + + if (byExpressions != null) { + retVal += " BY " + byExpressions.toString(); + } + + return retVal; + } + + public Limit withRowCount(Expression rowCount) { + this.setRowCount(rowCount); + return this; + } + + public Limit withOffset(Expression offset) { + this.setOffset(offset); + return this; + } + + @Deprecated + public Limit withLimitAll(boolean limitAll) { + this.setLimitAll(limitAll); + return this; + } + + @Deprecated + public Limit withLimitNull(boolean limitNull) { + this.setLimitNull(limitNull); + return this; + } + + public E getOffset(Class type) { + return type.cast(getOffset()); + } + + public E getRowCount(Class type) { + return type.cast(getRowCount()); + } + + public ExpressionList getByExpressions() { + return byExpressions; + } + + public void setByExpressions(ExpressionList byExpressions) { + this.byExpressions = byExpressions; + } + + public void setByExpressions(Expression... byExpressions) { + this.setByExpressions(new ExpressionList<>(byExpressions)); + } + + public void addByExpression(Expression byExpression) { + if (byExpression == null) { + byExpressions = new ExpressionList<>(); + } + byExpressions.add(byExpression); + } + + public Limit withByExpressions(ExpressionList byExpressions) { + this.setByExpressions(byExpressions); + return this; + } + + public Limit withByExpressions(Expression... byExpressions) { + return withByExpressions(new ExpressionList<>(Arrays.asList(byExpressions))); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/MinusOp.java b/src/main/java/net/sf/jsqlparser/statement/select/MinusOp.java new file mode 100644 index 0000000..ab77997 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/MinusOp.java @@ -0,0 +1,19 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.statement.select.SetOperationList.SetOperationType; + +public class MinusOp extends SetOperation { + + public MinusOp() { + super(SetOperationType.MINUS); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/MySqlSqlCacheFlags.java b/src/main/java/net/sf/jsqlparser/statement/select/MySqlSqlCacheFlags.java new file mode 100644 index 0000000..1656e30 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/MySqlSqlCacheFlags.java @@ -0,0 +1,21 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2022 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +/** + * @author tw + */ +public enum MySqlSqlCacheFlags { + SQL_CACHE, SQL_NO_CACHE; + + public static MySqlSqlCacheFlags from(String flag) { + return Enum.valueOf(MySqlSqlCacheFlags.class, flag.toUpperCase()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/Offset.java b/src/main/java/net/sf/jsqlparser/statement/select/Offset.java new file mode 100644 index 0000000..eeb354a --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/Offset.java @@ -0,0 +1,55 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.expression.Expression; + +import java.io.Serializable; + +public class Offset implements Serializable { + private Expression offsetExpression = null; + private String offsetParam = null; + + public Expression getOffset() { + return offsetExpression; + } + + public void setOffset(Expression offsetExpression) { + this.offsetExpression = offsetExpression; + } + + public String getOffsetParam() { + return offsetParam; + } + + public void setOffsetParam(String s) { + offsetParam = s; + } + + + @Override + public String toString() { + return " OFFSET " + offsetExpression + (offsetParam != null ? " " + offsetParam : ""); + } + + public Offset withOffset(Expression offsetExpression) { + this.setOffset(offsetExpression); + return this; + } + + public Offset withOffsetParam(String offsetParam) { + this.setOffsetParam(offsetParam); + return this; + } + + public E getOffset(Class type) { + return type.cast(getOffset()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/OptimizeFor.java b/src/main/java/net/sf/jsqlparser/statement/select/OptimizeFor.java new file mode 100644 index 0000000..3e34219 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/OptimizeFor.java @@ -0,0 +1,42 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import java.io.Serializable; + +/** + * A optimize for clause. + */ +public class OptimizeFor implements Serializable { + + private long rowCount; + + public OptimizeFor(long rowCount) { + this.rowCount = rowCount; + } + + public long getRowCount() { + return rowCount; + } + + public void setRowCount(long l) { + rowCount = l; + } + + @Override + public String toString() { + return " OPTIMIZE FOR " + rowCount + " ROWS"; + } + + public OptimizeFor withRowCount(long rowCount) { + this.setRowCount(rowCount); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/OrderByElement.java b/src/main/java/net/sf/jsqlparser/statement/select/OrderByElement.java new file mode 100644 index 0000000..8ac4de9 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/OrderByElement.java @@ -0,0 +1,123 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import java.io.Serializable; + +import net.sf.jsqlparser.expression.Expression; + +public class OrderByElement implements Serializable { + + private Expression expression; + // postgres rollup is an ExpressionList + private boolean mysqlWithRollup = false; + private boolean asc = true; + private boolean ascDescPresent = false; + private NullOrdering nullOrdering; + + public boolean isAsc() { + return asc; + } + + public void setAsc(boolean asc) { + this.asc = asc; + } + + public NullOrdering getNullOrdering() { + return nullOrdering; + } + + public void setNullOrdering(NullOrdering nullOrdering) { + this.nullOrdering = nullOrdering; + } + + public boolean isAscDescPresent() { + return ascDescPresent; + } + + public void setAscDescPresent(boolean ascDescPresent) { + this.ascDescPresent = ascDescPresent; + } + + public T accept(OrderByVisitor orderByVisitor, S context) { + return orderByVisitor.visit(this, context); + } + + public Expression getExpression() { + return expression; + } + + public void setExpression(Expression expression) { + this.expression = expression; + } + + @Override + public String toString() { + StringBuilder b = new StringBuilder(); + b.append(expression.toString()); + + if (!asc) { + b.append(" DESC"); + } else if (ascDescPresent) { + b.append(" ASC"); + } + + if (nullOrdering != null) { + b.append(' '); + b.append(nullOrdering == NullOrdering.NULLS_FIRST ? "NULLS FIRST" : "NULLS LAST"); + } + if (isMysqlWithRollup()) { + b.append(" WITH ROLLUP"); + } + return b.toString(); + } + + public OrderByElement withExpression(Expression expression) { + this.setExpression(expression); + return this; + } + + public OrderByElement withAsc(boolean asc) { + this.setAsc(asc); + return this; + } + + public OrderByElement withAscDescPresent(boolean ascDescPresent) { + this.setAscDescPresent(ascDescPresent); + return this; + } + + public OrderByElement withNullOrdering(NullOrdering nullOrdering) { + this.setNullOrdering(nullOrdering); + return this; + } + + public E getExpression(Class type) { + return type.cast(getExpression()); + } + + public boolean isMysqlWithRollup() { + return mysqlWithRollup; + } + + public OrderByElement setMysqlWithRollup(boolean mysqlWithRollup) { + this.mysqlWithRollup = mysqlWithRollup; + return this; + } + + public enum NullOrdering { + NULLS_FIRST, NULLS_LAST; + + public static NullOrdering from(String ordering) { + return Enum.valueOf(NullOrdering.class, ordering.toUpperCase()); + } + } + +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/OrderByVisitor.java b/src/main/java/net/sf/jsqlparser/statement/select/OrderByVisitor.java new file mode 100644 index 0000000..8b43f7c --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/OrderByVisitor.java @@ -0,0 +1,19 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +public interface OrderByVisitor { + + T visit(OrderByElement orderBy, S context); + + default void visit(OrderByElement orderBy) { + this.visit(orderBy, null); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/OrderByVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/statement/select/OrderByVisitorAdapter.java new file mode 100644 index 0000000..2ab0a50 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/OrderByVisitorAdapter.java @@ -0,0 +1,19 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +@SuppressWarnings({"PMD.UncommentedEmptyMethodBody"}) +public class OrderByVisitorAdapter implements OrderByVisitor { + + @Override + public T visit(OrderByElement orderBy, S context) { + return null; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/ParenthesedFromItem.java b/src/main/java/net/sf/jsqlparser/statement/select/ParenthesedFromItem.java new file mode 100644 index 0000000..2e4c44e --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/ParenthesedFromItem.java @@ -0,0 +1,147 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.expression.Alias; +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +public class ParenthesedFromItem extends ASTNodeAccessImpl implements FromItem { + private FromItem fromItem; + private List joins; + private Alias alias; + private Pivot pivot; + private UnPivot unPivot; + + public ParenthesedFromItem() {} + + public ParenthesedFromItem(FromItem fromItem) { + setFromItem(fromItem); + } + + public FromItem getFromItem() { + return fromItem; + } + + public final void setFromItem(FromItem fromItem) { + this.fromItem = fromItem; + } + + public List getJoins() { + return joins; + } + + public void setJoins(List list) { + joins = list; + } + + public Join getJoin(int index) { + return joins.get(index); + } + + public FromItem addJoins(Join... joins) { + List list = Optional.ofNullable(getJoins()).orElseGet(ArrayList::new); + Collections.addAll(list, joins); + return withJoins(list); + } + + public FromItem withJoins(List joins) { + this.setJoins(joins); + return this; + } + + @Override + public T accept(FromItemVisitor fromItemVisitor, S context) { + return fromItemVisitor.visit(this, context); + } + + public StringBuilder appendTo(StringBuilder builder) { + builder.append("("); + builder.append(fromItem); + if (joins != null) { + for (Join join : joins) { + if (join.isSimple()) { + builder.append(", ").append(join); + } else { + builder.append(" ").append(join); + } + } + } + builder.append(")"); + + if (alias != null) { + builder.append(alias); + } + + if (pivot != null) { + builder.append(pivot); + } + + if (unPivot != null) { + builder.append(unPivot); + } + + return builder; + } + + @Override + public String toString() { + return appendTo(new StringBuilder()).toString(); + } + + @Override + public Alias getAlias() { + return alias; + } + + @Override + public void setAlias(Alias alias) { + this.alias = alias; + } + + @Override + public Pivot getPivot() { + return pivot; + } + + @Override + public void setPivot(Pivot pivot) { + this.pivot = pivot; + } + + @Override + public UnPivot getUnPivot() { + return unPivot; + } + + @Override + public void setUnPivot(UnPivot unpivot) { + this.unPivot = unpivot; + } + + public ParenthesedFromItem withFromItem(FromItem fromItem) { + this.setFromItem(fromItem); + return this; + } + + @Override + public ParenthesedFromItem withAlias(Alias alias) { + this.setAlias(alias); + return this; + } + + public E getFromItem(Class type) { + return type.cast(getFromItem()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/ParenthesedSelect.java b/src/main/java/net/sf/jsqlparser/statement/select/ParenthesedSelect.java new file mode 100644 index 0000000..0fc9997 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/ParenthesedSelect.java @@ -0,0 +1,168 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2023 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.expression.Alias; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.schema.Table; + +import java.util.Collection; +import java.util.List; + +public class ParenthesedSelect extends Select implements FromItem { + Alias alias; + Pivot pivot; + UnPivot unPivot; + Select select; + + public ParenthesedSelect() {} + + public ParenthesedSelect(FromItem fromItem) { + this.select = new PlainSelect(fromItem); + this.alias = getAliasFromItem(fromItem); + } + + public ParenthesedSelect(FromItem fromItem, Expression whereExpressions) { + this.select = new PlainSelect(fromItem, whereExpressions); + this.alias = getAliasFromItem(fromItem); + } + + public ParenthesedSelect(FromItem fromItem, Collection orderByExpressions) { + this.select = new PlainSelect(fromItem, orderByExpressions); + this.alias = getAliasFromItem(fromItem); + } + + public ParenthesedSelect(FromItem fromItem, Expression whereExpressions, + Collection orderByExpressions) { + this.select = new PlainSelect(fromItem, whereExpressions, orderByExpressions); + this.alias = getAliasFromItem(fromItem); + } + + public ParenthesedSelect(Collection selectExpressions, FromItem fromItem) { + this.select = new PlainSelect(selectExpressions, fromItem); + this.alias = getAliasFromItem(fromItem); + } + + public ParenthesedSelect(Collection selectExpressions, FromItem fromItem, + Expression whereExpressions) { + this.select = new PlainSelect(selectExpressions, fromItem, whereExpressions); + this.alias = getAliasFromItem(fromItem); + } + + public ParenthesedSelect(Collection selectExpressions, FromItem fromItem, + Collection orderByExpressions) { + this.select = new PlainSelect(selectExpressions, fromItem, orderByExpressions); + this.alias = getAliasFromItem(fromItem); + } + + public ParenthesedSelect(Collection selectExpressions, FromItem fromItem, + Expression whereExpressions, Collection orderByExpressions) { + this.select = + new PlainSelect(selectExpressions, fromItem, whereExpressions, orderByExpressions); + this.alias = getAliasFromItem(fromItem); + } + + private static Alias getAliasFromItem(FromItem fromItem) { + if (fromItem instanceof Table && fromItem.getAlias() == null) { + Table t = (Table) fromItem; + return new Alias(t.getName(), true); + } else { + return new Alias(fromItem.getAlias().getName(), true); + } + } + + @Override + public Alias getAlias() { + return alias; + } + + @Override + public void setAlias(Alias alias) { + this.alias = alias; + } + + public ParenthesedSelect withAlias(Alias alias) { + this.setAlias(alias); + return this; + } + + @Override + public Pivot getPivot() { + return pivot; + } + + @Override + public void setPivot(Pivot pivot) { + this.pivot = pivot; + } + + public UnPivot getUnPivot() { + return unPivot; + } + + public void setUnPivot(UnPivot unPivot) { + this.unPivot = unPivot; + } + + public Select getSelect() { + return select; + } + + public void setSelect(Select select) { + this.select = select; + } + + public Values getValues() { + return (Values) select; + } + + public PlainSelect getPlainSelect() { + return (PlainSelect) select; + } + + public SetOperationList getSetOperationList() { + return (SetOperationList) select; + } + + public ParenthesedSelect withSelect(Select selectBody) { + setSelect(selectBody); + return this; + } + + public ParenthesedSelect withOrderByElements(List orderByElements) { + this.select.setOrderByElements(orderByElements); + return this; + } + + @Override + public T accept(SelectVisitor selectVisitor, S context) { + return selectVisitor.visit(this, context); + } + + @Override + public T accept(FromItemVisitor fromItemVisitor, S context) { + return fromItemVisitor.visit(this, context); + } + + public StringBuilder appendSelectBodyTo(StringBuilder builder) { + builder.append("(").append(select).append(")"); + if (alias != null) { + builder.append(alias); + } + + if (pivot != null) { + builder.append(" ").append(pivot); + } + if (unPivot != null) { + builder.append(" ").append(unPivot); + } + return builder; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/Pivot.java b/src/main/java/net/sf/jsqlparser/statement/select/Pivot.java new file mode 100644 index 0000000..87d20db --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/Pivot.java @@ -0,0 +1,169 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.expression.Alias; +import net.sf.jsqlparser.expression.Function; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.schema.Column; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +public class Pivot implements Serializable { + + private List> functionItems; + private ExpressionList forColumns; + private List> singleInItems; + private List>> multiInItems; + private Alias alias; + + public T accept(PivotVisitor pivotVisitor, S context) { + return pivotVisitor.visit(this, context); + } + + public List> getSingleInItems() { + return singleInItems; + } + + public void setSingleInItems(List> singleInItems) { + this.singleInItems = singleInItems; + } + + public List>> getMultiInItems() { + return multiInItems; + } + + public void setMultiInItems(List>> multiInItems) { + this.multiInItems = multiInItems; + } + + public List> getFunctionItems() { + return functionItems; + } + + public void setFunctionItems(List> functionItems) { + this.functionItems = functionItems; + } + + public ExpressionList getForColumns() { + return forColumns; + } + + public void setForColumns(ExpressionList forColumns) { + this.forColumns = forColumns; + } + + public List getInItems() { + return singleInItems == null ? multiInItems : singleInItems; + } + + public Alias getAlias() { + return alias; + } + + public void setAlias(Alias alias) { + this.alias = alias; + } + + @Override + public String toString() { + return "PIVOT (" + + PlainSelect.getStringList(functionItems) + + " FOR " + + PlainSelect.getStringList(forColumns, true, + forColumns != null && forColumns.size() > 1) + + " IN " + PlainSelect.getStringList(getInItems(), true, true) + ")" + + (alias != null ? alias.toString() : ""); + } + + public Pivot withFunctionItems(List> functionItems) { + this.setFunctionItems(functionItems); + return this; + } + + public Pivot withForColumns(ExpressionList forColumns) { + this.setForColumns(forColumns); + return this; + } + + public Pivot withSingleInItems(List> singleInItems) { + this.setSingleInItems(singleInItems); + return this; + } + + public Pivot withMultiInItems(List>> multiInItems) { + this.setMultiInItems(multiInItems); + return this; + } + + public Pivot withAlias(Alias alias) { + this.setAlias(alias); + return this; + } + + public Pivot addFunctionItems(SelectItem... functionItems) { + List> collection = + Optional.ofNullable(getFunctionItems()).orElseGet(ArrayList::new); + Collections.addAll(collection, functionItems); + return this.withFunctionItems(collection); + } + + public Pivot addFunctionItems(Collection> functionItems) { + List> collection = + Optional.ofNullable(getFunctionItems()).orElseGet(ArrayList::new); + collection.addAll(functionItems); + return this.withFunctionItems(collection); + } + + public Pivot addForColumns(Column... forColumns) { + return this.addForColumns(Arrays.asList(forColumns)); + } + + public Pivot addForColumns(Collection forColumns) { + ExpressionList collection = + Optional.ofNullable(getForColumns()).orElseGet(ExpressionList::new); + collection.addAll(forColumns); + return this.withForColumns(collection); + } + + public Pivot addSingleInItems(SelectItem... singleInItems) { + List> collection = + Optional.ofNullable(getSingleInItems()).orElseGet(ArrayList::new); + Collections.addAll(collection, singleInItems); + return this.withSingleInItems(collection); + } + + public Pivot addSingleInItems(Collection> singleInItems) { + List> collection = + Optional.ofNullable(getSingleInItems()).orElseGet(ArrayList::new); + collection.addAll(singleInItems); + return this.withSingleInItems(collection); + } + + public Pivot addMultiInItems(SelectItem>... multiInItems) { + List>> collection = + Optional.ofNullable(getMultiInItems()).orElseGet(ArrayList::new); + Collections.addAll(collection, multiInItems); + return this.withMultiInItems(collection); + } + + public Pivot addMultiInItems(Collection>> multiInItems) { + List>> collection = + Optional.ofNullable(getMultiInItems()).orElseGet(ArrayList::new); + collection.addAll(multiInItems); + return this.withMultiInItems(collection); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/PivotVisitor.java b/src/main/java/net/sf/jsqlparser/statement/select/PivotVisitor.java new file mode 100644 index 0000000..2706942 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/PivotVisitor.java @@ -0,0 +1,31 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +public interface PivotVisitor { + + T visit(Pivot pivot, S context); + + default void visit(Pivot pivot) { + this.visit(pivot, null); + } + + T visit(PivotXml pivotXml, S context); + + default void visit(PivotXml pivotXml) { + this.visit(pivotXml, null); + } + + T visit(UnPivot unpivot, S context); + + default void visit(UnPivot unpivot) { + this.visit(unpivot, null); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/PivotVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/statement/select/PivotVisitorAdapter.java new file mode 100644 index 0000000..6e7fce9 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/PivotVisitorAdapter.java @@ -0,0 +1,32 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +@SuppressWarnings({"PMD.UncommentedEmptyMethodBody"}) +public class PivotVisitorAdapter implements PivotVisitor { + + @Override + public T visit(Pivot pivot, S context) { + + return null; + } + + @Override + public T visit(PivotXml pivot, S context) { + + return null; + } + + @Override + public T visit(UnPivot unpivot, S context) { + + return null; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/PivotXml.java b/src/main/java/net/sf/jsqlparser/statement/select/PivotXml.java new file mode 100644 index 0000000..b016d79 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/PivotXml.java @@ -0,0 +1,139 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.expression.Alias; +import net.sf.jsqlparser.expression.Function; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.schema.Column; + +import java.util.Collection; +import java.util.List; + +public class PivotXml extends Pivot { + + private Select inSelect; + private boolean inAny = false; + + @Override + public T accept(PivotVisitor pivotVisitor, S context) { + return pivotVisitor.visit(this, context); + } + + public Select getInSelect() { + return inSelect; + } + + public void setInSelect(Select inSelect) { + this.inSelect = inSelect; + } + + public boolean isInAny() { + return inAny; + } + + public void setInAny(boolean inAny) { + this.inAny = inAny; + } + + @Override + public String toString() { + List forColumns = getForColumns(); + String in = inAny ? "ANY" + : inSelect == null ? PlainSelect.getStringList(getInItems()) : inSelect.toString(); + return "PIVOT XML (" + + PlainSelect.getStringList(getFunctionItems()) + + " FOR " + + PlainSelect.getStringList(forColumns, true, + forColumns != null && forColumns.size() > 1) + + " IN (" + in + "))"; + } + + public PivotXml withInSelect(Select inSelect) { + this.setInSelect(inSelect); + return this; + } + + public PivotXml withInAny(boolean inAny) { + this.setInAny(inAny); + return this; + } + + public E getInSelect(Class type) { + return type.cast(getInSelect()); + } + + @Override + public PivotXml withAlias(Alias alias) { + return (PivotXml) super.withAlias(alias); + } + + @Override + public PivotXml withFunctionItems(List> functionItems) { + return (PivotXml) super.withFunctionItems(functionItems); + } + + @Override + public PivotXml withForColumns(ExpressionList forColumns) { + return (PivotXml) super.withForColumns(forColumns); + } + + @Override + public PivotXml withSingleInItems(List> singleInItems) { + return (PivotXml) super.withSingleInItems(singleInItems); + } + + @Override + public PivotXml withMultiInItems(List>> multiInItems) { + return (PivotXml) super.withMultiInItems(multiInItems); + } + + @Override + public PivotXml addFunctionItems(Collection> functionItems) { + return (PivotXml) super.addFunctionItems(functionItems); + } + + @Override + public PivotXml addFunctionItems(SelectItem... functionItems) { + return (PivotXml) super.addFunctionItems(functionItems); + } + + @Override + public PivotXml addForColumns(Collection forColumns) { + return (PivotXml) super.addForColumns(forColumns); + } + + @Override + public PivotXml addForColumns(Column... forColumns) { + return (PivotXml) super.addForColumns(forColumns); + } + + @Override + public PivotXml addSingleInItems(Collection> singleInItems) { + return (PivotXml) super.addSingleInItems(singleInItems); + } + + @Override + public PivotXml addSingleInItems(SelectItem... singleInItems) { + return (PivotXml) super.addSingleInItems(singleInItems); + } + + @Override + public PivotXml addMultiInItems(SelectItem>... multiInItems) { + return (PivotXml) super.addMultiInItems(multiInItems); + } + + @Override + public PivotXml addMultiInItems( + Collection>> multiInItems) { + return (PivotXml) super.addMultiInItems(multiInItems); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/PlainSelect.java b/src/main/java/net/sf/jsqlparser/statement/select/PlainSelect.java new file mode 100644 index 0000000..eb10686 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/PlainSelect.java @@ -0,0 +1,744 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.expression.Alias; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.OracleHierarchicalExpression; +import net.sf.jsqlparser.expression.OracleHint; +import net.sf.jsqlparser.expression.WindowDefinition; +import net.sf.jsqlparser.schema.Table; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; + +import static java.util.stream.Collectors.joining; + +@SuppressWarnings({"PMD.CyclomaticComplexity"}) +public class PlainSelect extends Select { + + private Distinct distinct = null; + private BigQuerySelectQualifier bigQuerySelectQualifier = null; + private List> selectItems; + private List

intoTables; + private FromItem fromItem; + private List lateralViews; + private List joins; + private Expression where; + private GroupByElement groupBy; + private Expression having; + private Expression qualify; + private OptimizeFor optimizeFor; + private Skip skip; + private boolean mySqlHintStraightJoin; + private First first; + private Top top; + private OracleHierarchicalExpression oracleHierarchical = null; + private OracleHint oracleHint = null; + private boolean mySqlSqlCalcFoundRows = false; + private MySqlSqlCacheFlags mySqlCacheFlag = null; + private String forXmlPath; + private KSQLWindow ksqlWindow = null; + private boolean emitChanges = false; + private List windowDefinitions; + /** + * @see Clickhouse + * FINAL + */ + private boolean isUsingFinal = false; + private boolean isUsingOnly = false; + private boolean useWithNoLog = false; + private Table intoTempTable = null; + + public PlainSelect() {} + + public PlainSelect(FromItem fromItem) { + addSelectItem(new AllColumns()); + setFromItem(fromItem); + } + + public PlainSelect(FromItem fromItem, Expression whereExpressions) { + addSelectItem(new AllColumns()); + setFromItem(fromItem); + setWhere(whereExpressions); + } + + public PlainSelect(FromItem fromItem, Collection orderByExpressions) { + addSelectItem(new AllColumns()); + setFromItem(fromItem); + addOrderByExpressions(orderByExpressions); + } + + public PlainSelect(FromItem fromItem, Expression whereExpressions, + Collection orderByExpressions) { + addSelectItem(new AllColumns()); + setFromItem(fromItem); + setWhere(whereExpressions); + addOrderByExpressions(orderByExpressions); + } + + public PlainSelect(Collection selectExpressions, FromItem fromItem) { + addSelectExpressions(selectExpressions); + setFromItem(fromItem); + } + + public PlainSelect(Collection selectExpressions, FromItem fromItem, + Expression whereExpressions) { + addSelectExpressions(selectExpressions); + setFromItem(fromItem); + setWhere(whereExpressions); + } + + public PlainSelect(Collection selectExpressions, FromItem fromItem, + Collection orderByExpressions) { + addSelectExpressions(selectExpressions); + setFromItem(fromItem); + addOrderByExpressions(orderByExpressions); + } + + public PlainSelect(Collection selectExpressions, FromItem fromItem, + Expression whereExpressions, Collection orderByExpressions) { + addSelectExpressions(selectExpressions); + setFromItem(fromItem); + setWhere(whereExpressions); + addOrderByExpressions(orderByExpressions); + } + + @Deprecated + public boolean isUseBrackets() { + return false; + } + + public FromItem getFromItem() { + return fromItem; + } + + public void setFromItem(FromItem item) { + fromItem = item; + } + + public List
getIntoTables() { + return intoTables; + } + + public void setIntoTables(List
intoTables) { + this.intoTables = intoTables; + } + + public List> getSelectItems() { + return selectItems; + } + + public void setSelectItems(List> list) { + selectItems = list; + } + + public SelectItem getSelectItem(int index) { + return selectItems.get(index); + } + + public Expression getWhere() { + return where; + } + + public void setWhere(Expression where) { + this.where = where; + } + + public PlainSelect withFromItem(FromItem item) { + this.setFromItem(item); + return this; + } + + public PlainSelect withSelectItems(List> list) { + this.setSelectItems(list); + return this; + } + + public PlainSelect withSelectItems(SelectItem... selectItems) { + return this.withSelectItems(Arrays.asList(selectItems)); + } + + public PlainSelect addSelectItems(SelectItem... items) { + selectItems = Optional.ofNullable(selectItems).orElseGet(ArrayList::new); + selectItems.addAll(Arrays.asList(items)); + return this; + } + + public PlainSelect addSelectExpressions(Collection expressions) { + selectItems = Optional.ofNullable(selectItems).orElseGet(ArrayList::new); + for (Expression expression : expressions) { + selectItems.add(SelectItem.from(expression)); + } + return this; + } + + public PlainSelect addSelectItems(Expression... expressions) { + return this.addSelectExpressions(Arrays.asList(expressions)); + } + + public PlainSelect addSelectItem(Expression expression, Alias alias) { + selectItems = Optional.ofNullable(selectItems).orElseGet(ArrayList::new); + selectItems.add(new SelectItem<>(expression, alias)); + return this; + } + + public PlainSelect addSelectItem(Expression expression) { + return addSelectItem(expression, null); + } + + public List getLateralViews() { + return lateralViews; + } + + public void setLateralViews(Collection lateralViews) { + if (this.lateralViews == null && lateralViews != null) { + this.lateralViews = new ArrayList<>(); + } else { + this.lateralViews.clear(); + } + + if (lateralViews != null) { + this.lateralViews.addAll(lateralViews); + } else { + this.lateralViews = null; + } + } + + public PlainSelect addLateralView(LateralView lateralView) { + if (this.lateralViews == null) { + this.lateralViews = new ArrayList<>(); + } + + this.lateralViews.add(lateralView); + return this; + } + + public PlainSelect withLateralViews(Collection lateralViews) { + this.setLateralViews(lateralViews); + return this; + } + + /** + * The list of {@link Join}s + * + * @return the list of {@link Join}s + */ + public List getJoins() { + return joins; + } + + public void setJoins(List list) { + joins = list; + } + + public Join getJoin(int index) { + return joins.get(index); + } + + public PlainSelect addJoins(Join... joins) { + List list = Optional.ofNullable(getJoins()).orElseGet(ArrayList::new); + Collections.addAll(list, joins); + return withJoins(list); + } + + public PlainSelect withJoins(List joins) { + this.setJoins(joins); + return this; + } + + public boolean isUsingFinal() { + return isUsingFinal; + } + + public void setUsingFinal(boolean usingFinal) { + this.isUsingFinal = usingFinal; + } + + public PlainSelect withUsingFinal(boolean usingFinal) { + this.setUsingFinal(usingFinal); + return this; + } + + public boolean isUsingOnly() { + return isUsingOnly; + } + + public void setUsingOnly(boolean usingOnly) { + isUsingOnly = usingOnly; + } + + public PlainSelect withUsingOnly(boolean usingOnly) { + this.setUsingOnly(usingOnly); + return this; + } + + public boolean isUseWithNoLog() { + return useWithNoLog; + } + + public void setUseWithNoLog(boolean useWithNoLog) { + this.useWithNoLog = useWithNoLog; + } + + public PlainSelect withUseWithNoLog(boolean useWithNoLog) { + this.setUseWithNoLog(useWithNoLog); + return this; + } + + public Table getIntoTempTable() { + return intoTempTable; + } + + public void setIntoTempTable(Table intoTempTable) { + this.intoTempTable = intoTempTable; + } + + public PlainSelect withIntoTempTable(Table intoTempTable) { + this.setIntoTempTable(intoTempTable); + return this; + } + + @Override + public T accept(SelectVisitor selectVisitor, S context) { + return selectVisitor.visit(this, context); + } + + public OptimizeFor getOptimizeFor() { + return optimizeFor; + } + + public void setOptimizeFor(OptimizeFor optimizeFor) { + this.optimizeFor = optimizeFor; + } + + public Top getTop() { + return top; + } + + public void setTop(Top top) { + this.top = top; + } + + public Skip getSkip() { + return skip; + } + + public void setSkip(Skip skip) { + this.skip = skip; + } + + public boolean getMySqlHintStraightJoin() { + return this.mySqlHintStraightJoin; + } + + public void setMySqlHintStraightJoin(boolean mySqlHintStraightJoin) { + this.mySqlHintStraightJoin = mySqlHintStraightJoin; + } + + public First getFirst() { + return first; + } + + public void setFirst(First first) { + this.first = first; + } + + public Distinct getDistinct() { + return distinct; + } + + public void setDistinct(Distinct distinct) { + this.distinct = distinct; + } + + public BigQuerySelectQualifier getBigQuerySelectQualifier() { + return bigQuerySelectQualifier; + } + + public PlainSelect setBigQuerySelectQualifier(BigQuerySelectQualifier bigQuerySelectQualifier) { + this.bigQuerySelectQualifier = bigQuerySelectQualifier; + return this; + } + + public Expression getHaving() { + return having; + } + + public void setHaving(Expression expression) { + having = expression; + } + + public Expression getQualify() { + return qualify; + } + + public PlainSelect setQualify(Expression qualify) { + this.qualify = qualify; + return this; + } + + /** + * A list of {@link Expression}s of the GROUP BY clause. It is null in case there is no GROUP BY + * clause + * + * @return a list of {@link Expression}s + */ + public GroupByElement getGroupBy() { + return this.groupBy; + } + + public void setGroupByElement(GroupByElement groupBy) { + this.groupBy = groupBy; + } + + public PlainSelect addGroupByColumnReference(Expression expr) { + this.groupBy = Optional.ofNullable(groupBy).orElseGet(GroupByElement::new); + this.groupBy.addGroupByExpression(expr); + return this; + } + + public OracleHierarchicalExpression getOracleHierarchical() { + return oracleHierarchical; + } + + public void setOracleHierarchical(OracleHierarchicalExpression oracleHierarchical) { + this.oracleHierarchical = oracleHierarchical; + } + + public OracleHint getOracleHint() { + return oracleHint; + } + + public void setOracleHint(OracleHint oracleHint) { + this.oracleHint = oracleHint; + } + + public String getForXmlPath() { + return forXmlPath; + } + + public void setForXmlPath(String forXmlPath) { + this.forXmlPath = forXmlPath; + } + + public KSQLWindow getKsqlWindow() { + return ksqlWindow; + } + + public void setKsqlWindow(KSQLWindow ksqlWindow) { + this.ksqlWindow = ksqlWindow; + } + + public boolean isEmitChanges() { + return emitChanges; + } + + public void setEmitChanges(boolean emitChanges) { + this.emitChanges = emitChanges; + } + + public List getWindowDefinitions() { + return windowDefinitions; + } + + public void setWindowDefinitions(List windowDefinitions) { + this.windowDefinitions = windowDefinitions; + } + + @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.ExcessiveMethodLength", + "PMD.NPathComplexity"}) + public StringBuilder appendSelectBodyTo(StringBuilder builder) { + builder.append("SELECT "); + + if (this.mySqlHintStraightJoin) { + builder.append("STRAIGHT_JOIN "); + } + + if (oracleHint != null) { + builder.append(oracleHint).append(" "); + } + + if (skip != null) { + builder.append(skip).append(" "); + } + + if (first != null) { + builder.append(first).append(" "); + } + + if (distinct != null) { + builder.append(distinct).append(" "); + } + + if (bigQuerySelectQualifier != null) { + switch (bigQuerySelectQualifier) { + case AS_STRUCT: + builder.append("AS STRUCT "); + break; + case AS_VALUE: + builder.append("AS VALUE "); + break; + } + } + + if (top != null) { + builder.append(top).append(" "); + } + if (mySqlCacheFlag != null) { + builder.append(mySqlCacheFlag.name()).append(" "); + } + if (mySqlSqlCalcFoundRows) { + builder.append("SQL_CALC_FOUND_ROWS").append(" "); + } + builder.append(getStringList(selectItems)); + + if (intoTables != null) { + builder.append(" INTO "); + for (Iterator
iter = intoTables.iterator(); iter.hasNext();) { + builder.append(iter.next().toString()); + if (iter.hasNext()) { + builder.append(", "); + } + } + } + + if (fromItem != null) { + builder.append(" FROM "); + if (isUsingOnly) { + builder.append("ONLY "); + } + builder.append(fromItem); + if (lateralViews != null) { + for (LateralView lateralView : lateralViews) { + builder.append(" ").append(lateralView); + } + } + if (joins != null) { + for (Join join : joins) { + if (join.isSimple()) { + builder.append(", ").append(join); + } else { + builder.append(" ").append(join); + } + } + } + + if (isUsingFinal) { + builder.append(" FINAL"); + } + + if (ksqlWindow != null) { + builder.append(" WINDOW ").append(ksqlWindow); + } + if (where != null) { + builder.append(" WHERE ").append(where); + } + if (oracleHierarchical != null) { + builder.append(oracleHierarchical); + } + if (groupBy != null) { + builder.append(" ").append(groupBy); + } + if (having != null) { + builder.append(" HAVING ").append(having); + } + if (qualify != null) { + builder.append(" QUALIFY ").append(qualify); + } + if (windowDefinitions != null) { + builder.append(" WINDOW "); + builder.append(windowDefinitions.stream().map(WindowDefinition::toString) + .collect(joining(", "))); + } + if (emitChanges) { + builder.append(" EMIT CHANGES"); + } + } else { + // without from + if (where != null) { + builder.append(" WHERE ").append(where); + } + } + if (intoTempTable != null) { + builder.append(" INTO TEMP ").append(intoTempTable); + } + if (useWithNoLog) { + builder.append(" WITH NO LOG"); + } + return builder; + } + + @Override + @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.ExcessiveMethodLength", + "PMD.NPathComplexity"}) + public String toString() { + StringBuilder builder = new StringBuilder(); + super.appendTo(builder); + + if (optimizeFor != null) { + builder.append(optimizeFor); + } + + if (forXmlPath != null) { + builder.append(" FOR XML PATH(").append(forXmlPath).append(")"); + } + + return builder.toString(); + } + + public PlainSelect withMySqlSqlCalcFoundRows(boolean mySqlCalcFoundRows) { + this.setMySqlSqlCalcFoundRows(mySqlCalcFoundRows); + return this; + } + + public PlainSelect withMySqlSqlNoCache(MySqlSqlCacheFlags mySqlCacheFlag) { + this.setMySqlSqlCacheFlag(mySqlCacheFlag); + return this; + } + + public boolean getMySqlSqlCalcFoundRows() { + return this.mySqlSqlCalcFoundRows; + } + + public void setMySqlSqlCalcFoundRows(boolean mySqlCalcFoundRows) { + this.mySqlSqlCalcFoundRows = mySqlCalcFoundRows; + } + + public MySqlSqlCacheFlags getMySqlSqlCacheFlag() { + return this.mySqlCacheFlag; + } + + public void setMySqlSqlCacheFlag(MySqlSqlCacheFlags sqlCacheFlag) { + this.mySqlCacheFlag = sqlCacheFlag; + } + + public PlainSelect withDistinct(Distinct distinct) { + this.setDistinct(distinct); + return this; + } + + public PlainSelect withIntoTables(List
intoTables) { + this.setIntoTables(intoTables); + return this; + } + + public PlainSelect withWhere(Expression where) { + this.setWhere(where); + return this; + } + + public PlainSelect withOptimizeFor(OptimizeFor optimizeFor) { + this.setOptimizeFor(optimizeFor); + return this; + } + + public PlainSelect withSkip(Skip skip) { + this.setSkip(skip); + return this; + } + + public PlainSelect withMySqlHintStraightJoin(boolean mySqlHintStraightJoin) { + this.setMySqlHintStraightJoin(mySqlHintStraightJoin); + return this; + } + + public PlainSelect withFirst(First first) { + this.setFirst(first); + return this; + } + + public PlainSelect withTop(Top top) { + this.setTop(top); + return this; + } + + public PlainSelect withOracleHierarchical(OracleHierarchicalExpression oracleHierarchical) { + this.setOracleHierarchical(oracleHierarchical); + return this; + } + + public PlainSelect withOracleHint(OracleHint oracleHint) { + this.setOracleHint(oracleHint); + return this; + } + + public PlainSelect withOracleSiblings(boolean oracleSiblings) { + this.setOracleSiblings(oracleSiblings); + return this; + } + + public PlainSelect withForXmlPath(String forXmlPath) { + this.setForXmlPath(forXmlPath); + return this; + } + + public PlainSelect withKsqlWindow(KSQLWindow ksqlWindow) { + this.setKsqlWindow(ksqlWindow); + return this; + } + + public PlainSelect withNoWait(boolean noWait) { + this.setNoWait(noWait); + return this; + } + + public PlainSelect withHaving(Expression having) { + this.setHaving(having); + return this; + } + + public PlainSelect addSelectItems(Collection> selectItems) { + List> collection = + Optional.ofNullable(getSelectItems()).orElseGet(ArrayList::new); + collection.addAll(selectItems); + return this.withSelectItems(collection); + } + + public PlainSelect addIntoTables(Table... intoTables) { + List
collection = Optional.ofNullable(getIntoTables()).orElseGet(ArrayList::new); + Collections.addAll(collection, intoTables); + return this.withIntoTables(collection); + } + + public PlainSelect addIntoTables(Collection intoTables) { + List
collection = Optional.ofNullable(getIntoTables()).orElseGet(ArrayList::new); + collection.addAll(intoTables); + return this.withIntoTables(collection); + } + + public PlainSelect addJoins(Collection joins) { + List collection = Optional.ofNullable(getJoins()).orElseGet(ArrayList::new); + collection.addAll(joins); + return this.withJoins(collection); + } + + public E getFromItem(Class type) { + return type.cast(getFromItem()); + } + + public E getWhere(Class type) { + return type.cast(getWhere()); + } + + public E getHaving(Class type) { + return type.cast(getHaving()); + } + + public enum BigQuerySelectQualifier { + AS_STRUCT, AS_VALUE + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/SampleClause.java b/src/main/java/net/sf/jsqlparser/statement/select/SampleClause.java new file mode 100644 index 0000000..8aeea8d --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/SampleClause.java @@ -0,0 +1,128 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2023 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +public class SampleClause { + private SampleKeyword keyword; + private SampleMethod method; + private Number percentageArgument; + private Number repeatArgument; + // Oracle Specific + private Number seedArgument; + + public SampleClause(String keyword, String method, Number percentageArgument, + Number repeatArgument, Number seedArgument) { + this.keyword = SampleKeyword.from(keyword); + this.method = method == null || method.length() == 0 ? null : SampleMethod.from(method); + this.percentageArgument = percentageArgument; + this.repeatArgument = repeatArgument; + this.seedArgument = seedArgument; + } + + public SampleClause() { + this(SampleKeyword.TABLESAMPLE.toString(), null, null, null, null); + } + + public SampleClause(String keyword) { + this(keyword, null, null, null, null); + } + + public SampleKeyword getKeyword() { + return keyword; + } + + public SampleClause setKeyword(SampleKeyword keyword) { + this.keyword = keyword; + return this; + } + + public Number getPercentageArgument() { + return percentageArgument; + } + + public SampleClause setPercentageArgument(Number percentageArgument) { + this.percentageArgument = percentageArgument; + return this; + } + + public Number getRepeatArgument() { + return repeatArgument; + } + + public SampleClause setRepeatArgument(Number repeatArgument) { + this.repeatArgument = repeatArgument; + return this; + } + + public Number getSeedArgument() { + return seedArgument; + } + + public SampleClause setSeedArgument(Number seedArgument) { + this.seedArgument = seedArgument; + return this; + } + + public SampleMethod getMethod() { + return method; + } + + public SampleClause setMethod(SampleMethod method) { + this.method = method; + return this; + } + + public SampleClause setMethod(String method) { + this.method = method == null || method.length() == 0 ? null : SampleMethod.from(method); + return this; + } + + public StringBuilder appendTo(StringBuilder builder) { + + builder.append(" ").append(keyword); + if (method != null) { + builder.append(" ").append(method); + } + + if (percentageArgument != null) { + builder.append(" (").append(percentageArgument).append(")"); + } + + if (repeatArgument != null) { + builder.append(" REPEATABLE (").append(repeatArgument).append(")"); + } + + if (seedArgument != null) { + builder.append(" SEED (").append(seedArgument).append(")"); + } + + return builder; + } + + public String toString() { + return appendTo(new StringBuilder()).toString(); + } + + public enum SampleKeyword { + SAMPLE, TABLESAMPLE; + + public static SampleKeyword from(String sampleKeyword) { + return Enum.valueOf(SampleKeyword.class, sampleKeyword.toUpperCase()); + } + } + + public enum SampleMethod { + BERNOULLI, SYSTEM, BLOCK; + + public static SampleMethod from(String sampleMethod) { + return Enum.valueOf(SampleMethod.class, sampleMethod.toUpperCase()); + } + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/Select.java b/src/main/java/net/sf/jsqlparser/statement/select/Select.java new file mode 100644 index 0000000..e6cc739 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/Select.java @@ -0,0 +1,443 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.StatementVisitor; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; + +public abstract class Select extends ASTNodeAccessImpl implements Statement, Expression { + protected Table forUpdateTable = null; + List withItemsList; + Limit limitBy; + Limit limit; + Offset offset; + Fetch fetch; + WithIsolation isolation; + boolean oracleSiblings = false; + + ForClause forClause = null; + + List orderByElements; + ForMode forMode = null; + private boolean skipLocked; + private Wait wait; + private boolean noWait = false; + + public static String orderByToString(List orderByElements) { + return orderByToString(false, orderByElements); + } + + public static String orderByToString(boolean oracleSiblings, + List orderByElements) { + return getFormattedList(orderByElements, oracleSiblings ? "ORDER SIBLINGS BY" : "ORDER BY"); + } + + public static String getFormattedList(List list, String expression) { + return getFormattedList(list, expression, true, false); + } + + public static String getFormattedList(List list, String expression, boolean useComma, + boolean useBrackets) { + String sql = getStringList(list, useComma, useBrackets); + + if (!sql.isEmpty()) { + if (!expression.isEmpty()) { + sql = " " + expression + " " + sql; + } else { + sql = " " + sql; + } + } + + return sql; + } + + /** + * List the toString out put of the objects in the List comma separated. If the List is null or + * empty an empty string is returned. + *

+ * The same as getStringList(list, true, false) + * + * @param list list of objects with toString methods + * @return comma separated list of the elements in the list + * @see #getStringList(List, boolean, boolean) + */ + public static String getStringList(List list) { + return getStringList(list, true, false); + } + + /** + * List the toString out put of the objects in the List that can be comma separated. If the List + * is null or empty an empty string is returned. + * + * @param list list of objects with toString methods + * @param useComma true if the list has to be comma separated + * @param useBrackets true if the list has to be enclosed in brackets + * @return comma separated list of the elements in the list + */ + public static String getStringList(List list, boolean useComma, boolean useBrackets) { + return appendStringListTo(new StringBuilder(), list, useComma, useBrackets).toString(); + } + + /** + * Append the toString out put of the objects in the List (that can be comma separated). If the + * List is null or empty an empty string is returned. + * + * @param list list of objects with toString methods + * @param useComma true if the list has to be comma separated + * @param useBrackets true if the list has to be enclosed in brackets + * @return comma separated list of the elements in the list + */ + public static StringBuilder appendStringListTo(StringBuilder builder, List list, + boolean useComma, boolean useBrackets) { + if (list != null) { + String comma = useComma ? ", " : " "; + + if (useBrackets) { + builder.append("("); + } + + int size = list.size(); + for (int i = 0; i < size; i++) { + builder.append(list.get(i)).append(i < size - 1 ? comma : ""); + } + + if (useBrackets) { + builder.append(")"); + } + } + return builder; + } + + public List getWithItemsList() { + return withItemsList; + } + + public void setWithItemsList(List withItemsList) { + this.withItemsList = withItemsList; + } + + public Select withWithItemsList(List withItemsList) { + this.setWithItemsList(withItemsList); + return this; + } + + public Select addWithItemsList(Collection withItemsList) { + List collection = + Optional.ofNullable(getWithItemsList()).orElseGet(ArrayList::new); + collection.addAll(withItemsList); + return this.withWithItemsList(collection); + } + + public Select addWithItemsList(WithItem... withItemsList) { + return addWithItemsList(Arrays.asList(withItemsList)); + } + + public boolean isOracleSiblings() { + return oracleSiblings; + } + + public void setOracleSiblings(boolean oracleSiblings) { + this.oracleSiblings = oracleSiblings; + } + + public boolean isNoWait() { + return this.noWait; + } + + public void setNoWait(boolean noWait) { + this.noWait = noWait; + } + + public Select withOracleSiblings(boolean oracleSiblings) { + this.setOracleSiblings(oracleSiblings); + return this; + } + + public ForClause getForClause() { + return forClause; + } + + public Select setForClause(ForClause forClause) { + this.forClause = forClause; + return this; + } + + public List getOrderByElements() { + return orderByElements; + } + + public void setOrderByElements(List orderByElements) { + this.orderByElements = orderByElements; + } + + public Select withOrderByElements(List orderByElements) { + this.setOrderByElements(orderByElements); + return this; + } + + public Select addOrderByElements(Collection orderByElements) { + List collection = + Optional.ofNullable(getOrderByElements()).orElseGet(ArrayList::new); + collection.addAll(orderByElements); + return this.withOrderByElements(collection); + } + + public Select addOrderByElements(OrderByElement... orderByElements) { + return this.addOrderByElements(Arrays.asList(orderByElements)); + } + + public Select addOrderByExpressions(Collection orderByExpressions) { + for (Expression e : orderByExpressions) { + addOrderByElements(new OrderByElement().withExpression(e)); + } + return this; + } + + public Select addOrderByElements(Expression... orderByExpressions) { + return addOrderByExpressions(Arrays.asList(orderByExpressions)); + } + + public Limit getLimit() { + return limit; + } + + public void setLimit(Limit limit) { + this.limit = limit; + } + + public Select withLimit(Limit limit) { + this.setLimit(limit); + return this; + } + + public Limit getLimitBy() { + return limitBy; + } + + public void setLimitBy(Limit limitBy) { + this.limitBy = limitBy; + } + + public E withLimitBy(Class type, Limit limitBy) { + this.setLimitBy(limitBy); + return type.cast(this); + } + + public Offset getOffset() { + return offset; + } + + public void setOffset(Offset offset) { + this.offset = offset; + } + + public Select withOffset(Offset offset) { + this.setOffset(offset); + return this; + } + + public Fetch getFetch() { + return fetch; + } + + public void setFetch(Fetch fetch) { + this.fetch = fetch; + } + + public Select withFetch(Fetch fetch) { + this.setFetch(fetch); + return this; + } + + public WithIsolation getIsolation() { + return isolation; + } + + public void setIsolation(WithIsolation isolation) { + this.isolation = isolation; + } + + public Select withIsolation(WithIsolation isolation) { + this.setIsolation(isolation); + return this; + } + + public ForMode getForMode() { + return this.forMode; + } + + public void setForMode(ForMode forMode) { + this.forMode = forMode; + } + + public Table getForUpdateTable() { + return this.forUpdateTable; + } + + public void setForUpdateTable(Table forUpdateTable) { + this.forUpdateTable = forUpdateTable; + } + + /** + * Returns the value of the {@link Wait} set for this SELECT + * + * @return the value of the {@link Wait} set for this SELECT + */ + public Wait getWait() { + return wait; + } + + /** + * Sets the {@link Wait} for this SELECT + * + * @param wait the {@link Wait} for this SELECT + */ + public void setWait(final Wait wait) { + this.wait = wait; + } + + public boolean isSkipLocked() { + return skipLocked; + } + + public void setSkipLocked(boolean skipLocked) { + this.skipLocked = skipLocked; + } + + public abstract StringBuilder appendSelectBodyTo(StringBuilder builder); + + @SuppressWarnings({"PMD.CyclomaticComplexity"}) + public StringBuilder appendTo(StringBuilder builder) { + if (withItemsList != null && !withItemsList.isEmpty()) { + builder.append("WITH "); + for (Iterator iter = withItemsList.iterator(); iter.hasNext();) { + WithItem withItem = iter.next(); + builder.append(withItem); + if (iter.hasNext()) { + builder.append(","); + } + builder.append(" "); + } + } + + appendSelectBodyTo(builder); + + if (forClause != null) { + forClause.appendTo(builder); + } + + builder.append(orderByToString(oracleSiblings, orderByElements)); + + if (limitBy != null) { + builder.append(limitBy); + } + if (limit != null) { + builder.append(limit); + } + if (offset != null) { + builder.append(offset); + } + if (fetch != null) { + builder.append(fetch); + } + if (isolation != null) { + builder.append(isolation); + } + if (forMode != null) { + builder.append(" FOR "); + builder.append(forMode.getValue()); + + if (getForUpdateTable() != null) { + builder.append(" OF ").append(forUpdateTable); + } + + if (wait != null) { + // Wait's toString will do the formatting for us + builder.append(wait); + } + + if (isNoWait()) { + builder.append(" NOWAIT"); + } else if (isSkipLocked()) { + builder.append(" SKIP LOCKED"); + } + } + + return builder; + } + + @Override + public String toString() { + return appendTo(new StringBuilder()).toString(); + } + + public abstract T accept(SelectVisitor selectVisitor, S context); + + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + @Deprecated + public Select getSelectBody() { + return this; + } + + public Values getValues() { + return (Values) this; + } + + public PlainSelect getPlainSelect() { + return (PlainSelect) this; + } + + public SetOperationList getSetOperationList() { + return (SetOperationList) this; + } + + public E as(Class type) { + return type.cast(this); + } + + public Select withForMode(ForMode forMode) { + this.setForMode(forMode); + return this; + } + + public Select withForUpdateTable(Table forUpdateTable) { + this.setForUpdateTable(forUpdateTable); + return this; + } + + public Select withSkipLocked(boolean skipLocked) { + this.setSkipLocked(skipLocked); + return this; + } + + public Select withWait(Wait wait) { + this.setWait(wait); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/SelectItem.java b/src/main/java/net/sf/jsqlparser/statement/select/SelectItem.java new file mode 100644 index 0000000..4f8112d --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/SelectItem.java @@ -0,0 +1,104 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.expression.Alias; +import net.sf.jsqlparser.expression.DoubleValue; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.LongValue; +import net.sf.jsqlparser.expression.StringValue; +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +public class SelectItem extends ASTNodeAccessImpl { + + private T expression; + private Alias alias; + + public SelectItem(T expression, Alias alias) { + this.expression = expression; + this.alias = alias; + } + + public SelectItem(T expression, String aliasName) { + this.expression = expression; + this.alias = new Alias(aliasName); + } + + public SelectItem(Long expression, String aliasName) { + this((T) new LongValue(expression), aliasName); + } + + public SelectItem(Integer expression, String aliasName) { + this((T) new LongValue(expression), aliasName); + } + + public SelectItem(Double expression, String aliasName) { + this((T) new DoubleValue(expression), aliasName); + } + + public SelectItem(String expression, String aliasName) { + this((T) new StringValue(expression), aliasName); + } + + public SelectItem() { + this(null, (Alias) null); + } + + public SelectItem(T expression) { + this(expression, (Alias) null); + } + + public static SelectItem from(Expression expression, Alias alias) { + return new SelectItem<>(expression, alias); + } + + public static SelectItem from(Expression expression) { + return from(expression, null); + } + + public Alias getAlias() { + return alias; + } + + public void setAlias(Alias alias) { + this.alias = alias; + } + + public T getExpression() { + return expression; + } + + public void setExpression(T expression) { + this.expression = expression; + } + + public K accept(SelectItemVisitor selectItemVisitor, S context) { + return selectItemVisitor.visit(this, context); + } + + @Override + public String toString() { + return expression + ((alias != null) ? alias.toString() : ""); + } + + public SelectItem withExpression(T expression) { + this.setExpression(expression); + return this; + } + + public SelectItem withAlias(Alias alias) { + this.setAlias(alias); + return this; + } + + public E getExpression(Class type) { + return type.cast(getExpression()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/SelectItemVisitor.java b/src/main/java/net/sf/jsqlparser/statement/select/SelectItemVisitor.java new file mode 100644 index 0000000..9e23873 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/SelectItemVisitor.java @@ -0,0 +1,20 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.expression.Expression; + +public interface SelectItemVisitor { + T visit(SelectItem selectItem, S context); + + default void visit(SelectItem selectItem) { + this.visit(selectItem, null); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/SelectItemVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/statement/select/SelectItemVisitorAdapter.java new file mode 100644 index 0000000..1fc7a23 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/SelectItemVisitorAdapter.java @@ -0,0 +1,20 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.expression.Expression; + +@SuppressWarnings({"PMD.UncommentedEmptyMethodBody"}) +public class SelectItemVisitorAdapter implements SelectItemVisitor { + @Override + public T visit(SelectItem item, S context) { + return null; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/SelectVisitor.java b/src/main/java/net/sf/jsqlparser/statement/select/SelectVisitor.java new file mode 100644 index 0000000..d8ee278 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/SelectVisitor.java @@ -0,0 +1,55 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +public interface SelectVisitor { + + T visit(ParenthesedSelect parenthesedSelect, S context); + + default void visit(ParenthesedSelect parenthesedSelect) { + this.visit(parenthesedSelect, null); + } + + T visit(PlainSelect plainSelect, S context); + + default void visit(PlainSelect plainSelect) { + this.visit(plainSelect, null); + } + + T visit(SetOperationList setOpList, S context); + + default void visit(SetOperationList setOpList) { + this.visit(setOpList, null); + } + + T visit(WithItem withItem, S context); + + default void visit(WithItem withItem) { + this.visit(withItem, null); + } + + T visit(Values values, S context); + + default void visit(Values values) { + this.visit(values, null); + } + + T visit(LateralSubSelect lateralSubSelect, S context); + + default void visit(LateralSubSelect lateralSubSelect) { + this.visit(lateralSubSelect, null); + } + + T visit(TableStatement tableStatement, S context); + + default void visit(TableStatement tableStatement) { + this.visit(tableStatement, null); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/SelectVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/statement/select/SelectVisitorAdapter.java new file mode 100644 index 0000000..5820fa8 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/SelectVisitorAdapter.java @@ -0,0 +1,49 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +@SuppressWarnings({"PMD.UncommentedEmptyMethodBody"}) +public class SelectVisitorAdapter implements SelectVisitor { + + @Override + public T visit(ParenthesedSelect parenthesedSelect, S context) { + return parenthesedSelect.getSelect().accept(this, context); + } + + @Override + public T visit(PlainSelect plainSelect, S context) { + return null; + } + + @Override + public T visit(SetOperationList setOpList, S context) { + return null; + } + + @Override + public T visit(WithItem withItem, S context) { + return null; + } + + @Override + public T visit(Values aThis, S context) { + return null; + } + + @Override + public T visit(LateralSubSelect lateralSubSelect, S context) { + return null; + } + + @Override + public T visit(TableStatement tableStatement, S context) { + return null; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/SetOperation.java b/src/main/java/net/sf/jsqlparser/statement/select/SetOperation.java new file mode 100644 index 0000000..0600e59 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/SetOperation.java @@ -0,0 +1,27 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; +import net.sf.jsqlparser.statement.select.SetOperationList.SetOperationType; + +public abstract class SetOperation extends ASTNodeAccessImpl { + + private SetOperationType type; + + public SetOperation(SetOperationType type) { + this.type = type; + } + + @Override + public String toString() { + return type.name(); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/SetOperationList.java b/src/main/java/net/sf/jsqlparser/statement/select/SetOperationList.java new file mode 100644 index 0000000..46708e8 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/SetOperationList.java @@ -0,0 +1,125 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +public class SetOperationList extends Select { + + private List getSelects() { + return selects; + } + + public void setSelects(List select, List ops) { + selects = select; + operations = ops; + } + + @Override + public StringBuilder appendSelectBodyTo(StringBuilder builder) { + for (int i = 0; i < selects.size(); i++) { + if (i != 0) { + builder.append(" ").append(operations.get(i - 1).toString()).append(" "); + } + builder.append(selects.get(i).toString()); + } + + if (orderByElements != null) { + builder.append(PlainSelect.orderByToString(orderByElements)); + } + return builder; + } + + public SetOperationList withOperations(List operationList) { + setOperations(operationList); + return this; + } + + public SetOperationList withSelects(List collection = Optional.ofNullable(getSelects()).orElseGet(ArrayList::new); + Collections.addAll(collection, selects); + return this.withSelects(collection); + } + + public SetOperationList addSelects(Collection selects) { + List

iter = plainSelect.getIntoTables().iterator(); iter.hasNext();) { + visit(iter.next(), context); + if (iter.hasNext()) { + buffer.append(", "); + } + } + } + + if (plainSelect.getFromItem() != null) { + buffer.append(" FROM "); + if (plainSelect.isUsingOnly()) { + buffer.append("ONLY "); + } + plainSelect.getFromItem().accept(this, context); + + if (plainSelect.getFromItem() instanceof Table) { + Table table = (Table) plainSelect.getFromItem(); + if (table.getSampleClause() != null) { + table.getSampleClause().appendTo(buffer); + } + } + } + + if (plainSelect.getLateralViews() != null) { + for (LateralView lateralView : plainSelect.getLateralViews()) { + deparseLateralView(lateralView); + } + } + + if (plainSelect.getJoins() != null) { + for (Join join : plainSelect.getJoins()) { + deparseJoin(join); + } + } + + if (plainSelect.isUsingFinal()) { + buffer.append(" FINAL"); + } + + if (plainSelect.getKsqlWindow() != null) { + buffer.append(" WINDOW "); + buffer.append(plainSelect.getKsqlWindow().toString()); + } + + deparseWhereClause(plainSelect); + + if (plainSelect.getOracleHierarchical() != null) { + plainSelect.getOracleHierarchical().accept(expressionVisitor, context); + } + + if (plainSelect.getGroupBy() != null) { + buffer.append(" "); + new GroupByDeParser(expressionVisitor, buffer).deParse(plainSelect.getGroupBy()); + } + + if (plainSelect.getHaving() != null) { + buffer.append(" HAVING "); + plainSelect.getHaving().accept(expressionVisitor, context); + } + if (plainSelect.getQualify() != null) { + buffer.append(" QUALIFY "); + plainSelect.getQualify().accept(expressionVisitor, context); + } + if (plainSelect.getWindowDefinitions() != null) { + buffer.append(" WINDOW "); + buffer.append(plainSelect.getWindowDefinitions().stream() + .map(WindowDefinition::toString).collect(joining(", "))); + } + if (plainSelect.getForClause() != null) { + plainSelect.getForClause().appendTo(buffer); + } + + deparseOrderByElementsClause(plainSelect, plainSelect.getOrderByElements()); + if (plainSelect.isEmitChanges()) { + buffer.append(" EMIT CHANGES"); + } + if (plainSelect.getLimitBy() != null) { + new LimitDeparser(expressionVisitor, buffer).deParse(plainSelect.getLimitBy()); + } + if (plainSelect.getLimit() != null) { + new LimitDeparser(expressionVisitor, buffer).deParse(plainSelect.getLimit()); + } + if (plainSelect.getOffset() != null) { + visit(plainSelect.getOffset()); + } + if (plainSelect.getFetch() != null) { + visit(plainSelect.getFetch()); + } + if (plainSelect.getIsolation() != null) { + buffer.append(plainSelect.getIsolation().toString()); + } + if (plainSelect.getForMode() != null) { + buffer.append(" FOR "); + buffer.append(plainSelect.getForMode().getValue()); + + if (plainSelect.getForUpdateTable() != null) { + buffer.append(" OF ").append(plainSelect.getForUpdateTable()); + } + if (plainSelect.getWait() != null) { + // wait's toString will do the formatting for us + buffer.append(plainSelect.getWait()); + } + if (plainSelect.isNoWait()) { + buffer.append(" NOWAIT"); + } else if (plainSelect.isSkipLocked()) { + buffer.append(" SKIP LOCKED"); + } + } + if (plainSelect.getOptimizeFor() != null) { + deparseOptimizeFor(plainSelect.getOptimizeFor()); + } + if (plainSelect.getForXmlPath() != null) { + buffer.append(" FOR XML PATH(").append(plainSelect.getForXmlPath()).append(")"); + } + if (plainSelect.getIntoTempTable() != null) { + buffer.append(" INTO TEMP ").append(plainSelect.getIntoTempTable()); + } + if (plainSelect.isUseWithNoLog()) { + buffer.append(" WITH NO LOG"); + } + return buffer; + } + + protected void deparseWhereClause(PlainSelect plainSelect) { + if (plainSelect.getWhere() != null) { + buffer.append(" WHERE "); + plainSelect.getWhere().accept(expressionVisitor, null); + } + } + + protected void deparseDistinctClause(Distinct distinct) { + if (distinct != null) { + if (distinct.isUseUnique()) { + buffer.append("UNIQUE "); + } else { + buffer.append("DISTINCT "); + } + if (distinct.getOnSelectItems() != null) { + buffer.append("ON ("); + for (Iterator> iter = distinct.getOnSelectItems().iterator(); iter + .hasNext();) { + SelectItem selectItem = iter.next(); + selectItem.accept(this, null); + if (iter.hasNext()) { + buffer.append(", "); + } + } + buffer.append(") "); + } + } + } + + protected void deparseSelectItemsClause(List> selectItems) { + if (selectItems != null) { + for (Iterator> iter = selectItems.iterator(); iter.hasNext();) { + SelectItem selectItem = iter.next(); + selectItem.accept(this, null); + if (iter.hasNext()) { + buffer.append(", "); + } + } + } + } + + protected void deparseOrderByElementsClause(PlainSelect plainSelect, + List orderByElements) { + if (orderByElements != null) { + new OrderByDeParser(expressionVisitor, buffer).deParse(plainSelect.isOracleSiblings(), + orderByElements); + } + } + + @Override + public StringBuilder visit(SelectItem selectItem, S context) { + selectItem.getExpression().accept(expressionVisitor, context); + if (selectItem.getAlias() != null) { + buffer.append(selectItem.getAlias().toString()); + } + return buffer; + } + + + @Override + public StringBuilder visit(Table tableName, S context) { + buffer.append(tableName.getFullyQualifiedName()); + Alias alias = tableName.getAlias(); + if (alias != null) { + buffer.append(alias); + } + Pivot pivot = tableName.getPivot(); + if (pivot != null) { + pivot.accept(this, context); + } + UnPivot unpivot = tableName.getUnPivot(); + if (unpivot != null) { + unpivot.accept(this, context); + } + MySQLIndexHint indexHint = tableName.getIndexHint(); + if (indexHint != null) { + buffer.append(indexHint); + } + SQLServerHints sqlServerHints = tableName.getSqlServerHints(); + if (sqlServerHints != null) { + buffer.append(sqlServerHints); + } + return buffer; + } + + @Override + public StringBuilder visit(Pivot pivot, S context) { + // @todo: implement this as Visitor + buffer.append(" PIVOT (").append(PlainSelect.getStringList(pivot.getFunctionItems())); + + buffer.append(" FOR "); + pivot.getForColumns().accept(expressionVisitor, context); + + // @todo: implement this as Visitor + buffer.append(" IN ").append(PlainSelect.getStringList(pivot.getInItems(), true, true)); + + buffer.append(")"); + if (pivot.getAlias() != null) { + buffer.append(pivot.getAlias().toString()); + } + return buffer; + } + + @Override + public StringBuilder visit(UnPivot unpivot, S context) { + boolean showOptions = unpivot.getIncludeNullsSpecified(); + boolean includeNulls = unpivot.getIncludeNulls(); + List unPivotClause = unpivot.getUnPivotClause(); + List unpivotForClause = unpivot.getUnPivotForClause(); + buffer.append(" UNPIVOT").append(showOptions && includeNulls ? " INCLUDE NULLS" : "") + .append(showOptions && !includeNulls ? " EXCLUDE NULLS" : "").append(" (") + .append(PlainSelect.getStringList(unPivotClause, true, + unPivotClause != null && unPivotClause.size() > 1)) + .append(" FOR ") + .append(PlainSelect.getStringList(unpivotForClause, true, + unpivotForClause != null && unpivotForClause.size() > 1)) + .append(" IN ") + .append(PlainSelect.getStringList(unpivot.getUnPivotInClause(), true, true)) + .append(")"); + if (unpivot.getAlias() != null) { + buffer.append(unpivot.getAlias().toString()); + } + return buffer; + } + + @Override + public StringBuilder visit(PivotXml pivot, S context) { + List forColumns = pivot.getForColumns(); + buffer.append(" PIVOT XML (").append(PlainSelect.getStringList(pivot.getFunctionItems())) + .append(" FOR ").append(PlainSelect.getStringList(forColumns, true, + forColumns != null && forColumns.size() > 1)) + .append(" IN ("); + if (pivot.isInAny()) { + buffer.append("ANY"); + } else if (pivot.getInSelect() != null) { + buffer.append(pivot.getInSelect()); + } else { + buffer.append(PlainSelect.getStringList(pivot.getInItems())); + } + buffer.append("))"); + return buffer; + } + + public void visit(Offset offset) { + // OFFSET offset + // or OFFSET offset (ROW | ROWS) + buffer.append(" OFFSET "); + offset.getOffset().accept(expressionVisitor, null); + if (offset.getOffsetParam() != null) { + buffer.append(" ").append(offset.getOffsetParam()); + } + + } + + public void visit(Fetch fetch) { + buffer.append(" FETCH "); + if (fetch.isFetchParamFirst()) { + buffer.append("FIRST "); + } else { + buffer.append("NEXT "); + } + if (fetch.getExpression() != null) { + fetch.getExpression().accept(expressionVisitor, null); + } + + for (String p : fetch.getFetchParameters()) { + buffer.append(" ").append(p); + } + } + + public ExpressionVisitor getExpressionVisitor() { + return expressionVisitor; + } + + public void setExpressionVisitor(ExpressionVisitor visitor) { + expressionVisitor = visitor; + } + + @SuppressWarnings({"PMD.CyclomaticComplexity"}) + public void deparseJoin(Join join) { + if (join.isGlobal()) { + buffer.append(" GLOBAL "); + } + + if (join.isSimple() && join.isOuter()) { + buffer.append(", OUTER "); + } else if (join.isSimple()) { + buffer.append(", "); + } else { + + if (join.isNatural()) { + buffer.append(" NATURAL"); + } + + if (join.isRight()) { + buffer.append(" RIGHT"); + } else if (join.isFull()) { + buffer.append(" FULL"); + } else if (join.isLeft()) { + buffer.append(" LEFT"); + } else if (join.isCross()) { + buffer.append(" CROSS"); + } + + if (join.isOuter()) { + buffer.append(" OUTER"); + } else if (join.isInner()) { + buffer.append(" INNER"); + } else if (join.isSemi()) { + buffer.append(" SEMI"); + } + + if (join.isStraight()) { + buffer.append(" STRAIGHT_JOIN "); + } else if (join.isApply()) { + buffer.append(" APPLY "); + } else { + if (join.getJoinHint() != null) { + buffer.append(" ").append(join.getJoinHint()); + } + buffer.append(" JOIN "); + } + + } + + FromItem fromItem = join.getFromItem(); + fromItem.accept(this, null); + if (join.isWindowJoin()) { + buffer.append(" WITHIN "); + buffer.append(join.getJoinWindow().toString()); + } + for (Expression onExpression : join.getOnExpressions()) { + buffer.append(" ON "); + onExpression.accept(expressionVisitor, null); + } + if (!join.getUsingColumns().isEmpty()) { + buffer.append(" USING ("); + for (Iterator iterator = join.getUsingColumns().iterator(); iterator + .hasNext();) { + Column column = iterator.next(); + buffer.append(column.toString()); + if (iterator.hasNext()) { + buffer.append(", "); + } + } + buffer.append(")"); + } + + } + + public void deparseLateralView(LateralView lateralView) { + buffer.append(" LATERAL VIEW"); + + if (lateralView.isUsingOuter()) { + buffer.append(" OUTER"); + } + + buffer.append(" "); + lateralView.getGeneratorFunction().accept(expressionVisitor, null); + + if (lateralView.getTableAlias() != null) { + buffer.append(" ").append(lateralView.getTableAlias()); + } + + buffer.append(" ").append(lateralView.getColumnAlias()); + } + + @Override + public StringBuilder visit(SetOperationList list, S context) { + List withItemsList = list.getWithItemsList(); + if (withItemsList != null && !withItemsList.isEmpty()) { + buffer.append("WITH "); + for (Iterator iter = withItemsList.iterator(); iter.hasNext();) { + iter.next().accept((SelectVisitor) this, context); + if (iter.hasNext()) { + buffer.append(","); + } + buffer.append(" "); + } + } + + for (int i = 0; i < list.getSelects().size(); i++) { + if (i != 0) { + buffer.append(' ').append(list.getOperations().get(i - 1)).append(' '); + } + list.getSelects().get(i).accept(this, context); + } + if (list.getOrderByElements() != null) { + new OrderByDeParser(expressionVisitor, buffer).deParse(list.getOrderByElements()); + } + + if (list.getLimit() != null) { + new LimitDeparser(expressionVisitor, buffer).deParse(list.getLimit()); + } + if (list.getOffset() != null) { + visit(list.getOffset()); + } + if (list.getFetch() != null) { + visit(list.getFetch()); + } + if (list.getIsolation() != null) { + buffer.append(list.getIsolation().toString()); + } + return buffer; + } + + @Override + public StringBuilder visit(WithItem withItem, S context) { + if (withItem.isRecursive()) { + buffer.append("RECURSIVE "); + } + buffer.append(withItem.getAlias().getName()); + if (withItem.getWithItemList() != null) { + buffer.append(" ") + .append(PlainSelect.getStringList(withItem.getWithItemList(), true, true)); + } + buffer.append(" AS "); + withItem.getSelect().accept(this, context); + return buffer; + } + + @Override + public StringBuilder visit(LateralSubSelect lateralSubSelect, S context) { + buffer.append(lateralSubSelect.getPrefix()); + visit((ParenthesedSelect) lateralSubSelect, context); + + return buffer; + } + + @Override + public StringBuilder visit(TableStatement tableStatement, S context) { + new TableStatementDeParser(expressionVisitor, buffer).deParse(tableStatement); + return buffer; + } + + @Override + public StringBuilder visit(TableFunction tableFunction, S context) { + buffer.append(tableFunction.toString()); + return buffer; + } + + @Override + public StringBuilder visit(ParenthesedFromItem fromItem, S context) { + + buffer.append("("); + fromItem.getFromItem().accept(this, context); + List joins = fromItem.getJoins(); + if (joins != null) { + for (Join join : joins) { + if (join.isSimple()) { + buffer.append(", ").append(join); + } else { + buffer.append(" ").append(join); + } + } + } + buffer.append(")"); + + if (fromItem.getAlias() != null) { + buffer.append(fromItem.getAlias().toString()); + } + + if (fromItem.getPivot() != null) { + visit(fromItem.getPivot(), context); + } + + if (fromItem.getUnPivot() != null) { + visit(fromItem.getUnPivot(), context); + } + return buffer; + } + + @Override + public StringBuilder visit(Values values, S context) { + new ValuesStatementDeParser(expressionVisitor, buffer).deParse(values); + return buffer; + } + + @Override + public void visit(Values values) { + SelectVisitor.super.visit(values); + } + + public void visit(ParenthesedSelect select) { + visit(select, null); + } + + public void visit(PlainSelect plainSelect) { + visit(plainSelect, null); + } + + public void visit(SelectItem selectExpressionItem) { + visit(selectExpressionItem, null); + } + + public void visit(Table tableName) { + visit(tableName, null); + } + + public void visit(Pivot pivot) { + visit(pivot, null); + } + + public void visit(UnPivot unpivot) { + visit(unpivot, null); + } + + public void visit(PivotXml pivot) { + visit(pivot, null); + } + + public void visit(SetOperationList list) { + visit(list, null); + } + + public void visit(WithItem withItem) { + visit(withItem, null); + } + + public void visit(LateralSubSelect lateralSubSelect) { + visit(lateralSubSelect, null); + } + + public void visit(TableStatement tableStatement) { + visit(tableStatement, null); + } + + public void visit(TableFunction tableFunction) { + visit(tableFunction, null); + } + + public void visit(ParenthesedFromItem fromItem) { + visit(fromItem, null); + } + + + private void deparseOptimizeFor(OptimizeFor optimizeFor) { + buffer.append(" OPTIMIZE FOR "); + buffer.append(optimizeFor.getRowCount()); + buffer.append(" ROWS"); + } + + @Override + void deParse(PlainSelect statement) { + statement.accept(this, null); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/SetStatementDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/SetStatementDeParser.java new file mode 100644 index 0000000..e38cb41 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/deparser/SetStatementDeParser.java @@ -0,0 +1,61 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.deparser; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; +import net.sf.jsqlparser.statement.SetStatement; + +import java.util.List; + +public class SetStatementDeParser extends AbstractDeParser { + + private ExpressionVisitor expressionVisitor; + + public SetStatementDeParser(ExpressionVisitor expressionVisitor, + StringBuilder buffer) { + super(buffer); + this.expressionVisitor = expressionVisitor; + } + + @Override + public void deParse(SetStatement set) { + buffer.append("SET "); + if (set.getEffectParameter() != null) { + buffer.append(set.getEffectParameter()).append(" "); + } + for (int i = 0; i < set.getCount(); i++) { + if (i > 0) { + buffer.append(", "); + } + buffer.append(set.getName(i)); + if (set.isUseEqual(i)) { + buffer.append(" ="); + } + buffer.append(" "); + List expressions = set.getExpressions(i); + for (int j = 0; j < expressions.size(); j++) { + if (j > 0) { + buffer.append(", "); + } + expressions.get(j).accept(expressionVisitor, null); + } + } + + } + + public ExpressionVisitor getExpressionVisitor() { + return expressionVisitor; + } + + public void setExpressionVisitor(ExpressionVisitor visitor) { + expressionVisitor = visitor; + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/ShowColumnsStatementDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/ShowColumnsStatementDeParser.java new file mode 100644 index 0000000..ad4627a --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/deparser/ShowColumnsStatementDeParser.java @@ -0,0 +1,24 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.deparser; + +import net.sf.jsqlparser.statement.ShowColumnsStatement; + +public class ShowColumnsStatementDeParser extends AbstractDeParser { + + public ShowColumnsStatementDeParser(StringBuilder buffer) { + super(buffer); + } + + @Override + public void deParse(ShowColumnsStatement show) { + buffer.append("SHOW COLUMNS FROM ").append(show.getTableName()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/ShowIndexStatementDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/ShowIndexStatementDeParser.java new file mode 100644 index 0000000..5e4a223 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/deparser/ShowIndexStatementDeParser.java @@ -0,0 +1,29 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.deparser; + +import net.sf.jsqlparser.statement.show.ShowIndexStatement; + +/** + * @author Jayant Kumar Yadav + */ + +public class ShowIndexStatementDeParser extends AbstractDeParser { + + public ShowIndexStatementDeParser(StringBuilder buffer) { + super(buffer); + } + + @Override + public void deParse(ShowIndexStatement show) { + buffer.append("SHOW INDEX FROM ").append(show.getTableName()); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/ShowStatementDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/ShowStatementDeParser.java new file mode 100644 index 0000000..70c5d9b --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/deparser/ShowStatementDeParser.java @@ -0,0 +1,24 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.deparser; + +import net.sf.jsqlparser.statement.ShowStatement; + +public class ShowStatementDeParser extends AbstractDeParser { + + public ShowStatementDeParser(StringBuilder buffer) { + super(buffer); + } + + @Override + public void deParse(ShowStatement show) { + buffer.append("SHOW ").append(show.getName()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/ShowTablesStatementDeparser.java b/src/main/java/net/sf/jsqlparser/util/deparser/ShowTablesStatementDeparser.java new file mode 100644 index 0000000..a218f3f --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/deparser/ShowTablesStatementDeparser.java @@ -0,0 +1,24 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.deparser; + +import net.sf.jsqlparser.statement.show.ShowTablesStatement; + +public class ShowTablesStatementDeparser extends AbstractDeParser { + + public ShowTablesStatementDeparser(StringBuilder buffer) { + super(buffer); + } + + @Override + void deParse(ShowTablesStatement statement) { + buffer.append(statement); + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java new file mode 100644 index 0000000..30cf27f --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java @@ -0,0 +1,442 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.deparser; + +import java.lang.reflect.InvocationTargetException; +import java.util.stream.Collectors; + +import net.sf.jsqlparser.statement.Block; +import net.sf.jsqlparser.statement.Commit; +import net.sf.jsqlparser.statement.CreateFunctionalStatement; +import net.sf.jsqlparser.statement.DeclareStatement; +import net.sf.jsqlparser.statement.DescribeStatement; +import net.sf.jsqlparser.statement.ExplainStatement; +import net.sf.jsqlparser.statement.IfElseStatement; +import net.sf.jsqlparser.statement.PurgeStatement; +import net.sf.jsqlparser.statement.ResetStatement; +import net.sf.jsqlparser.statement.RollbackStatement; +import net.sf.jsqlparser.statement.SavepointStatement; +import net.sf.jsqlparser.statement.SetStatement; +import net.sf.jsqlparser.statement.ShowColumnsStatement; +import net.sf.jsqlparser.statement.ShowStatement; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.StatementVisitor; +import net.sf.jsqlparser.statement.Statements; +import net.sf.jsqlparser.statement.UnsupportedStatement; +import net.sf.jsqlparser.statement.UseStatement; +import net.sf.jsqlparser.statement.alter.Alter; +import net.sf.jsqlparser.statement.alter.AlterSession; +import net.sf.jsqlparser.statement.alter.AlterSystemStatement; +import net.sf.jsqlparser.statement.alter.RenameTableStatement; +import net.sf.jsqlparser.statement.alter.sequence.AlterSequence; +import net.sf.jsqlparser.statement.analyze.Analyze; +import net.sf.jsqlparser.statement.comment.Comment; +import net.sf.jsqlparser.statement.create.index.CreateIndex; +import net.sf.jsqlparser.statement.create.schema.CreateSchema; +import net.sf.jsqlparser.statement.create.sequence.CreateSequence; +import net.sf.jsqlparser.statement.create.synonym.CreateSynonym; +import net.sf.jsqlparser.statement.create.table.CreateTable; +import net.sf.jsqlparser.statement.create.view.AlterView; +import net.sf.jsqlparser.statement.create.view.CreateView; +import net.sf.jsqlparser.statement.delete.Delete; +import net.sf.jsqlparser.statement.drop.Drop; +import net.sf.jsqlparser.statement.execute.Execute; +import net.sf.jsqlparser.statement.grant.Grant; +import net.sf.jsqlparser.statement.insert.Insert; +import net.sf.jsqlparser.statement.merge.*; +import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement; +import net.sf.jsqlparser.statement.select.Select; +import net.sf.jsqlparser.statement.show.ShowIndexStatement; +import net.sf.jsqlparser.statement.show.ShowTablesStatement; +import net.sf.jsqlparser.statement.truncate.Truncate; +import net.sf.jsqlparser.statement.update.Update; +import net.sf.jsqlparser.statement.upsert.Upsert; + +public class StatementDeParser extends AbstractDeParser + implements StatementVisitor { + + private final ExpressionDeParser expressionDeParser; + + private final SelectDeParser selectDeParser; + + public StatementDeParser(Class expressionDeparserClass, + Class selectDeparserClass, StringBuilder builder) + throws NoSuchMethodException, InvocationTargetException, InstantiationException, + IllegalAccessException { + super(builder); + + this.selectDeParser = selectDeparserClass + .getConstructor(Class.class, StringBuilder.class) + .newInstance(expressionDeparserClass, builder); + + + this.expressionDeParser = + expressionDeparserClass.cast(this.selectDeParser.getExpressionVisitor()); + + } + + public StatementDeParser(Class expressionDeparserClass, + Class selectDeparserClass) + throws NoSuchMethodException, InvocationTargetException, InstantiationException, + IllegalAccessException { + this(expressionDeparserClass, selectDeparserClass, new StringBuilder()); + } + + public StatementDeParser(StringBuilder buffer) { + this(new ExpressionDeParser(), new SelectDeParser(), buffer); + } + + public StatementDeParser(ExpressionDeParser expressionDeParser, SelectDeParser selectDeParser, + StringBuilder buffer) { + super(buffer); + + this.expressionDeParser = expressionDeParser; + this.selectDeParser = selectDeParser; + + this.selectDeParser.setBuffer(buffer); + this.selectDeParser.setExpressionVisitor(expressionDeParser); + + this.expressionDeParser.setSelectVisitor(selectDeParser); + this.expressionDeParser.setBuffer(buffer); + } + + @Override + public StringBuilder visit(CreateIndex createIndex, S context) { + CreateIndexDeParser createIndexDeParser = new CreateIndexDeParser(buffer); + createIndexDeParser.deParse(createIndex); + return buffer; + } + + @Override + public StringBuilder visit(CreateTable createTable, S context) { + CreateTableDeParser createTableDeParser = new CreateTableDeParser(this, buffer); + createTableDeParser.deParse(createTable); + return buffer; + } + + @Override + public StringBuilder visit(CreateView createView, S context) { + CreateViewDeParser createViewDeParser = new CreateViewDeParser(buffer); + createViewDeParser.deParse(createView); + return buffer; + } + + @Override + public StringBuilder visit(RefreshMaterializedViewStatement materializedViewStatement, + S context) { + new RefreshMaterializedViewStatementDeParser(buffer).deParse(materializedViewStatement); + return buffer; + } + + @Override + public StringBuilder visit(AlterView alterView, S context) { + AlterViewDeParser alterViewDeParser = new AlterViewDeParser(buffer); + alterViewDeParser.deParse(alterView); + return buffer; + } + + @Override + public StringBuilder visit(Delete delete, S context) { + DeleteDeParser deleteDeParser = new DeleteDeParser(expressionDeParser, buffer); + deleteDeParser.deParse(delete); + return buffer; + } + + @Override + public StringBuilder visit(Drop drop, S context) { + DropDeParser dropDeParser = new DropDeParser(buffer); + dropDeParser.deParse(drop); + return buffer; + } + + @Override + public StringBuilder visit(Insert insert, S context) { + InsertDeParser insertDeParser = + new InsertDeParser(expressionDeParser, selectDeParser, buffer); + insertDeParser.deParse(insert); + return buffer; + } + + @Override + public StringBuilder visit(Select select, S context) { + select.accept(selectDeParser, context); + return buffer; + } + + @Override + public StringBuilder visit(Truncate truncate, S context) { + buffer.append("TRUNCATE"); + if (truncate.isTableToken()) { + buffer.append(" TABLE"); + } + if (truncate.isOnly()) { + buffer.append(" ONLY"); + } + buffer.append(" "); + buffer.append(truncate.getTable()); + + if (truncate.getCascade()) { + buffer.append(" CASCADE"); + } + + return buffer; + } + + @Override + public StringBuilder visit(Update update, S context) { + UpdateDeParser updateDeParser = new UpdateDeParser(expressionDeParser, buffer); + updateDeParser.deParse(update); + + return buffer; + } + + public StringBuilder visit(Analyze analyzer, S context) { + buffer.append("ANALYZE "); + buffer.append(analyzer.getTable()); + return buffer; + } + + @Override + public StringBuilder visit(Alter alter, S context) { + AlterDeParser alterDeParser = new AlterDeParser(buffer); + alterDeParser.deParse(alter); + return buffer; + } + + @Override + public StringBuilder visit(Statements statements, S context) { + statements.accept(this, context); + return buffer; + } + + @Override + public StringBuilder visit(Execute execute, S context) { + ExecuteDeParser executeDeParser = new ExecuteDeParser(expressionDeParser, buffer); + executeDeParser.deParse(execute); + return buffer; + } + + @Override + public StringBuilder visit(SetStatement set, S context) { + SetStatementDeParser setStatementDeparser = + new SetStatementDeParser(expressionDeParser, buffer); + setStatementDeparser.deParse(set); + return buffer; + } + + @Override + public StringBuilder visit(ResetStatement reset, S context) { + ResetStatementDeParser setStatementDeparser = + new ResetStatementDeParser(expressionDeParser, buffer); + setStatementDeparser.deParse(reset); + return buffer; + } + + @SuppressWarnings({"PMD.CyclomaticComplexity"}) + @Override + public StringBuilder visit(Merge merge, S context) { + new MergeDeParser(expressionDeParser, selectDeParser, buffer).deParse(merge); + return buffer; + } + + @Override + public StringBuilder visit(SavepointStatement savepointStatement, S context) { + buffer.append(savepointStatement.toString()); + return buffer; + } + + @Override + public StringBuilder visit(RollbackStatement rollbackStatement, S context) { + buffer.append(rollbackStatement.toString()); + return buffer; + } + + @Override + public StringBuilder visit(Commit commit, S context) { + buffer.append(commit.toString()); + return buffer; + } + + @Override + public StringBuilder visit(Upsert upsert, S context) { + UpsertDeParser upsertDeParser = + new UpsertDeParser(expressionDeParser, selectDeParser, buffer); + upsertDeParser.deParse(upsert); + return buffer; + } + + @Override + public StringBuilder visit(UseStatement use, S context) { + new UseStatementDeParser(buffer).deParse(use); + return buffer; + } + + @Override + public StringBuilder visit(ShowColumnsStatement show, S context) { + new ShowColumnsStatementDeParser(buffer).deParse(show); + return buffer; + } + + @Override + public StringBuilder visit(ShowIndexStatement showIndexes, S context) { + new ShowIndexStatementDeParser(buffer).deParse(showIndexes); + return buffer; + } + + @Override + public StringBuilder visit(ShowTablesStatement showTables, S context) { + new ShowTablesStatementDeparser(buffer).deParse(showTables); + return buffer; + } + + @Override + public StringBuilder visit(Block block, S context) { + buffer.append("BEGIN\n"); + if (block.getStatements() != null) { + for (Statement stmt : block.getStatements().getStatements()) { + stmt.accept(this, context); + buffer.append(";\n"); + } + } + buffer.append("END"); + if (block.hasSemicolonAfterEnd()) { + buffer.append(";"); + } + return buffer; + } + + @Override + public StringBuilder visit(Comment comment, S context) { + buffer.append(comment.toString()); + return buffer; + } + + @Override + public StringBuilder visit(DescribeStatement describe, S context) { + buffer.append(describe.getDescribeType()); + buffer.append(" "); + buffer.append(describe.getTable()); + return buffer; + } + + @Override + public StringBuilder visit(ExplainStatement explainStatement, S context) { + buffer.append("EXPLAIN "); + if (explainStatement.getTable() != null) { + buffer.append(explainStatement.getTable()); + } else if (explainStatement.getOptions() != null) { + buffer.append(explainStatement.getOptions().values().stream() + .map(ExplainStatement.Option::formatOption).collect(Collectors.joining(" "))); + buffer.append(" "); + } + if (explainStatement.getStatement() != null) { + explainStatement.getStatement().accept(this, context); + } + return buffer; + } + + @Override + public StringBuilder visit(ShowStatement showStatement, S context) { + new ShowStatementDeParser(buffer).deParse(showStatement); + return buffer; + } + + @Override + public StringBuilder visit(DeclareStatement declareStatement, S context) { + new DeclareStatementDeParser(expressionDeParser, buffer).deParse(declareStatement); + return buffer; + } + + @Override + public StringBuilder visit(Grant grant, S context) { + GrantDeParser grantDeParser = new GrantDeParser(buffer); + grantDeParser.deParse(grant); + return buffer; + } + + @Override + public StringBuilder visit(CreateSchema aThis, S context) { + buffer.append(aThis.toString()); + return buffer; + } + + @Override + public StringBuilder visit(CreateSequence createSequence, S context) { + new CreateSequenceDeParser(buffer).deParse(createSequence); + return buffer; + } + + @Override + public StringBuilder visit(AlterSequence alterSequence, S context) { + new AlterSequenceDeParser(buffer).deParse(alterSequence); + return buffer; + } + + @Override + public StringBuilder visit(CreateFunctionalStatement createFunctionalStatement, S context) { + buffer.append(createFunctionalStatement.toString()); + return buffer; + } + + @Override + public StringBuilder visit(CreateSynonym createSynonym, S context) { + new CreateSynonymDeparser(buffer).deParse(createSynonym); + return buffer; + } + + @Override + void deParse(Statement statement) { + statement.accept(this, null); + } + + @Override + public StringBuilder visit(AlterSession alterSession, S context) { + new AlterSessionDeParser(buffer).deParse(alterSession); + return buffer; + } + + @Override + public StringBuilder visit(IfElseStatement ifElseStatement, S context) { + ifElseStatement.appendTo(buffer); + return buffer; + } + + @Override + public StringBuilder visit(RenameTableStatement renameTableStatement, S context) { + renameTableStatement.appendTo(buffer); + return buffer; + } + + @Override + public StringBuilder visit(PurgeStatement purgeStatement, S context) { + purgeStatement.appendTo(buffer); + return buffer; + } + + @Override + public StringBuilder visit(AlterSystemStatement alterSystemStatement, S context) { + alterSystemStatement.appendTo(buffer); + return buffer; + } + + @Override + public StringBuilder visit(UnsupportedStatement unsupportedStatement, S context) { + unsupportedStatement.appendTo(buffer); + return buffer; + } + + public ExpressionDeParser getExpressionDeParser() { + return expressionDeParser; + } + + public SelectDeParser getSelectDeParser() { + return selectDeParser; + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/TableStatementDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/TableStatementDeParser.java new file mode 100644 index 0000000..be83df5 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/deparser/TableStatementDeParser.java @@ -0,0 +1,106 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.deparser; + +import net.sf.jsqlparser.expression.ExpressionVisitor; +import net.sf.jsqlparser.statement.select.LateralSubSelect; +import net.sf.jsqlparser.statement.select.Offset; +import net.sf.jsqlparser.statement.select.ParenthesedSelect; +import net.sf.jsqlparser.statement.select.PlainSelect; +import net.sf.jsqlparser.statement.select.SelectVisitor; +import net.sf.jsqlparser.statement.select.SetOperationList; +import net.sf.jsqlparser.statement.select.TableStatement; +import net.sf.jsqlparser.statement.select.Values; +import net.sf.jsqlparser.statement.select.WithItem; + +/** + * @author jxnu-liguobin + */ +public class TableStatementDeParser extends AbstractDeParser + implements SelectVisitor { + + private final ExpressionVisitor expressionVisitor; + + public TableStatementDeParser(ExpressionVisitor expressionVisitor, + StringBuilder buffer) { + super(buffer); + this.expressionVisitor = expressionVisitor; + } + + @Override + public void deParse(TableStatement tableStatement) { + tableStatement.accept(this, null); + } + + public void deparse(Offset offset) { + buffer.append(" OFFSET "); + offset.getOffset().accept(expressionVisitor, null); + if (offset.getOffsetParam() != null) { + buffer.append(" ").append(offset.getOffsetParam()); + } + + } + + @Override + public StringBuilder visit(ParenthesedSelect parenthesedSelect, S context) { + + return buffer; + } + + @Override + public StringBuilder visit(PlainSelect plainSelect, S context) { + + return buffer; + } + + @Override + public StringBuilder visit(SetOperationList setOperationList, S context) { + + return buffer; + } + + @Override + public StringBuilder visit(WithItem withItem, S context) { + + return buffer; + } + + @Override + public StringBuilder visit(Values values, S context) { + + return buffer; + } + + @Override + public StringBuilder visit(LateralSubSelect lateralSubSelect, S context) { + + return buffer; + } + + @Override + public StringBuilder visit(TableStatement tableStatement, S context) { + buffer.append("TABLE "); + buffer.append(tableStatement.getTable()); + if (tableStatement.getOrderByElements() != null) { + new OrderByDeParser(expressionVisitor, buffer) + .deParse(tableStatement.getOrderByElements()); + } + + if (tableStatement.getLimit() != null) { + new LimitDeparser(expressionVisitor, buffer).deParse(tableStatement.getLimit()); + } + if (tableStatement.getOffset() != null) { + deparse(tableStatement.getOffset()); + } + + // TODO UNION + return buffer; + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/UpdateDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/UpdateDeParser.java new file mode 100644 index 0000000..682c1c0 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/deparser/UpdateDeParser.java @@ -0,0 +1,143 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.deparser; + +import net.sf.jsqlparser.expression.ExpressionVisitor; +import net.sf.jsqlparser.expression.ExpressionVisitorAdapter; +import net.sf.jsqlparser.statement.select.Join; +import net.sf.jsqlparser.statement.select.OrderByElement; +import net.sf.jsqlparser.statement.select.OrderByVisitor; +import net.sf.jsqlparser.statement.select.WithItem; +import net.sf.jsqlparser.statement.update.Update; + +import java.util.Iterator; + +public class UpdateDeParser extends AbstractDeParser + implements OrderByVisitor { + + private ExpressionVisitor expressionVisitor = new ExpressionVisitorAdapter<>(); + + public UpdateDeParser() { + super(new StringBuilder()); + } + + public UpdateDeParser(ExpressionVisitor expressionVisitor, + StringBuilder buffer) { + super(buffer); + this.expressionVisitor = expressionVisitor; + } + + @Override + @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity", + "PMD.ExcessiveMethodLength"}) + public void deParse(Update update) { + if (update.getWithItemsList() != null && !update.getWithItemsList().isEmpty()) { + buffer.append("WITH "); + for (Iterator iter = update.getWithItemsList().iterator(); iter.hasNext();) { + WithItem withItem = iter.next(); + buffer.append(withItem); + if (iter.hasNext()) { + buffer.append(","); + } + buffer.append(" "); + } + } + buffer.append("UPDATE "); + if (update.getOracleHint() != null) { + buffer.append(update.getOracleHint()).append(" "); + } + if (update.getModifierPriority() != null) { + buffer.append(update.getModifierPriority()).append(" "); + } + if (update.isModifierIgnore()) { + buffer.append("IGNORE "); + } + buffer.append(update.getTable()); + if (update.getStartJoins() != null) { + for (Join join : update.getStartJoins()) { + if (join.isSimple()) { + buffer.append(", ").append(join); + } else { + buffer.append(" ").append(join); + } + } + } + buffer.append(" SET "); + + deparseUpdateSetsClause(update); + + if (update.getOutputClause() != null) { + update.getOutputClause().appendTo(buffer); + } + + if (update.getFromItem() != null) { + buffer.append(" FROM ").append(update.getFromItem()); + if (update.getJoins() != null) { + for (Join join : update.getJoins()) { + if (join.isSimple()) { + buffer.append(", ").append(join); + } else { + buffer.append(" ").append(join); + } + } + } + } + + deparseWhereClause(update); + + if (update.getOrderByElements() != null) { + new OrderByDeParser(expressionVisitor, buffer).deParse(update.getOrderByElements()); + } + if (update.getLimit() != null) { + new LimitDeparser(expressionVisitor, buffer).deParse(update.getLimit()); + } + + if (update.getReturningClause() != null) { + update.getReturningClause().appendTo(buffer); + } + } + + protected void deparseWhereClause(Update update) { + if (update.getWhere() != null) { + buffer.append(" WHERE "); + update.getWhere().accept(expressionVisitor, null); + } + } + + protected void deparseUpdateSetsClause(Update update) { + deparseUpdateSets(update.getUpdateSets(), buffer, expressionVisitor); + } + + + public ExpressionVisitor getExpressionVisitor() { + return expressionVisitor; + } + + public void setExpressionVisitor(ExpressionVisitor visitor) { + expressionVisitor = visitor; + } + + @Override + public StringBuilder visit(OrderByElement orderBy, S context) { + orderBy.getExpression().accept(expressionVisitor, context); + if (!orderBy.isAsc()) { + buffer.append(" DESC"); + } else if (orderBy.isAscDescPresent()) { + buffer.append(" ASC"); + } + if (orderBy.getNullOrdering() != null) { + buffer.append(' '); + buffer.append(orderBy.getNullOrdering() == OrderByElement.NullOrdering.NULLS_FIRST + ? "NULLS FIRST" + : "NULLS LAST"); + } + return buffer; + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/UpsertDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/UpsertDeParser.java new file mode 100644 index 0000000..719c12a --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/deparser/UpsertDeParser.java @@ -0,0 +1,104 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.deparser; + +import net.sf.jsqlparser.expression.ExpressionVisitor; +import net.sf.jsqlparser.statement.select.SelectVisitor; +import net.sf.jsqlparser.statement.upsert.Upsert; + +@SuppressWarnings({"PMD.UncommentedEmptyMethodBody"}) +public class UpsertDeParser extends AbstractDeParser { + + private ExpressionDeParser expressionVisitor; + private SelectDeParser selectVisitor; + + public UpsertDeParser(ExpressionDeParser expressionVisitor, SelectDeParser selectVisitor, + StringBuilder buffer) { + super(buffer); + this.expressionVisitor = expressionVisitor; + this.expressionVisitor.setSelectVisitor(selectVisitor); + this.selectVisitor = selectVisitor; + this.selectVisitor.setExpressionVisitor(expressionVisitor); + } + + @Override + @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"}) + public void deParse(Upsert upsert) { + switch (upsert.getUpsertType()) { + case REPLACE: + case REPLACE_SET: + buffer.append("REPLACE "); + break; + case INSERT_OR_ABORT: + buffer.append("INSERT OR ABORT "); + break; + case INSERT_OR_FAIL: + buffer.append("INSERT OR FAIL "); + break; + case INSERT_OR_IGNORE: + buffer.append("INSERT OR IGNORE "); + break; + case INSERT_OR_REPLACE: + buffer.append("INSERT OR REPLACE "); + break; + case INSERT_OR_ROLLBACK: + buffer.append("INSERT OR ROLLBACK "); + break; + case UPSERT: + default: + buffer.append("UPSERT "); + } + + if (upsert.isUsingInto()) { + buffer.append("INTO "); + } + buffer.append(upsert.getTable().getFullyQualifiedName()); + + if (upsert.getUpdateSets() != null) { + buffer.append(" SET "); + deparseUpdateSets(upsert.getUpdateSets(), buffer, expressionVisitor); + } else { + if (upsert.getColumns() != null) { + upsert.getColumns().accept(expressionVisitor, null); + } + + if (upsert.getExpressions() != null) { + upsert.getExpressions().accept(expressionVisitor, null); + } + + if (upsert.getSelect() != null) { + buffer.append(" "); + upsert.getSelect().accept(selectVisitor, null); + } + + if (upsert.getDuplicateUpdateSets() != null) { + buffer.append(" ON DUPLICATE KEY UPDATE "); + deparseUpdateSets(upsert.getDuplicateUpdateSets(), buffer, expressionVisitor); + } + } + } + + public ExpressionVisitor getExpressionVisitor() { + return expressionVisitor; + } + + public void setExpressionVisitor(ExpressionDeParser visitor) { + expressionVisitor = visitor; + } + + public SelectVisitor getSelectVisitor() { + return selectVisitor; + } + + public void setSelectVisitor(SelectDeParser visitor) { + selectVisitor = visitor; + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/UseStatementDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/UseStatementDeParser.java new file mode 100644 index 0000000..c0f4a8f --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/deparser/UseStatementDeParser.java @@ -0,0 +1,28 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.deparser; + +import net.sf.jsqlparser.statement.UseStatement; + +public class UseStatementDeParser extends AbstractDeParser { + + public UseStatementDeParser(StringBuilder buffer) { + super(buffer); + } + + @Override + public void deParse(UseStatement set) { + buffer.append("USE "); + if (set.hasSchemaKeyword()) { + buffer.append("SCHEMA "); + } + buffer.append(set.getName()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/ValuesStatementDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/ValuesStatementDeParser.java new file mode 100644 index 0000000..8b57da1 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/deparser/ValuesStatementDeParser.java @@ -0,0 +1,33 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.deparser; + +import net.sf.jsqlparser.expression.ExpressionVisitor; +import net.sf.jsqlparser.statement.select.Values; + +public class ValuesStatementDeParser extends AbstractDeParser { + + private final ExpressionVisitor expressionVisitor; + + public ValuesStatementDeParser(ExpressionVisitor expressionVisitor, + StringBuilder buffer) { + super(buffer); + this.expressionVisitor = expressionVisitor; + } + + @Override + public void deParse(Values values) { + buffer.append("VALUES "); + values.getExpressions().accept(expressionVisitor, null); + if (values.getAlias() != null) { + buffer.append(" ").append(values.getAlias()); + } + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/ContextKey.java b/src/main/java/net/sf/jsqlparser/util/validation/ContextKey.java new file mode 100644 index 0000000..774d4fb --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/ContextKey.java @@ -0,0 +1,18 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation; + +/** + * the context key - a ValidationCapability should define constants of expected context - values + * needed for validation. + */ +public interface ContextKey { + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/ParseCapability.java b/src/main/java/net/sf/jsqlparser/util/validation/ParseCapability.java new file mode 100644 index 0000000..a57cf3e --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/ParseCapability.java @@ -0,0 +1,69 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.function.Consumer; + +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.statement.Statements; + +/** + * package - private class for {@link Validation} to parse the statements within it's own + * {@link ValidationCapability} + * + * @author gitmotte + */ +final class ParseCapability implements ValidationCapability { + + public static final String NAME = "parsing"; + + private String statements; + private Statements parsedStatement; + + public ParseCapability(String statements) { + this.statements = statements; + } + + public String getStatements() { + return statements; + } + + /** + * @return null on parse error, otherwise the {@link Statements} parsed. + */ + public Statements getParsedStatements() { + return parsedStatement; + } + + @Override + public void validate(ValidationContext context, Consumer errorConsumer) { + ExecutorService executorService = Executors.newSingleThreadExecutor(); + try { + this.parsedStatement = CCJSqlParserUtil.parseStatements( + CCJSqlParserUtil.newParser(statements) + .withConfiguration(context.getConfiguration()), + executorService); + } catch (JSQLParserException e) { + errorConsumer + .accept(new ParseException("Cannot parse statement: " + e.getMessage(), e)); + } finally { + executorService.shutdown(); + } + } + + @Override + public String getName() { + return NAME; + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/ParseContext.java b/src/main/java/net/sf/jsqlparser/util/validation/ParseContext.java new file mode 100644 index 0000000..f544f25 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/ParseContext.java @@ -0,0 +1,18 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation; + +/** + * @author gitmotte + * @see ParseCapability + */ +enum ParseContext implements ContextKey { + statement +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/ParseException.java b/src/main/java/net/sf/jsqlparser/util/validation/ParseException.java new file mode 100644 index 0000000..f9cee3c --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/ParseException.java @@ -0,0 +1,35 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation; + +import net.sf.jsqlparser.JSQLParserException; + +/** + * wraps a {@link JSQLParserException} to add to the errors collected by validation + * + * @author gitmotte + */ +public class ParseException extends ValidationException { + + private static final long serialVersionUID = 1L; + + public ParseException(String message, Throwable cause) { + super(message, cause); + } + + public ParseException(String message) { + super(message); + } + + public ParseException(Throwable cause) { + super(cause); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/UnexpectedValidationException.java b/src/main/java/net/sf/jsqlparser/util/validation/UnexpectedValidationException.java new file mode 100644 index 0000000..5f1f28b --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/UnexpectedValidationException.java @@ -0,0 +1,32 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation; + +/** + * can be used on unexpected errors during validation + * + * @author gitmotte + */ +public class UnexpectedValidationException extends ValidationException { + + private static final long serialVersionUID = 1L; + + public UnexpectedValidationException(String message, Throwable cause) { + super(message, cause); + } + + public UnexpectedValidationException(String message) { + super(message); + } + + public UnexpectedValidationException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/Validation.java b/src/main/java/net/sf/jsqlparser/util/validation/Validation.java new file mode 100644 index 0000000..3d7c4f7 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/Validation.java @@ -0,0 +1,160 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import net.sf.jsqlparser.parser.feature.FeatureConfiguration; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.Statements; +import net.sf.jsqlparser.util.validation.validator.StatementValidator; + +/** + * Parses the given statement list with {@link ParseCapability} and performs validation with + * configured {@link ValidationCapability}'s. + *

+ * Errors are are reported by calling {@link #validate()}. + * + * @author gitmotte + * @see #getErrors() + * @see #validate() + */ +public class Validation { + + private FeatureConfiguration featureConfiguration; + private Collection capabilities; + private List statementsList; + + private List errors; + private Statements parsedStatements; + + public Validation(Collection capabilities, + String... statements) { + this(new FeatureConfiguration(), capabilities, statements); + } + + public Validation(FeatureConfiguration featureConfiguration, + Collection capabilities, String... statements) { + this.featureConfiguration = featureConfiguration; + this.capabilities = capabilities; + this.statementsList = Arrays.asList(statements); + } + + /** + * @param capabilities + * @param statements + * @return a list of {@link ValidationError}'s + */ + public static List validate( + Collection capabilities, + String... statements) { + return new Validation(capabilities, statements).validate(); + } + + /** + * @param config + * @param capabilities + * @return a {@link ValidationContext} of the given config and capabilities + */ + public static ValidationContext createValidationContext(FeatureConfiguration config, + Collection capabilities) { + ValidationContext context = new ValidationContext(); + context.setCapabilities(new ArrayList<>(capabilities)); + context.setConfiguration(config); + return context; + } + + /** + * @param statements + * @param parsedStatement + * @param errorMap + * @return a list of {@link ValidationError}' + */ + public static List toValidationErrors(String statements, + Statement parsedStatement, + Map> errorMap) { + List errors = new ArrayList<>(); + for (Entry> e : errorMap.entrySet()) { + errors.add(new ValidationError(statements).withParsedStatement(parsedStatement) + .withCapability(e.getKey()).addErrors(e.getValue())); + } + return errors; + } + + /** + * @param statement + * @param context + * @return a map mapping the {@link ValidationCapability} to a set of + * {@link ValidationException}s + */ + public static Map> validate(Statement statement, + ValidationContext context) { + StatementValidator validator = new StatementValidator(); + validator.setContext(context); + validator.validate(statement); + return validator.getValidationErrors(); + } + + /** + * @return the errors - may be an empty list. + */ + public List validate() { + this.errors = new ArrayList<>(); + + ValidationContext context = createValidationContext(featureConfiguration, capabilities); + for (String statements : statementsList) { + + ParseCapability parse = new ParseCapability(statements); + parse.validate(context, e -> errors + .add(new ValidationError(statements).withCapability(parse).addError(e))); + + parsedStatements = parse.getParsedStatements(); + if (parsedStatements != null && parsedStatements.getStatements() != null + && !capabilities.isEmpty()) { + for (Statement parsedStatement : parsedStatements.getStatements()) { + Map> errorMap = + validate(parsedStatement, context); + errors.addAll(toValidationErrors(statements, parsedStatement, errorMap)); + } + } + + } + return errors; + } + + public FeatureConfiguration getFeatureConfiguration() { + return featureConfiguration; + } + + // STATIC util-methods + + public Collection getCapabilities() { + return capabilities; + } + + public List getStatements() { + return statementsList; + } + + public List getErrors() { + return errors; + } + + public Statements getParsedStatements() { + return parsedStatements; + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/ValidationCapability.java b/src/main/java/net/sf/jsqlparser/util/validation/ValidationCapability.java new file mode 100644 index 0000000..b0abb42 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/ValidationCapability.java @@ -0,0 +1,39 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation; + +import java.util.function.Consumer; + +public interface ValidationCapability { + + /** + * @return a name of this {@link ValidationCapability} + */ + default String getName() { + return getClass().getSimpleName(); + } + + /** + * Validate and add {@link ValidationException}'s to given consumer. + * + * @param context + * @param errorConsumer + */ + void validate(ValidationContext context, Consumer errorConsumer); + + default ValidationException toError(String message) { + return new ValidationException(message); + } + + default ValidationException toError(String message, Throwable th) { + return new ValidationException(message, th); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/ValidationContext.java b/src/main/java/net/sf/jsqlparser/util/validation/ValidationContext.java new file mode 100644 index 0000000..994be3b --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/ValidationContext.java @@ -0,0 +1,75 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.parser.feature.FeatureConfiguration; + +public class ValidationContext { + + private Collection capabilities; + private FeatureConfiguration configuration = new FeatureConfiguration(); + private Map contextMap = new HashMap<>(); + + public ValidationContext put(ContextKey key, Object value) { + contextMap.put(key, value); + return this; + } + + public T get(ContextKey key, Class type) { + T t = getOptional(key, type); + if (t == null) { + throw new IllegalStateException(key + ": value missing"); + } + return t; + } + + public T getOptional(ContextKey key, Class type) { + return type.cast(contextMap.get(key)); + } + + public ValidationContext reinit(boolean reInit) { + if (reInit) { + contextMap.clear(); + } + return this; + } + + public FeatureConfiguration getConfiguration() { + return configuration; + } + + public ValidationContext setConfiguration(FeatureConfiguration configuration) { + this.configuration = configuration; + return this; + } + + public boolean getAsBoolean(Feature f) { + return getConfiguration().getAsBoolean(f); + } + + public String getAsString(Feature f) { + return getConfiguration().getAsString(f); + } + + public Collection getCapabilities() { + return capabilities; + } + + public ValidationContext setCapabilities(Collection capabilities) { + this.capabilities = capabilities; + return this; + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/ValidationError.java b/src/main/java/net/sf/jsqlparser/util/validation/ValidationError.java new file mode 100644 index 0000000..f0ef8c5 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/ValidationError.java @@ -0,0 +1,94 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import net.sf.jsqlparser.statement.Statement; + +public class ValidationError { + + private final String statements; + private Statement parsedStatement; + + private Set errors = new HashSet<>(); + private ValidationCapability capability; + + public ValidationError(String statements) { + this.statements = statements; + } + + public ValidationError addError(ValidationException error) { + this.errors.add(error); + return this; + } + + public ValidationError addErrors(Collection errors) { + this.errors.addAll(errors); + return this; + } + + /** + * @return the set of {@link ValidationException}'s (no duplicates) + */ + public Set getErrors() { + return errors; + } + + /** + * @return the {@link ValidationCapability} which produced this error + */ + public ValidationCapability getCapability() { + return capability; + } + + public void setCapability(ValidationCapability databaseType) { + this.capability = databaseType; + } + + /** + * @return the parsed {@link Statement}, if parsing was possible + */ + public Statement getParsedStatement() { + return parsedStatement; + } + + public void setParsedStatement(Statement parsedStatement) { + this.parsedStatement = parsedStatement; + } + + /** + * @return the statements (may be more than one) given for validation + */ + public String getStatements() { + return statements; + } + + public ValidationError withCapability(ValidationCapability databaseType) { + setCapability(databaseType); + return this; + } + + public ValidationError withParsedStatement(Statement parsedStatement) { + setParsedStatement(parsedStatement); + return this; + } + + @Override + public String toString() { + return "ValidationError [\nstatement=" + statements + "\ncapability=" + + (capability != null ? capability.getName() : "") + "\nerrors=" + errors + + "\n]"; + } + + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/ValidationException.java b/src/main/java/net/sf/jsqlparser/util/validation/ValidationException.java new file mode 100644 index 0000000..99295f4 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/ValidationException.java @@ -0,0 +1,60 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation; + +import java.util.Objects; + +public class ValidationException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public ValidationException(String message, Throwable cause) { + super(message, cause); + } + + public ValidationException(String message) { + super(message); + } + + public ValidationException(Throwable cause) { + super(cause == null ? null : cause.getMessage(), cause); + } + + @Override + public boolean equals(Object o) { + if (o == null) { + return false; + } + if (this == o) { + return true; + } + + if (o.getClass().equals(this.getClass())) { + // exact type match! + ValidationException ve = (ValidationException) o; + return Objects.equals(getMessage(), ve.getMessage()) + && Objects.equals(getCause(), ve.getCause()); + } else { + return false; + } + } + + @Override + public int hashCode() { + return getMessage().hashCode() + + (getCause() == null ? 0 : getCause().toString().hashCode()); + } + + @Override + public String toString() { + return getClass().getSimpleName() + ": " + getMessage(); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/ValidationUtil.java b/src/main/java/net/sf/jsqlparser/util/validation/ValidationUtil.java new file mode 100644 index 0000000..cb83662 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/ValidationUtil.java @@ -0,0 +1,33 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation; + +import java.util.List; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; + +import net.sf.jsqlparser.expression.Alias; + +public class ValidationUtil { + + private ValidationUtil() { + // no construction allowed + } + + public static List map(List list, Function fn) { + return list.stream().map(fn).collect(Collectors.toList()); + } + + public static String getAlias(Alias alias) { + return Optional.ofNullable(alias).map(Alias::getName).orElse(null); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/Validator.java b/src/main/java/net/sf/jsqlparser/util/validation/Validator.java new file mode 100644 index 0000000..c1cc3d2 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/Validator.java @@ -0,0 +1,103 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import java.util.Set; + +/** + * @param + * @author gitmotte + */ +public interface Validator { + + /** + * @return true, all {@link ValidationCapability}'s have no errors + */ + default boolean isValid() { + return getValidationErrors().isEmpty(); + } + + /** + * @param capabilities + * @return true, if the given {@link ValidationCapability}'s have no errors. + * false otherwise. + */ + default boolean isValid(ValidationCapability... capabilities) { + return getValidationErrors(capabilities).isEmpty(); + } + + /** + * @return the {@link ValidationCapability}'s requested mapped to a set of error-messages + */ + Map> getValidationErrors(); + + /** + * @param capabilities + * @return the filtered view of requested {@link ValidationCapability}'s mapped to a set of + * error-messages + */ + default Map> getValidationErrors( + ValidationCapability... capabilities) { + return getValidationErrors(Arrays.asList(capabilities)); + } + + /** + * @param capabilities + * @return the filtered view of requested {@link ValidationCapability}'s mapped to a set of + * error-messages + */ + default Map> getValidationErrors( + Collection capabilities) { + Map> map = new HashMap<>(); + for (Entry> e : getValidationErrors() + .entrySet()) { + if (capabilities.contains(e.getKey())) { + map.put(e.getKey(), e.getValue()); + } + } + return map; + } + + // /** + // * Set the {@link ValidationCapability}'s this {@link Validator} should + // check. + // * + // * @param capabilities + // */ + // public void setCapabilities(Collection + // capabilities); + // + // /** + // * @param configuration + // */ + // public void setConfiguration(FeatureConfiguration configuration); + + /** + * @param ctx + */ + void setContext(ValidationContext ctx); + + /** + * validates given statement. + * + * @param statement + * @see #getValidationErrors() + * @see #getValidationErrors(Collection) + * @see #getValidationErrors(ValidationCapability...) + */ + void validate(S statement); + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/allowedtypes/AllowedTypesContext.java b/src/main/java/net/sf/jsqlparser/util/validation/allowedtypes/AllowedTypesContext.java new file mode 100644 index 0000000..958340b --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/allowedtypes/AllowedTypesContext.java @@ -0,0 +1,27 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.allowedtypes; + +import net.sf.jsqlparser.util.validation.ContextKey; + +public enum AllowedTypesContext implements ContextKey { + /** + * a collection of allowed {@link Class}es + */ + allowed_types, + /** + * the object given (may be null) + */ + argument, + /** + * a boolean, default = true + */ + allow_null +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/allowedtypes/AllowedTypesValidation.java b/src/main/java/net/sf/jsqlparser/util/validation/allowedtypes/AllowedTypesValidation.java new file mode 100644 index 0000000..14b0a40 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/allowedtypes/AllowedTypesValidation.java @@ -0,0 +1,52 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.allowedtypes; + +import java.util.Collection; +import java.util.function.Consumer; + +import net.sf.jsqlparser.util.validation.ValidationCapability; +import net.sf.jsqlparser.util.validation.ValidationContext; +import net.sf.jsqlparser.util.validation.ValidationException; + +public class AllowedTypesValidation implements ValidationCapability { + + public static final String NAME = "allowed types"; + + @Override + public void validate(ValidationContext context, Consumer errorConsumer) { + Object arg = context.getOptional(AllowedTypesContext.argument, Object.class); + Boolean allowNull = context.getOptional(AllowedTypesContext.allow_null, Boolean.class); + @SuppressWarnings("unchecked") + Collection> allowedTypes = + context.get(AllowedTypesContext.allowed_types, Collection.class); + if (arg != null) { + boolean error = true; + for (Class cls : allowedTypes) { + if (cls.isAssignableFrom(arg.getClass())) { + error = false; + break; + } + } + if (error) { + errorConsumer.accept(toError(arg.getClass() + + " is not a valid argument - expected one of " + allowedTypes)); + } + } else if (Boolean.FALSE.equals(allowNull)) { + errorConsumer.accept(toError("argument is missing one of " + allowedTypes)); + } + } + + @Override + public String getName() { + return NAME; + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/feature/DatabaseType.java b/src/main/java/net/sf/jsqlparser/util/validation/feature/DatabaseType.java new file mode 100644 index 0000000..2359f93 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/feature/DatabaseType.java @@ -0,0 +1,97 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.feature; + +import java.util.EnumSet; +import java.util.Set; + +import net.sf.jsqlparser.parser.feature.Feature; + +/** + *

+ * The DatabaseType is named like the identifier used within the jdbc-connection-url (upper case), + * this may change in future, therefore use {@link #get(String)} to retrieve the + * {@link DatabaseType}. + *

+ */ +public enum DatabaseType implements FeatureSetValidation, Version { + + ANSI_SQL("ANSI SQL", SQLVersion.values()), + // DBMS + ORACLE(OracleVersion.values()), MYSQL(MySqlVersion.values()), SQLSERVER( + SqlServerVersion.values()), MARIADB(MariaDbVersion.values()), POSTGRESQL( + PostgresqlVersion.values()), H2(H2Version.values()); + + public static final DatabaseType[] DATABASES = + new DatabaseType[] {ORACLE, MYSQL, SQLSERVER, MARIADB, POSTGRESQL, + H2}; + + private String name; + private Version[] versions; + + /** + * @param versions - ordered ascending - the last version is the latest. + */ + DatabaseType(Version... versions) { + this.versions = versions; + } + + /** + * @param versions - ordered ascending - the last version is the latest. + */ + DatabaseType(String name, Version... versions) { + this.name = name; + this.versions = versions; + } + + /** + * @param jdbcIdentifier - the database-identifier-part of jdbc-url + * @return the {@link DatabaseType} + * @throws IllegalArgumentException - if the specified jdbcIdentifier cannot be mapped to a + * {@link DatabaseType} + * @throws NullPointerException if {@code jdbcIdentifier} is null + */ + public static DatabaseType get(String jdbcIdentifier) { + return DatabaseType.valueOf(jdbcIdentifier.toUpperCase()); + } + + /** + * + */ + @Override + public String getName() { + return name == null ? name() : name; + } + + /** + * @return the features supported by the latest version. + */ + @Override + public Set getFeatures() { + if (versions.length > 0) { + return versions[versions.length - 1].getFeatures(); + } else { + return EnumSet.noneOf(Feature.class); + } + } + + /** + * @return the latest version-string. + */ + @Override + public String getVersionString() { + if (versions.length > 0) { + return versions[versions.length - 1].getVersionString(); + } else { + return null; + } + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/feature/FeatureContext.java b/src/main/java/net/sf/jsqlparser/util/validation/feature/FeatureContext.java new file mode 100644 index 0000000..3f579ce --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/feature/FeatureContext.java @@ -0,0 +1,20 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.feature; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.util.validation.ContextKey; + +public enum FeatureContext implements ContextKey { + /** + * @see Feature + */ + feature +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/feature/FeatureSetValidation.java b/src/main/java/net/sf/jsqlparser/util/validation/feature/FeatureSetValidation.java new file mode 100644 index 0000000..e4e52a0 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/feature/FeatureSetValidation.java @@ -0,0 +1,49 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.feature; + +import java.util.Set; +import java.util.function.Consumer; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.parser.feature.FeatureSet; +import net.sf.jsqlparser.util.validation.ValidationCapability; +import net.sf.jsqlparser.util.validation.ValidationContext; +import net.sf.jsqlparser.util.validation.ValidationException; + +public interface FeatureSetValidation extends ValidationCapability, FeatureSet { + + String DEFAULT_NAME = "feature set"; + + @Override + default void validate(ValidationContext context, Consumer errorConsumer) { + Feature feature = context.get(FeatureContext.feature, Feature.class); + if (!contains(feature)) { + errorConsumer.accept(getMessage(feature)); + } + } + + /** + * @return all supported {@link Feature}'s + */ + @Override + Set getFeatures(); + + /** + * @return the default message if not contained in the feature set + */ + ValidationException getMessage(Feature feature); + + @Override + default String getName() { + return DEFAULT_NAME; + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/feature/FeaturesAllowed.java b/src/main/java/net/sf/jsqlparser/util/validation/feature/FeaturesAllowed.java new file mode 100644 index 0000000..431e4d7 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/feature/FeaturesAllowed.java @@ -0,0 +1,299 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.feature; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.parser.feature.FeatureSet; +import net.sf.jsqlparser.parser.feature.ModifyableFeatureSet; +import net.sf.jsqlparser.util.validation.ValidationException; + +/** + * Privileges/Features allowed + * + * @author gitmotte + */ +public class FeaturesAllowed implements FeatureSetValidation, ModifyableFeatureSet { + + public static final FeaturesAllowed JDBC = new FeaturesAllowed("jdbc", + // always allowed if used with jdbc + Feature.jdbcParameter, + Feature.jdbcNamedParameter).unmodifyable(); + public static final FeaturesAllowed EXPRESSIONS = + new FeaturesAllowed("EXPRESSIONS", Feature.exprLike, + Feature.exprSimilarTo); + /** + * all {@link Feature}' within SQL SELECT without modification features like + * {@link Feature#selectInto}, but jdbc-features like {@link Feature#jdbcParameter} and + * {@link Feature#jdbcNamedParameter} + */ + public static final FeaturesAllowed SELECT = new FeaturesAllowed("SELECT", + // select features + Feature.select, + Feature.selectGroupBy, + Feature.selectHaving, + + Feature.join, + Feature.joinOuterSimple, + Feature.joinSimple, + Feature.joinRight, + Feature.joinNatural, + Feature.joinFull, + Feature.joinLeft, + Feature.joinCross, + Feature.joinOuter, + Feature.joinSemi, + Feature.joinInner, + Feature.joinStraight, + Feature.joinApply, + Feature.joinWindow, + Feature.joinUsingColumns, + + Feature.limit, + Feature.limitNull, + Feature.limitAll, + Feature.limitOffset, + Feature.offset, + Feature.offsetParam, + Feature.fetch, + Feature.fetchFirst, + Feature.fetchNext, + Feature.skip, + Feature.first, + Feature.top, + Feature.optimizeFor, + Feature.selectUnique, + Feature.distinct, + Feature.distinctOn, + Feature.orderBy, + Feature.orderByNullOrdering, + Feature.tableStatement, + + Feature.function).unmodifyable(); + /** + * all {@link Feature}' for SQL MERGE other similar commands + */ + public static final FeaturesAllowed MERGE = + new FeaturesAllowed("MERGE", Feature.merge, Feature.upsert, + Feature.insertUseDuplicateKeyUpdate).unmodifyable(); + public static final FeaturesAllowed EXECUTE = + new FeaturesAllowed("EXECUTE", Feature.execute).unmodifyable(); + /** + * all "CREATE" {@link Feature}'s + */ + public static final FeaturesAllowed CREATE = new FeaturesAllowed("CREATE", Feature.createIndex, + Feature.createSchema, Feature.createSequence, Feature.createTable, + Feature.createTableUnlogged, + Feature.createTableCreateOptionStrings, Feature.createTableTableOptionStrings, + Feature.createTableIfNotExists, Feature.createTableRowMovement, + Feature.createTableFromSelect, + Feature.createTrigger, + Feature.createView).unmodifyable(); + /** + * all "ALTER" {@link Feature}'s + */ + public static final FeaturesAllowed ALTER = + new FeaturesAllowed("ALTER", Feature.alterTable, Feature.alterSequence, + Feature.alterView, Feature.alterIndex) + .unmodifyable(); + /** + * all "DROP" {@link Feature}'s + */ + public static final FeaturesAllowed DROP = + new FeaturesAllowed("DROP", Feature.drop, Feature.dropTable, + Feature.dropIndex, Feature.dropView, Feature.dropSchema, Feature.dropSequence, + Feature.dropTableIfExists, + Feature.dropIndexIfExists, Feature.dropViewIfExists, Feature.dropSchemaIfExists, + Feature.dropSequenceIfExists) + .unmodifyable(); + private static final String SEPERATOR_REGEX = " \\+ "; + /** + * all {@link Feature}' for SQL INSERT including {@link #SELECT} and {@link Feature#selectInto} + */ + public static final FeaturesAllowed INSERT = + new FeaturesAllowed("INSERT", Feature.insert, Feature.insertFromSelect, + Feature.insertModifierIgnore, Feature.insertModifierPriority, + Feature.insertReturningAll, + Feature.insertReturningExpressionList, Feature.insertUseSet, + Feature.insertValues, Feature.selectInto).add(SELECT).unmodifyable(); + /** + * all {@link Feature}' for SQL UPDATE including {@link #SELECT} + */ + public static final FeaturesAllowed UPDATE = new FeaturesAllowed("UPDATE", Feature.update, + Feature.updateJoins, + Feature.updateFrom, Feature.updateLimit, Feature.updateOrderBy, Feature.updateReturning, + Feature.updateUseSelect) + .add(SELECT).unmodifyable(); + /** + * all {@link Feature}' for SQL UPDATE including {@link #SELECT} + */ + public static final FeaturesAllowed DELETE = + new FeaturesAllowed("DELETE", Feature.delete, Feature.deleteJoin, + Feature.deleteLimit, Feature.deleteOrderBy, Feature.deleteTables, + Feature.deleteReturningExpressionList, + Feature.truncate) + .add(SELECT).unmodifyable(); + /** + * all DML {@link Feature}'s + */ + public static final FeaturesAllowed DML = + new FeaturesAllowed("DML").add(SELECT, INSERT, UPDATE, DELETE, MERGE) + .unmodifyable(); + /** + * all DDL {@link Feature}'s + */ + public static final FeaturesAllowed DDL = + new FeaturesAllowed("DDL").add(CREATE, ALTER, DROP).unmodifyable(); + private static final String SEPERATOR = " + "; + private Set names = new LinkedHashSet<>(); + private Set features = new HashSet<>(); + + /** + * @param features + */ + public FeaturesAllowed(Feature... features) { + add(features); + } + + /** + * @param features + */ + public FeaturesAllowed(String name, Feature... features) { + this.names.add(name); + add(features); + } + + /** + * @param featureSets + * @return this + */ + @Override + public FeaturesAllowed add(FeatureSet... featureSets) { + Stream.of(featureSets).forEach(fs -> { + features.addAll(fs.getFeatures()); + if (fs instanceof FeatureSetValidation) { + names.addAll(collectNames((FeatureSetValidation) fs)); + } + }); + return this; + } + + /** + * @param features + * @return this + */ + @Override + public FeaturesAllowed add(Feature... features) { + Collections.addAll(this.features, features); + return this; + } + + /** + * @param features + * @return this + */ + @Override + public FeaturesAllowed add(Collection features) { + this.features.addAll(features); + return this; + } + + /** + * @param featureSets + * @return this + */ + @Override + public FeaturesAllowed remove(FeatureSet... featureSets) { + Stream.of(featureSets).forEach(fs -> { + features.removeAll(fs.getFeatures()); + if (fs instanceof FeatureSetValidation) { + names.removeAll(collectNames((FeatureSetValidation) fs)); + } + }); + return this; + } + + /** + * @param features + * @return this + */ + @Override + public FeaturesAllowed remove(Feature... features) { + this.features.removeAll(Arrays.asList(features)); + return this; + } + + /** + * @param features + * @return this + */ + @Override + public FeaturesAllowed remove(Collection features) { + this.features.removeAll(features); + return this; + } + + /** + * @return returns a modifiable copy of this {@link FeaturesAllowed} object + * @see #unmodifyable() + */ + @Override + public FeaturesAllowed copy() { + return new FeaturesAllowed().add(this); + } + + /** + * makes the inner {@link Feature}-set unmodifiable + * + * @return this + * @see #copy() + */ + @Override + public FeaturesAllowed unmodifyable() { + this.features = Collections.unmodifiableSet(features); + return this; + } + + /** + * @return featureName + " not allowed." + */ + @Override + public ValidationException getMessage(Feature feature) { + return toError(feature.name() + " not allowed."); + } + + @Override + public String getName() { + return names.isEmpty() ? FeatureSetValidation.super.getName() + : names.stream().collect(Collectors.joining(SEPERATOR)); + } + + + @Override + public Set getFeatures() { + return features; + } + + private List collectNames(FeatureSetValidation fs) { + String name = fs.getName(); + return Stream.of(name.split(SEPERATOR_REGEX)).map(String::trim) + .collect(Collectors.toList()); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/feature/H2Version.java b/src/main/java/net/sf/jsqlparser/util/validation/feature/H2Version.java new file mode 100644 index 0000000..3984e7f --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/feature/H2Version.java @@ -0,0 +1,181 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.feature; + +import java.util.Collections; +import java.util.EnumSet; +import java.util.Set; + +import net.sf.jsqlparser.parser.feature.Feature; + +/** + * Please add Features supported and place a link to public documentation + * + * @author gitmotte + * @see http://www.h2database.com/html/commands.html + */ +public enum H2Version implements Version { + V_1_4_200("1.4.200", + EnumSet.of( + // supported if used with jdbc + Feature.jdbcParameter, + Feature.jdbcNamedParameter, + // expressions + Feature.exprLike, + // http://h2database.com/html/commands.html#select + Feature.select, + Feature.selectGroupBy, Feature.function, + Feature.selectHaving, + // https://h2database.com/html/grammar.html?#table_expression + // https://h2database.com/html/grammar.html?#join_specification + Feature.join, + Feature.joinSimple, + Feature.joinRight, + Feature.joinFull, + Feature.joinLeft, + Feature.joinCross, + Feature.joinOuter, + Feature.joinInner, + Feature.joinNatural, + Feature.joinUsingColumns, + + // http://www.h2database.com/html/commands.html?highlight=ORDER%20BY&search=SELECT#firstFound + // http://www.h2database.com/html/grammar.html#order + Feature.orderBy, Feature.orderByNullOrdering, + + // http://www.h2database.com/html/commands.html?highlight=select&search=SELECT#with + Feature.withItem, Feature.withItemRecursive, + + // http://h2database.com/html/commands.html#comment + Feature.comment, + Feature.commentOnTable, + Feature.commentOnColumn, + Feature.commentOnView, + + // http://h2database.com/html/functions.html#table + Feature.tableFunction, + + // http://h2database.com/html/commands.html#select + Feature.setOperation, + Feature.setOperationUnion, + Feature.setOperationIntersect, + Feature.setOperationExcept, + Feature.setOperationMinus, + + // http://h2database.com/html/commands.html#create_sequence + Feature.createSequence, + // http://h2database.com/html/commands.html#alter_sequence + Feature.alterSequence, + // http://h2database.com/html/commands.html#create_schema + Feature.createSchema, + // http://h2database.com/html/commands.html#create_index + Feature.createIndex, + // http://h2database.com/html/commands.html#create_table + Feature.createTable, Feature.createTableCreateOptionStrings, + Feature.createTableTableOptionStrings, Feature.createTableFromSelect, + Feature.createTableIfNotExists, + // http://h2database.com/html/commands.html#create_view + Feature.createView, + Feature.createViewForce, Feature.createOrReplaceView, + // http://h2database.com/html/commands.html#alter_view_rename + // Feature.alterView, + + // http://h2database.com/html/commands.html#select + Feature.top, + // http://www.h2database.com/html/advanced.html?search=limit#result_sets + Feature.fetch, Feature.fetchFirst, + + // http://www.h2database.com/html/commands.html?highlight=DISTINCT&search=SELECT#firstFound + Feature.distinct, + // http://www.h2database.com/html/commands.html?highlight=DISTINCT%20ON&search=SELECT#firstFound + Feature.distinctOn, + // http://h2database.com/html/commands.html#insert + Feature.insert, + Feature.insertValues, + Feature.values, + Feature.insertFromSelect, + // http://h2database.com/html/commands.html#update + Feature.update, + // http://h2database.com/html/commands.html#delete + Feature.delete, + // http://h2database.com/html/commands.html#truncate_table + Feature.truncate, + + // http://www.h2database.com/html/commands.html#execute_immediate + Feature.executeStatementImmediate, + + // http://h2database.com/html/commands.html#drop_table + // http://h2database.com/html/commands.html#drop_index + Feature.drop, + // http://h2database.com/html/commands.html#drop_table + Feature.dropTable, + // http://h2database.com/html/commands.html#drop_index + Feature.dropIndex, + // http://h2database.com/html/commands.html#drop_view + Feature.dropView, + // http://h2database.com/html/commands.html#drop_schema + Feature.dropSchema, + // http://h2database.com/html/commands.html#drop_sequence + Feature.dropSequence, + Feature.dropTableIfExists, Feature.dropIndexIfExists, Feature.dropViewIfExists, + Feature.dropSchemaIfExists, Feature.dropSequenceIfExists, + // http://h2database.com/html/commands.html#alter_table_add + // http://h2database.com/html/commands.html#alter_table_add_constraint + // ... + Feature.alterTable, + // http://www.h2database.com/html/commands.html#explain + Feature.explain, + // http://www.h2database.com/html/commands.html#grant_right + // http://www.h2database.com/html/commands.html#grant_role + Feature.grant, + // http://h2database.com/html/commands.html#commit + Feature.commit)); + + private Set features; + private String versionString; + + /** + * @param versionString + * @param featuresSupported + * @see #copy() to copy from previous version + */ + H2Version(String versionString, Set featuresSupported) { + this(versionString, featuresSupported, Collections.emptySet()); + } + + /** + * @param versionString + * @param featuresSupported + * @param unsupported + * @see #copy() to copy from previous version + */ + H2Version(String versionString, Set featuresSupported, Set unsupported) { + this.versionString = versionString; + this.features = featuresSupported; + this.features.removeAll(unsupported); + } + + @Override + public String getVersionString() { + return versionString; + } + + @Override + public Set getFeatures() { + return features; + } + + @Override + public String getName() { + return DatabaseType.H2.getName() + " " + getVersionString(); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/feature/MariaDbVersion.java b/src/main/java/net/sf/jsqlparser/util/validation/feature/MariaDbVersion.java new file mode 100644 index 0000000..742e84b --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/feature/MariaDbVersion.java @@ -0,0 +1,196 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.feature; + +import java.util.Collections; +import java.util.EnumSet; +import java.util.Set; + +import net.sf.jsqlparser.parser.feature.Feature; + +/** + * Please add Features supported and place a link to public documentation + * + * @author gitmotte + * @see https://mariadb.com/kb/en/sql-statements-structure/ + */ +public enum MariaDbVersion implements Version { + V10_5_4("10.5.4", + EnumSet.of(// supported if used with jdbc + Feature.jdbcParameter, + Feature.jdbcNamedParameter, + // expressions + Feature.exprLike, + // https://mariadb.com/kb/en/select/ + Feature.select, + Feature.selectGroupBy, Feature.function, + Feature.selectHaving, + Feature.limit, Feature.limitOffset, Feature.offset, Feature.offsetParam, + Feature.orderBy, + Feature.selectForUpdate, + Feature.selectForUpdateWait, + Feature.selectForUpdateNoWait, + Feature.selectForUpdateSkipLocked, + + // https://mariadb.com/kb/en/join-syntax/ + Feature.join, Feature.joinSimple, Feature.joinRight, Feature.joinNatural, + Feature.joinLeft, + Feature.joinCross, Feature.joinOuter, Feature.joinInner, Feature.joinStraight, + Feature.joinUsingColumns, + + // https://mariadb.com/kb/en/select/#distinct + Feature.distinct, + + Feature.setOperation, + // https://mariadb.com/kb/en/union/ + Feature.setOperationUnion, + // https://mariadb.com/kb/en/intersect/ + Feature.setOperationIntersect, + // https://mariadb.com/kb/en/except/ + Feature.setOperationExcept, + + // https://mariadb.com/kb/en/common-table-expressions/ + // https://mariadb.com/kb/en/with/ + // https://mariadb.com/kb/en/non-recursive-common-table-expressions-overview/ + // https://mariadb.com/kb/en/recursive-common-table-expressions-overview/ + Feature.withItem, Feature.withItemRecursive, + + // https://mariadb.com/kb/en/insert/ + Feature.insert, Feature.insertValues, Feature.values, + Feature.insertFromSelect, Feature.insertModifierPriority, + Feature.insertModifierIgnore, + Feature.insertUseSet, Feature.insertUseDuplicateKeyUpdate, + Feature.insertReturningExpressionList, + + // https://mariadb.com/kb/en/update/ + Feature.update, + Feature.updateJoins, + Feature.updateOrderBy, Feature.updateLimit, + + // https://mariadb.com/kb/en/delete/ + Feature.delete, + Feature.deleteJoin, Feature.deleteTables, + Feature.deleteLimit, Feature.deleteOrderBy, + + // https://mariadb.com/kb/en/truncate-table/ + Feature.truncate, + + // https://mariadb.com/kb/en/call/ + Feature.execute, Feature.executeCall, + + // https://mariadb.com/kb/en/drop/ + Feature.drop, + // https://mariadb.com/kb/en/drop-index/ + Feature.dropIndex, + // https://mariadb.com/kb/en/drop-table/ + Feature.dropTable, + // https://mariadb.com/kb/en/drop-database/ + // SCHEMA = DATABASE + Feature.dropSchema, + // https://mariadb.com/kb/en/drop-view/ + Feature.dropView, + // https://mariadb.com/kb/en/drop-sequence/ + Feature.dropSequence, Feature.dropTableIfExists, Feature.dropIndexIfExists, + Feature.dropViewIfExists, Feature.dropSchemaIfExists, + Feature.dropSequenceIfExists, + + // https://mariadb.com/kb/en/replace/ + Feature.upsert, + + // https://mariadb.com/kb/en/alter/ + Feature.alterTable, + // https://mariadb.com/kb/en/alter-sequence/ + Feature.alterSequence, + // https://mariadb.com/kb/en/alter-view/ + Feature.alterView, + // https://mariadb.com/kb/en/create-view/ + Feature.createView, + Feature.createOrReplaceView, + Feature.createViewWithComment, + + // https://mariadb.com/kb/en/create-table/ + Feature.createTable, Feature.createTableCreateOptionStrings, + Feature.createTableTableOptionStrings, + Feature.createTableFromSelect, Feature.createTableIfNotExists, + // https://mariadb.com/kb/en/create-index/ + Feature.createIndex, + // https://mariadb.com/kb/en/create-sequence/ + Feature.createSequence, + // https://mariadb.com/kb/en/create-database/ + Feature.createSchema, + // https://mariadb.com/kb/en/create-trigger/ + Feature.createTrigger, + + // https://mariadb.com/kb/en/describe/ + Feature.describe, + // https://mariadb.com/kb/en/explain/ + Feature.explain, + // https://mariadb.com/kb/en/show/ + Feature.show, + // https://mariadb.com/kb/en/show-tables/ + Feature.showTables, + // https://mariadb.com/kb/en/show-columns/ + Feature.showColumns, + // https://mariadb.com/kb/en/show-index/ + Feature.showIndex, + // https://mariadb.com/kb/en/use/ + Feature.use, + // https://mariadb.com/kb/en/grant/ + Feature.grant, + // https://mariadb.com/kb/en/commit/ + Feature.commit, + // https://mariadb.com/kb/en/optimizer-hints/ + Feature.mySqlHintStraightJoin, + Feature.mysqlCalcFoundRows, + Feature.mysqlSqlCacheFlag)), + + ORACLE_MODE("oracle_mode", V10_5_4.copy().add(Feature.selectUnique).getFeatures()); + + private Set features; + private String versionString; + + /** + * @param versionString + * @param featuresSupported + * @see #copy() to copy from previous version + */ + MariaDbVersion(String versionString, Set featuresSupported) { + this(versionString, featuresSupported, Collections.emptySet()); + } + + /** + * @param versionString + * @param featuresSupported + * @param unsupported + * @see #copy() to copy from previous version + */ + MariaDbVersion(String versionString, Set featuresSupported, Set unsupported) { + this.versionString = versionString; + this.features = featuresSupported; + this.features.removeAll(unsupported); + } + + @Override + public String getVersionString() { + return versionString; + } + + @Override + public Set getFeatures() { + return features; + } + + @Override + public String getName() { + return DatabaseType.MARIADB.getName() + " " + getVersionString(); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/feature/MySqlVersion.java b/src/main/java/net/sf/jsqlparser/util/validation/feature/MySqlVersion.java new file mode 100644 index 0000000..cd6c2c0 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/feature/MySqlVersion.java @@ -0,0 +1,180 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.feature; + +import java.util.Collections; +import java.util.EnumSet; +import java.util.Set; + +import net.sf.jsqlparser.parser.feature.Feature; + +/** + * Please add Features supported and place a link to public documentation + * + * @author gitmotte + * @see https://dev.mysql.com/doc/refman/8.0/en/ + */ +public enum MySqlVersion implements Version { + V8_0("8.0", + EnumSet.of( + // supported if used with jdbc + Feature.jdbcParameter, + Feature.jdbcNamedParameter, + // expressions + Feature.exprLike, + // https://dev.mysql.com/doc/refman/8.0/en/select.html + Feature.select, + Feature.selectGroupBy, Feature.selectHaving, + Feature.limit, Feature.limitOffset, Feature.offset, Feature.offsetParam, + Feature.orderBy, + Feature.selectForUpdate, + Feature.selectForUpdateOfTable, + Feature.selectForUpdateNoWait, + Feature.selectForUpdateSkipLocked, + Feature.selectForShare, + Feature.distinct, + + Feature.setOperation, + // https://dev.mysql.com/doc/refman/8.0/en/union.html + Feature.setOperationUnion, + + // https://dev.mysql.com/doc/refman/8.0/en/with.html#common-table-expressions + Feature.withItem, Feature.withItemRecursive, + + // https://dev.mysql.com/doc/refman/8.0/en/sql-function-reference.html + Feature.function, + + // https://dev.mysql.com/doc/refman/8.0/en/join.html + Feature.join, Feature.joinSimple, Feature.joinLeft, Feature.joinRight, + Feature.joinOuter, + Feature.joinNatural, Feature.joinInner, Feature.joinCross, Feature.joinStraight, + Feature.joinUsingColumns, + + // https://dev.mysql.com/doc/refman/8.0/en/insert.html + Feature.insert, + Feature.insertValues, + Feature.values, + Feature.tableStatement, + Feature.insertFromSelect, Feature.insertUseSet, Feature.insertModifierPriority, + Feature.insertModifierIgnore, Feature.insertUseDuplicateKeyUpdate, + + // https://dev.mysql.com/doc/refman/8.0/en/update.html + Feature.update, Feature.updateJoins, Feature.updateOrderBy, Feature.updateLimit, + + // https://dev.mysql.com/doc/refman/8.0/en/replace.html + Feature.upsert, + + // https://dev.mysql.com/doc/refman/8.0/en/delete.html + Feature.delete, Feature.deleteJoin, Feature.deleteTables, Feature.deleteLimit, + Feature.deleteOrderBy, + + // https://dev.mysql.com/doc/refman/8.0/en/truncate-table.html + Feature.truncate, + + // https://dev.mysql.com/doc/refman/8.0/en/call.html + Feature.execute, Feature.executeCall, + + Feature.drop, + // https://dev.mysql.com/doc/refman/8.0/en/drop-table.html + Feature.dropTable, + // https://dev.mysql.com/doc/refman/8.0/en/drop-index.html + Feature.dropIndex, + // https://dev.mysql.com/doc/refman/8.0/en/drop-view.html + Feature.dropView, + // https://dev.mysql.com/doc/refman/8.0/en/drop-database.html + Feature.dropSchema, + Feature.dropTableIfExists, Feature.dropViewIfExists, + Feature.dropSchemaIfExists, Feature.dropSequenceIfExists, + + // https://dev.mysql.com/doc/refman/8.0/en/alter-table.html + Feature.alterTable, + // https://dev.mysql.com/doc/refman/8.0/en/alter-view.html + Feature.alterView, + + // https://dev.mysql.com/doc/refman/8.0/en/create-database.html + Feature.createSchema, + // https://dev.mysql.com/doc/refman/8.0/en/create-view.html + Feature.createView, + Feature.createViewWithComment, + Feature.createOrReplaceView, + // https://dev.mysql.com/doc/refman/8.0/en/create-table.html + Feature.createTable, Feature.createTableCreateOptionStrings, + Feature.createTableTableOptionStrings, + Feature.createTableFromSelect, Feature.createTableIfNotExists, + // https://dev.mysql.com/doc/refman/8.0/en/create-index.html + Feature.createIndex, + // https://dev.mysql.com/doc/refman/8.0/en/create-trigger.html + Feature.createTrigger, + + // https://dev.mysql.com/doc/refman/8.0/en/describe.html + Feature.describe, + Feature.desc, + // https://dev.mysql.com/doc/refman/8.0/en/explain.html + Feature.explain, + // https://dev.mysql.com/doc/refman/8.0/en/show.html + Feature.show, + // https://dev.mysql.com/doc/refman/8.0/en/show-tables.html + Feature.showTables, + // https://dev.mysql.com/doc/refman/8.0/en/show-columns.html + Feature.showColumns, + // https://dev.mysql.com/doc/refman/8.0/en/show-index.html + Feature.showIndex, + // https://dev.mysql.com/doc/refman/8.0/en/grant.html + Feature.grant, + // https://dev.mysql.com/doc/refman/8.0/en/use.html + Feature.use, + // https://dev.mysql.com/doc/refman/8.0/en/commit.html + Feature.commit, + // + Feature.mySqlHintStraightJoin, + Feature.mysqlSqlCacheFlag, + Feature.mysqlCalcFoundRows)); + + private Set features; + private String versionString; + + /** + * @param versionString + * @param featuresSupported + * @see #copy() to copy from previous version + */ + MySqlVersion(String versionString, Set featuresSupported) { + this(versionString, featuresSupported, Collections.emptySet()); + } + + /** + * @param versionString + * @param featuresSupported + * @param unsupported + * @see #copy() to copy from previous version + */ + MySqlVersion(String versionString, Set featuresSupported, Set unsupported) { + this.versionString = versionString; + this.features = featuresSupported; + this.features.removeAll(unsupported); + } + + @Override + public String getVersionString() { + return versionString; + } + + @Override + public Set getFeatures() { + return features; + } + + @Override + public String getName() { + return DatabaseType.MYSQL.getName() + " " + name(); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/feature/OracleVersion.java b/src/main/java/net/sf/jsqlparser/util/validation/feature/OracleVersion.java new file mode 100644 index 0000000..37b1938 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/feature/OracleVersion.java @@ -0,0 +1,210 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.feature; + +import java.util.Collections; +import java.util.EnumSet; +import java.util.Set; + +import net.sf.jsqlparser.parser.feature.Feature; + +/** + * Please add Features supported and place a link to public documentation + * + * @author gitmotte + * @see https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/index.html + */ +public enum OracleVersion implements Version { + V19C("19c", + EnumSet.of( + // supported if used with jdbc + Feature.jdbcParameter, + Feature.jdbcNamedParameter, + // expressions + Feature.exprLike, + // common features + // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/SELECT.html + Feature.select, + // https://www.oracletutorial.com/oracle-basics/oracle-group-by/ + Feature.selectGroupBy, Feature.function, + // https://www.oracletutorial.com/oracle-basics/oracle-grouping-sets/ + Feature.selectGroupByGroupingSets, + // https://www.oracletutorial.com/oracle-basics/oracle-having/ + Feature.selectHaving, + + // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/SELECT.html + // see "join_clause" + Feature.join, + Feature.joinSimple, + Feature.joinLeft, + Feature.joinRight, + Feature.joinFull, Feature.joinCross, + Feature.joinNatural, + Feature.joinOuter, + Feature.joinInner, + Feature.joinApply, + Feature.joinUsingColumns, + + // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/SELECT.html + // see "row_limiting_clause" + Feature.offset, Feature.offsetParam, Feature.fetch, Feature.fetchFirst, + Feature.fetchNext, + + // https://www.oracletutorial.com/oracle-basics/oracle-select-distinct/ + Feature.distinct, Feature.selectUnique, + + // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/SELECT.html + // see "order_by_clause" + Feature.orderBy, + Feature.orderByNullOrdering, + + // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/SELECT.html + // see "with_clause" + Feature.withItem, + + // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/SELECT.html + // see "pivot_clause" + // see "unpivot_clause" + // see "LATERAL" + Feature.lateralSubSelect, + + + // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/Set-Operators.html + Feature.setOperation, Feature.setOperationUnion, Feature.setOperationIntersect, + Feature.setOperationMinus, + + // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/SELECT.html + // see "for_update_clause" + Feature.selectForUpdate, + Feature.selectForUpdateWait, + Feature.selectForUpdateNoWait, + Feature.selectForUpdateSkipLocked, + + // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/INSERT.html + Feature.insert, + Feature.insertValues, + Feature.values, + // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/INSERT.html + // see "single_table_insert" + Feature.insertFromSelect, + + // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/UPDATE.html + Feature.update, + Feature.updateReturning, + Feature.updateUseSelect, + + // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/DELETE.html + Feature.delete, + Feature.deleteReturningExpressionList, + + // https://www.oracletutorial.com/oracle-basics/oracle-truncate-table/ + Feature.truncate, + + Feature.drop, + // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/DROP-TABLE.html + Feature.dropTable, + // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/DROP-INDEX.html + Feature.dropIndex, + // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/DROP-VIEW.html + Feature.dropView, + // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/DROP-SEQUENCE.html + Feature.dropSequence, + + // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/ALTER-TABLE.html + Feature.alterTable, + // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/ALTER-SEQUENCE.html + Feature.alterSequence, + // https://docs.oracle.com/en/database/oracle/oracle-database/19/lnpls/EXECUTE-IMMEDIATE-statement.html + Feature.executeStatementImmediate, + // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/CREATE-VIEW.html + Feature.createView, + Feature.createViewForce, Feature.createOrReplaceView, + // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/CREATE-MATERIALIZED-VIEW.htm + Feature.createViewMaterialized, + // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/CREATE-TABLE.html + Feature.createTable, Feature.createTableCreateOptionStrings, + Feature.createTableTableOptionStrings, + Feature.createTableFromSelect, Feature.createTableRowMovement, + // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/CREATE-INDEX.html + Feature.createIndex, + // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/CREATE-SEQUENCE.html + Feature.createSequence, + // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/CREATE-TRIGGER.html + Feature.createTrigger, + // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/CREATE-SCHEMA.html + Feature.createSchema, + + Feature.commit, + + // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/COMMENT.html + Feature.comment, Feature.commentOnTable, Feature.commentOnColumn, + Feature.commentOnView, + + // https://docs.oracle.com/en/database/oracle/oracle-database/19/rcmrf/DESCRIBE.html + Feature.describe, + + // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/GRANT.htm + Feature.grant, + + // https://www.oracletutorial.com/oracle-basics/oracle-merge/ + Feature.merge, + + Feature.createFunction, Feature.createProcedure, Feature.functionalStatement, + Feature.block, + Feature.declare, + + // special oracle features + Feature.oracleOldJoinSyntax, + Feature.oraclePriorPosition, + Feature.oracleHint, + Feature.oracleHierarchicalExpression, + Feature.oracleOrderBySiblings)); + + private Set features; + private String versionString; + + /** + * @param versionString + * @param featuresSupported + * @see #copy() to copy from previous version + */ + OracleVersion(String versionString, Set featuresSupported) { + this(versionString, featuresSupported, Collections.emptySet()); + } + + /** + * @param versionString + * @param featuresSupported + * @param unsupported + * @see #copy() to copy from previous version + */ + OracleVersion(String versionString, Set featuresSupported, Set unsupported) { + this.versionString = versionString; + this.features = featuresSupported; + this.features.removeAll(unsupported); + } + + @Override + public String getVersionString() { + return versionString; + } + + @Override + public Set getFeatures() { + return features; + } + + @Override + public String getName() { + return DatabaseType.ORACLE.getName() + " " + getVersionString(); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/feature/PostgresqlVersion.java b/src/main/java/net/sf/jsqlparser/util/validation/feature/PostgresqlVersion.java new file mode 100644 index 0000000..386274f --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/feature/PostgresqlVersion.java @@ -0,0 +1,207 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.feature; + +import java.util.Collections; +import java.util.EnumSet; +import java.util.Set; + +import net.sf.jsqlparser.parser.feature.Feature; + +/** + * Please add Features supported and place a link to public documentation + * + * @author gitmotte + * @see https://www.postgresql.org/docs/current + */ +public enum PostgresqlVersion implements Version { + V10("10", + EnumSet.of( + // supported if used with jdbc + Feature.jdbcParameter, + Feature.jdbcNamedParameter, + // expressions + Feature.exprLike, + Feature.exprSimilarTo, + // https://www.postgresql.org/docs/current/sql-select.html + Feature.select, + Feature.selectGroupBy, Feature.function, Feature.tableFunction, + Feature.lateralSubSelect, + Feature.selectHaving, + // https://www.postgresql.org/docs/current/queries-table-expressions.html#QUERIES-GROUPING-SETS + Feature.selectGroupByGroupingSets, + + // https://www.postgresql.org/docs/current/sql-select.html#join_type + Feature.join, + Feature.joinSimple, + Feature.joinRight, + Feature.joinFull, + Feature.joinLeft, + Feature.joinCross, + Feature.joinOuter, + Feature.joinInner, + Feature.joinUsingColumns, + + // https://www.postgresql.org/docs/current/queries-with.html + Feature.withItem, + Feature.withItemRecursive, + + // https://www.postgresql.org/docs/current/queries-limit.html + // https://www.postgresql.org/docs/current/sql-select.html#SQL-LIMIT + Feature.limit, + Feature.limitAll, // + Feature.limitNull, + Feature.offset, + + // https://www.postgresql.org/docs/current/sql-select.html + Feature.fetch, // + Feature.fetchFirst, // + Feature.fetchNext, + + // https://www.postgresql.org/docs/current/sql-select.html + Feature.distinct, + Feature.distinctOn, + + // https://www.postgresql.org/docs/current/sql-select.html + Feature.orderBy, + Feature.orderByNullOrdering, + + Feature.selectForNoKeyUpdate, + Feature.selectForKeyShare, + Feature.selectForShare, + Feature.selectForUpdate, + Feature.selectForUpdateOfTable, + Feature.selectForUpdateNoWait, + Feature.selectForUpdateSkipLocked, + + // https://www.postgresql.org/docs/current/queries-union.html + Feature.setOperation, + Feature.setOperationUnion, + Feature.setOperationIntersect, + Feature.setOperationExcept, + + // https://www.postgresql.org/docs/current/sql-comment.html + Feature.comment, + Feature.commentOnTable, + Feature.commentOnColumn, + Feature.commentOnView, + + // https://www.postgresql.org/docs/current/sql-createsequence.html + Feature.createSequence, + // https://www.postgresql.org/docs/current/sql-altersequence.html + Feature.alterSequence, + // https://www.postgresql.org/docs/current/sql-createschema.html + Feature.createSchema, + // https://www.postgresql.org/docs/current/sql-createindex.html + Feature.createIndex, + // https://www.postgresql.org/docs/current/sql-createtable.html + Feature.createTable, Feature.createTableUnlogged, + Feature.createTableCreateOptionStrings, Feature.createTableTableOptionStrings, + Feature.createTableFromSelect, Feature.createTableIfNotExists, + // https://www.postgresql.org/docs/current/sql-createview.html + Feature.createView, Feature.createViewTemporary, Feature.createOrReplaceView, + // https://www.postgresql.org/docs/current/sql-alterview.html + // Feature.alterView, + + // https://www.postgresql.org/docs/16/sql-refreshmaterializedview.html + Feature.refreshMaterializedView, + Feature.refreshMaterializedWithNoDataView, + Feature.refreshMaterializedWithDataView, + + // https://www.postgresql.org/docs/current/sql-insert.html + Feature.insert, + Feature.insertValues, + Feature.values, + Feature.insertFromSelect, + Feature.insertReturningAll, + Feature.insertReturningExpressionList, + // https://www.postgresql.org/docs/current/sql-update.html + Feature.update, + Feature.updateReturning, + // https://www.postgresql.org/docs/current/sql-delete.html + Feature.delete, + Feature.deleteReturningExpressionList, + // https://www.postgresql.org/docs/current/sql-truncate.html + Feature.truncate, + + // https://www.postgresql.org/docs/current/sql-call.html + Feature.execute, Feature.executeCall, + + Feature.drop, + // https://www.postgresql.org/docs/current/sql-droptable.html + Feature.dropTable, + // https://www.postgresql.org/docs/current/sql-dropindex.html + Feature.dropIndex, + // https://www.postgresql.org/docs/current/sql-dropview.html + Feature.dropView, + // https://www.postgresql.org/docs/current/sql-dropschema.html + Feature.dropSchema, + // https://www.postgresql.org/docs/current/sql-dropsequence.html + Feature.dropSequence, + Feature.dropTableIfExists, Feature.dropIndexIfExists, Feature.dropViewIfExists, + Feature.dropSchemaIfExists, Feature.dropSequenceIfExists, + + // https://www.postgresql.org/docs/current/sql-altertable.html + Feature.alterTable, + // https://www.postgresql.org/docs/current/using-explain.html + Feature.explain, + // https://www.postgresql.org/docs/current/sql-grant.html + Feature.grant, + // https://www.postgresql.org/docs/current/sql-set.html + Feature.set, + // https://www.postgresql.org/docs/current/sql-reset.html + Feature.reset, + // https://www.postgresql.org/docs/current/sql-commit.html + Feature.commit)), V11("11", V10.copy().getFeatures()), V12("12", + V11.copy().getFeatures()), V13("13", + V12.copy().getFeatures()), V14("14", V13.copy().getFeatures()); + + private Set features; + private String versionString; + + /** + * @param versionString + * @param featuresSupported + * @see #copy() to copy from previous version + */ + PostgresqlVersion(String versionString, Set featuresSupported) { + this(versionString, featuresSupported, Collections.emptySet()); + } + + /** + * @param versionString + * @param featuresSupported + * @param unsupported + * @see #copy() to copy from previous version + */ + PostgresqlVersion(String versionString, Set featuresSupported, + Set unsupported) { + this.versionString = versionString; + this.features = featuresSupported; + this.features.removeAll(unsupported); + } + + @Override + public String getVersionString() { + return versionString; + } + + @Override + public Set getFeatures() { + return features; + } + + @Override + public String getName() { + return DatabaseType.POSTGRESQL.getName() + " " + getVersionString(); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/feature/SQLVersion.java b/src/main/java/net/sf/jsqlparser/util/validation/feature/SQLVersion.java new file mode 100644 index 0000000..225cd00 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/feature/SQLVersion.java @@ -0,0 +1,93 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.feature; + +import java.util.Collections; +import java.util.EnumSet; +import java.util.Set; + +import net.sf.jsqlparser.parser.feature.Feature; + +/** + * Enum containing the ANSI SQL Standard Versions - features are not guaranteed to be complete, just + * add them if you are sure they are part of the standard :) + * + * @author gitmotte + * @see https://en.wikipedia.org/wiki/SQL#Interoperability_and_standardization + */ +public enum SQLVersion implements Version { + SQL1986("SQL-86", EnumSet.of( + // supported if used with jdbc + Feature.jdbcParameter, + Feature.jdbcNamedParameter, + // common features + Feature.setOperation, + Feature.select, + Feature.selectGroupBy, Feature.function, + Feature.insert, + Feature.insertFromSelect, + Feature.insertValues, + Feature.values, + Feature.update, + Feature.delete, + Feature.truncate, + Feature.drop, + Feature.alterTable)), // + SQL1989("SQL-89", SQL1986.copy().getFeatures()), // + SQL1992("SQL-92", SQL1989.copy().getFeatures()), // + SQL1999("SQL:1999", SQL1992.copy().add(Feature.exprSimilarTo).getFeatures()), // + SQL2003("SQL:2003", SQL1999.copy().getFeatures()), // + SQL2006("SQL:2006", SQL2003.copy().getFeatures()), // + SQL2008("SQL:2008", SQL2006.copy().getFeatures()), // + SQL2011("SQL:2011", SQL2008.copy().getFeatures()), // + SQL2016("SQL:2016", SQL2011.copy().getFeatures()), // + SQL2019("SQL:2019", SQL2016.copy().getFeatures()); + + private Set features; + private String versionString; + + /** + * @param versionString + * @param featuresSupported + * @see #copy() to copy from previous version + */ + SQLVersion(String versionString, Set featuresSupported) { + this(versionString, featuresSupported, Collections.emptySet()); + } + + /** + * @param versionString + * @param featuresSupported + * @param unsupported + * @see #copy() to copy from previous version + */ + SQLVersion(String versionString, Set featuresSupported, Set unsupported) { + this.versionString = versionString; + this.features = featuresSupported; + this.features.removeAll(unsupported); + } + + @Override + public String getVersionString() { + return versionString; + } + + @Override + public Set getFeatures() { + return features; + } + + @Override + public String getName() { + return DatabaseType.SQLSERVER.getName() + " " + getVersionString(); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/feature/SqlServerVersion.java b/src/main/java/net/sf/jsqlparser/util/validation/feature/SqlServerVersion.java new file mode 100644 index 0000000..a59e787 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/feature/SqlServerVersion.java @@ -0,0 +1,180 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.feature; + +import java.util.Collections; +import java.util.EnumSet; +import java.util.Set; + +import net.sf.jsqlparser.parser.feature.Feature; + +/** + * Please add Features supported and place a link to public documentation + * + * @author gitmotte + */ +public enum SqlServerVersion implements Version { + V2019("2019", + EnumSet.of( + // supported if used with jdbc + Feature.jdbcParameter, + Feature.jdbcNamedParameter, + // expressions + Feature.exprLike, + // https://docs.microsoft.com/en-us/sql/t-sql/queries/select-transact-sql?view=sql-server-ver15 + Feature.select, + Feature.selectInto, + Feature.withItem, + Feature.selectGroupBy, Feature.function, + // https://docs.microsoft.com/en-us/sql/t-sql/queries/from-transact-sql?view=sql-server-ver15 + // see user_defined_function + Feature.tableFunction, + Feature.selectHaving, Feature.orderBy, + Feature.distinct, + Feature.withItem, Feature.withItemRecursive, + // https://docs.microsoft.com/en-us/sql/t-sql/queries/top-transact-sql?view=sql-server-ver15 + Feature.top, + // https://docs.microsoft.com/en-us/sql/t-sql/queries/select-order-by-clause-transact-sql?view=sql-server-ver15 + Feature.offset, Feature.offsetParam, Feature.fetch, Feature.fetchFirst, + Feature.fetchNext, + + // https://docs.microsoft.com/en-us/sql/t-sql/language-elements/set-operators-except-and-intersect-transact-sql?view=sql-server-ver15 + // https://docs.microsoft.com/en-us/sql/t-sql/language-elements/set-operators-union-transact-sql?view=sql-server-ver15 + Feature.setOperation, Feature.setOperationUnion, Feature.setOperationIntersect, + Feature.setOperationExcept, + + // https://docs.microsoft.com/en-us/sql/t-sql/queries/table-value-constructor-transact-sql?view=sql-server-ver15 + Feature.values, + + // https://docs.microsoft.com/en-us/sql/t-sql/queries/from-transact-sql?view=sql-server-ver15#syntax + Feature.join, + Feature.joinSimple, + Feature.joinRight, + Feature.joinFull, + Feature.joinLeft, + Feature.joinCross, + Feature.joinOuter, + Feature.joinInner, + Feature.joinApply, + + // https://docs.microsoft.com/en-us/sql/t-sql/statements/insert-transact-sql?view=sql-server-ver15 + Feature.insert, + Feature.insertValues, + Feature.insertFromSelect, + // https://docs.microsoft.com/en-us/sql/t-sql/queries/update-transact-sql?view=sql-server-ver15 + Feature.update, + Feature.updateFrom, + + // https://docs.microsoft.com/en-us/sql/t-sql/statements/delete-transact-sql?view=sql-server-ver15 + Feature.delete, + // https://docs.microsoft.com/en-us/sql/t-sql/statements/truncate-table-transact-sql?view=sql-server-ver15 + Feature.truncate, + + Feature.drop, + // https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-table-transact-sql?view=sql-server-ver15 + Feature.dropTable, + // https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-index-transact-sql?view=sql-server-ver15 + Feature.dropIndex, + // https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-view-transact-sql?view=sql-server-ver15 + Feature.dropView, + // https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-schema-transact-sql?view=sql-server-ver15 + Feature.dropSchema, + // https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-sequence-transact-sql?view=sql-server-ver15 + Feature.dropSequence, + Feature.dropTableIfExists, Feature.dropIndexIfExists, Feature.dropViewIfExists, + Feature.dropSchemaIfExists, Feature.dropSequenceIfExists, + + // https://docs.microsoft.com/en-us/sql/t-sql/language-elements/execute-transact-sql?view=sql-server-ver15 + Feature.execute, + Feature.executeExec, Feature.executeExecute, + + // https://docs.microsoft.com/en-us/sql/t-sql/language-elements/set-local-variable-transact-sql?view=sql-server-ver15 + Feature.set, + + // https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-table-transact-sql?view=sql-server-ver15 + Feature.alterTable, + // https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-sequence-transact-sql?view=sql-server-ver15 + Feature.alterSequence, + // https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-view-transact-sql?view=sql-server-ver15 + Feature.alterView, + // https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-index-transact-sql?view=sql-server-ver15 + Feature.alterIndex, + // https://docs.microsoft.com/en-us/sql/t-sql/statements/create-index-transact-sql?view=sql-server-ver15 + Feature.createIndex, + // https://docs.microsoft.com/en-us/sql/t-sql/statements/create-sequence-transact-sql?view=sql-server-ver15 + Feature.createSequence, + // https://docs.microsoft.com/en-us/sql/t-sql/statements/create-table-transact-sql?view=sql-server-ver15 + Feature.createTable, Feature.createTableTableOptionStrings, + Feature.createTableFromSelect, + // https://docs.microsoft.com/en-us/sql/t-sql/statements/create-function-transact-sql?view=sql-server-ver15 + // https://docs.microsoft.com/en-us/sql/t-sql/statements/create-procedure-transact-sql?view=sql-server-ver15 + Feature.functionalStatement, Feature.createProcedure, Feature.createFunction, + Feature.block, + Feature.declare, + // https://docs.microsoft.com/en-us/sql/t-sql/statements/create-schema-transact-sql?view=sql-server-ver15 + Feature.createSchema, + // https://docs.microsoft.com/en-us/sql/t-sql/statements/create-view-transact-sql?view=sql-server-ver15 + Feature.createView, + // https://docs.microsoft.com/en-us/sql/t-sql/statements/create-trigger-transact-sql?view=sql-server-ver15 + Feature.createTrigger, + // https://docs.microsoft.com/en-us/sql/t-sql/statements/merge-transact-sql?view=sql-server-ver15 + Feature.merge, + // https://docs.microsoft.com/en-us/sql/t-sql/statements/grant-transact-sql?view=sql-server-ver15 + Feature.grant, + // https://docs.microsoft.com/en-us/sql/t-sql/language-elements/commit-transaction-transact-sql?view=sql-server-ver15 + Feature.commit, + // special sql-server features + // https://docs.microsoft.com/en-us/sql/relational-databases/xml/for-xml-sql-server?view=sql-server-ver15 + Feature.selectForXmlPath, + Feature.use, Feature.allowSquareBracketQuotation, // + Feature.pivot, Feature.unpivot, Feature.pivotXml, + Feature.selectGroupByGroupingSets)); + + private Set features; + private String versionString; + + /** + * @param versionString + * @param featuresSupported + * @see #copy() to copy from previous version + */ + SqlServerVersion(String versionString, Set featuresSupported) { + this(versionString, featuresSupported, Collections.emptySet()); + } + + /** + * @param versionString + * @param featuresSupported + * @param unsupported + * @see #copy() to copy from previous version + */ + SqlServerVersion(String versionString, Set featuresSupported, + Set unsupported) { + this.versionString = versionString; + this.features = featuresSupported; + this.features.removeAll(unsupported); + } + + @Override + public String getVersionString() { + return versionString; + } + + @Override + public Set getFeatures() { + return features; + } + + @Override + public String getName() { + return DatabaseType.SQLSERVER.getName() + " " + getVersionString(); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/feature/Version.java b/src/main/java/net/sf/jsqlparser/util/validation/feature/Version.java new file mode 100644 index 0000000..5ec921b --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/feature/Version.java @@ -0,0 +1,31 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.feature; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.util.validation.ValidationException; + +public interface Version extends FeatureSetValidation { + + /** + * @return the version string + */ + String getVersionString(); + + /** + * @return featureName + " not supported." + */ + @Override + default ValidationException getMessage(Feature feature) { + return toError(feature.name() + " not supported."); + } + + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/metadata/AbstractDatabaseMetaDataCapability.java b/src/main/java/net/sf/jsqlparser/util/validation/metadata/AbstractDatabaseMetaDataCapability.java new file mode 100644 index 0000000..22f8b6f --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/metadata/AbstractDatabaseMetaDataCapability.java @@ -0,0 +1,167 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.metadata; + +import java.sql.Connection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.function.BiPredicate; +import java.util.function.UnaryOperator; + +/** + * Adapter class always throwing {@link UnsupportedOperationException} for all exists - methods. + * + * @author gitmotte + */ +public abstract class AbstractDatabaseMetaDataCapability implements DatabaseMetaDataValidation { + + protected Connection connection; + protected boolean cacheResults; + protected Map results = new HashMap<>(); + protected UnaryOperator namesLookup = NamesLookup.NO_TRANSFORMATION; + + /** + * With caching enabled - see {@link #isCacheResults()} + * + * @param connection + * @param namesLookup - see {@link NamesLookup} + * @see #AbstractDatabaseMetaDataCapability(Connection, UnaryOperator, boolean) + */ + public AbstractDatabaseMetaDataCapability(Connection connection, + UnaryOperator namesLookup) { + this(connection, namesLookup, true); + } + + /** + * @param connection + * @param namesLookup - see {@link NamesLookup} + * @param cacheResults - whether the results should be cached for later lookups + * @see #AbstractDatabaseMetaDataCapability(Connection, UnaryOperator) + */ + public AbstractDatabaseMetaDataCapability(Connection connection, + UnaryOperator namesLookup, + boolean cacheResults) { + this.connection = connection; + this.namesLookup = namesLookup; + this.cacheResults = cacheResults; + } + + public UnaryOperator getNamesLookup() { + return namesLookup; + } + + public Connection getConnection() { + return connection; + } + + public boolean isCacheResults() { + return cacheResults; + } + + public AbstractDatabaseMetaDataCapability clearCache() { + this.results.clear(); + return this; + } + + @Override + @SuppressWarnings({"PMD.CyclomaticComplexity"}) + public final boolean exists(Named named) { + Objects.requireNonNull(named); + + named.setFqnLookup(getNamesLookup().apply(named.getFqn())); + named.setAliasLookup(getNamesLookup().apply(named.getAlias())); + + switch (named.getNamedObject()) { + case table: + return cache(named, this::tableExists); + case column: + return cache(named, this::columnExists); + case schema: + return cache(named, this::schemaExists); + case index: + return cache(named, this::indexExists); + case database: + return cache(named, this::databaseExists); + case constraint: + case uniqueConstraint: + return cache(named, this::constraintExists); + case view: + return cache(named, this::viewExists); + case procedure: + return cache(named, this::procedureExists); + case user: + return cache(named, this::userExists); + case role: + return cache(named, this::roleExists); + default: + } + throw new UnsupportedOperationException( + named.getFqn() + ": evaluation of " + named.getNamedObject() + + "-name not implemented."); + } + + protected boolean cache(Named named, BiPredicate, Named> fn) { + Map m = Collections.unmodifiableMap(results); + if (cacheResults) { + return results.computeIfAbsent(named, k -> fn.test(m, k)); + } else { + return fn.test(m, named); + } + } + + protected boolean roleExists(Map results, Named name) { + throw unsupported(name); + } + + protected boolean userExists(Map results, Named name) { + throw unsupported(name); + } + + protected boolean procedureExists(Map results, Named name) { + throw unsupported(name); + } + + protected boolean databaseExists(Map results, Named name) { + throw unsupported(name); + } + + protected boolean constraintExists(Map results, Named name) { + throw unsupported(name); + } + + protected boolean viewExists(Map results, Named name) { + throw unsupported(name); + } + + protected boolean indexExists(Map results, Named name) { + throw unsupported(name); + } + + protected boolean schemaExists(Map results, Named name) { + throw unsupported(name); + } + + protected boolean columnExists(Map results, Named name) { + throw unsupported(name); + } + + protected boolean tableExists(Map results, Named name) { + throw unsupported(name); + } + + protected UnsupportedOperationException unsupported(Named name) { + return new UnsupportedOperationException( + name.getFqn() + ": evaluation of " + name.getNamedObject() + + "-name not supported."); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/metadata/DatabaseException.java b/src/main/java/net/sf/jsqlparser/util/validation/metadata/DatabaseException.java new file mode 100644 index 0000000..9a7ff15 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/metadata/DatabaseException.java @@ -0,0 +1,37 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.metadata; + +import java.sql.SQLException; + +import net.sf.jsqlparser.util.validation.ValidationException; + +/** + * database-errors wrapping a {@link SQLException} or PersistenceException + * + * @author gitmotte + */ +public class DatabaseException extends ValidationException { + + private static final long serialVersionUID = 1L; + + public DatabaseException(String message, Throwable cause) { + super(message, cause); + } + + public DatabaseException(String message) { + super(message); + } + + public DatabaseException(Throwable cause) { + super(cause); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/metadata/DatabaseMetaDataValidation.java b/src/main/java/net/sf/jsqlparser/util/validation/metadata/DatabaseMetaDataValidation.java new file mode 100644 index 0000000..b9e8ff8 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/metadata/DatabaseMetaDataValidation.java @@ -0,0 +1,85 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.metadata; + +import java.sql.SQLException; +import java.util.function.Consumer; + +import net.sf.jsqlparser.util.validation.UnexpectedValidationException; +import net.sf.jsqlparser.util.validation.ValidationCapability; +import net.sf.jsqlparser.util.validation.ValidationContext; +import net.sf.jsqlparser.util.validation.ValidationException; + +public interface DatabaseMetaDataValidation extends ValidationCapability { + + String NAME = "meta data"; + + /** + * @param context + * @param errorConsumer + * @throws ValidationException + */ + @Override + default void validate(ValidationContext context, Consumer errorConsumer) { + Named named = context.get(MetadataContext.named, Named.class); + boolean checkForExists = context.get(MetadataContext.exists, Boolean.class); + try { + boolean exists = exists(named); + if (exists ^ checkForExists) { // XOR + errorConsumer.accept(getErrorMessage(named, checkForExists)); + } + } catch (ValidationException ve) { + errorConsumer.accept(ve); + } catch (UnsupportedOperationException uoe) { + errorConsumer.accept(new ValidationException( + "This Operation " + named.toString() + " is not supported yet.", uoe)); + } catch (Exception e) { + errorConsumer.accept(getUnexpectedErrorMessage(named, e)); + } + } + + /** + * @param named + * @return true, if the object exists, false otherwise. + * @throws ValidationException - on specific errors like {@link DatabaseException} on + * database-errors wrapping a {@link SQLException} or PersistenceException + * @throws UnsupportedOperationException - if testing of given {@link NamedObject} is not + * supported. + */ + boolean exists(Named named); + + /** + * @param named + * @param checkForExists + * @return a new {@link ValidationException} + */ + default ValidationException getErrorMessage(Named named, boolean checkForExists) { + return toError( + String.format("%s does %sexist.", named.getFqn(), checkForExists ? "not " : "")); + } + + /** + * @param named + * @param cause + * @return a new {@link ValidationException} + */ + default ValidationException getUnexpectedErrorMessage(Named named, Exception cause) { + return new UnexpectedValidationException( + named.getFqn() + ": cannot validate " + named.getNamedObject() + "-name. detail: " + + cause.getMessage(), + cause); + } + + @Override + default String getName() { + return NAME; + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/metadata/JdbcDatabaseMetaDataCapability.java b/src/main/java/net/sf/jsqlparser/util/validation/metadata/JdbcDatabaseMetaDataCapability.java new file mode 100644 index 0000000..a3d5102 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/metadata/JdbcDatabaseMetaDataCapability.java @@ -0,0 +1,206 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.metadata; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +import net.sf.jsqlparser.util.validation.UnexpectedValidationException; +import net.sf.jsqlparser.util.validation.ValidationException; + +/** + * Validates against schema by jdbc-metadata in a very basic way with simple caching and comparing + * names by {@link String#equalsIgnoreCase(String)} + * + * @author gitmotte + */ +public class JdbcDatabaseMetaDataCapability extends AbstractDatabaseMetaDataCapability { + + private static final String VIEW = "VIEW"; + private static final String TABLE = "TABLE"; + private static final String COLUMN = "COLUMN"; + private static final Logger LOG = + Logger.getLogger(JdbcDatabaseMetaDataCapability.class.getName()); + + /** + * @param connection + * @param namesLookup - see {@link NamesLookup} + */ + public JdbcDatabaseMetaDataCapability(Connection connection, + UnaryOperator namesLookup) { + super(connection, namesLookup); + } + + /** + * @param connection + * @param namesLookup - see {@link NamesLookup} + * @param cacheResults - whether the results should be cached for later lookups + */ + public JdbcDatabaseMetaDataCapability(Connection connection, UnaryOperator namesLookup, + boolean cacheResults) { + super(connection, namesLookup, cacheResults); + } + + @Override + @SuppressWarnings({"PMD.CyclomaticComplexity"}) + protected boolean columnExists(Map results, Named named) + throws ValidationException { + String[] names = splitAndValidateMinMax(COLUMN, named.getFqnLookup(), 1, 4); + String columnName = names[names.length - 1]; + + List possibleParents = null; + List parents = named.getParents().isEmpty() ? Arrays.asList(NamedObject.table) + : named.getParents(); + + int lastIndexOf = named.getFqnLookup().lastIndexOf("."); + String fqnParent = + lastIndexOf != -1 ? named.getFqnLookup().substring(0, lastIndexOf) : null; + + // try to match parents in results + Predicate predicate = null; + if (fqnParent != null) { + predicate = n -> parents.contains(n.getNamedObject()) + && (fqnParent.equals(n.getAliasLookup()) || fqnParent.equals(n.getFqnLookup())); + } else { + predicate = n -> parents.contains(n.getNamedObject()); + } + possibleParents = results.keySet().stream().filter(predicate).map(Named::getFqnLookup) + .collect(Collectors.toList()); + + if (possibleParents.isEmpty()) { + possibleParents = Collections.singletonList(fqnParent); + } + + for (String fqn : possibleParents) { + if (existsFromItem(results, fqn)) { + String query = String.format("SELECT * FROM %s", fqn); + try (PreparedStatement ps = connection.prepareStatement(query)) { + ResultSetMetaData metaData = ps.getMetaData(); + for (int i = 1; i <= metaData.getColumnCount(); i++) { + if (columnName.equalsIgnoreCase(metaData.getColumnLabel(i))) { + return true; + } + } + } catch (SQLException e) { + throw createDatabaseException(fqn, COLUMN, e); + } + } else if (LOG.isLoggable(Level.FINE)) { + LOG.fine(String.format("%s does not exists, cannot evaluate COLUMN from %s", fqn, + named.getFqn())); + } + } + return false; + } + + private boolean existsFromItem(Map results, String fqn) { + Named named = new Named(NamedObject.table, fqn).setFqnLookup(fqn); + return viewExists(results, named) || tableExists(results, named); + } + + @Override + protected boolean viewExists(Map results, Named named) + throws ValidationException { + return jdbcMetadataTables(named, VIEW); + } + + @Override + protected boolean tableExists(Map results, Named named) + throws ValidationException { + return jdbcMetadataTables(named, TABLE); + } + + @SuppressWarnings({"PMD.CyclomaticComplexity"}) + protected boolean jdbcMetadataTables(Named named, String type) throws ValidationException { + String[] names = splitAndValidateMinMax(type, named.getFqnLookup(), 1, 3); + + String catalog = null; + String schemaPattern = null; + String tableNamePattern; + if (names.length > 2) { + catalog = names[0]; + schemaPattern = names[1]; + tableNamePattern = names[2]; + } else if (names.length > 1) { + schemaPattern = names[0]; + tableNamePattern = names[1]; + } else { + tableNamePattern = names[0]; + } + + + List tables = new ArrayList<>(); + try (ResultSet rs = + connection.getMetaData().getTables(catalog, schemaPattern, tableNamePattern, + new String[] {type})) { + while (rs.next()) { + String tableCat = rs.getString("TABLE_CAT"); + String tableSchem = rs.getString("TABLE_SCHEM"); + String tableName = rs.getString("TABLE_NAME"); + if (tableName.equalsIgnoreCase(names[names.length - 1])) { + if (names.length > 1) { + if (tableSchem.equalsIgnoreCase(names[names.length - 2])) { + if (names.length > 2) { + if (tableCat.equalsIgnoreCase(names[names.length - 3])) { + tables.add(String.join(".", tableCat, tableSchem, tableName)); + } + } else { + tables.add(String.join(".", tableSchem, tableName)); + } + } + } else { + tables.add(tableName); + } + } + } + } catch (SQLException e) { + throw createDatabaseException(named.getFqn(), type, e); + } + + return !tables.isEmpty(); + } + + /** + * Split fqn by "." and validate expected path-elements + * + * @param type + * @param fqn + * @param min + * @param max + * @return the fqn-parts + */ + private String[] splitAndValidateMinMax(String type, String fqn, int min, int max) { + String[] names = fqn.split("\\."); + if (names.length < min || names.length > max) { + throw new UnexpectedValidationException(String.format( + "%s path-elements count needs to be between %s and %s for %s", fqn, min, max, + type)); + } + return names; + } + + private DatabaseException createDatabaseException(String fqn, String type, SQLException e) { + return new DatabaseException(String.format( + "cannot evaluate existence of %s by name '%s'", type, fqn), e); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/metadata/MetadataContext.java b/src/main/java/net/sf/jsqlparser/util/validation/metadata/MetadataContext.java new file mode 100644 index 0000000..38ea0b3 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/metadata/MetadataContext.java @@ -0,0 +1,23 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.metadata; + +import net.sf.jsqlparser.util.validation.ContextKey; + +public enum MetadataContext implements ContextKey { + /** + * @see Named + */ + named, + /** + * true, check for existence, false, check for non-existence + */ + exists +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/metadata/Named.java b/src/main/java/net/sf/jsqlparser/util/validation/metadata/Named.java new file mode 100644 index 0000000..2957df9 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/metadata/Named.java @@ -0,0 +1,127 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.metadata; + +import java.util.List; +import java.util.Objects; + +public class Named { + + private final NamedObject namedObject; + private final String fqn; + private String alias; + private List parents; + + private String fqnLookup; + private String aliasLookup; + + public Named(NamedObject namedObject, String fqn) { + Objects.requireNonNull(namedObject, "named object must not be null"); + Objects.requireNonNull(fqn, "fully qualified name must not be null"); + this.namedObject = namedObject; + this.fqn = fqn; + } + + public String getFqn() { + return fqn; + } + + public String getAlias() { + return alias; + } + + public Named setAlias(String alias) { + this.alias = alias; + return this; + } + + public NamedObject getNamedObject() { + return namedObject; + } + + public List getParents() { + return parents; + } + + public Named setParents(List parents) { + this.parents = parents; + return this; + } + + /** + * @return the fqn transformed for catalog-lookup (uppercase/lowercase/.. depends on database) + */ + public String getFqnLookup() { + return fqnLookup; + } + + public Named setFqnLookup(String fqnLookup) { + this.fqnLookup = fqnLookup; + return this; + } + + /** + * @return the alias transformed for catalog-lookup (uppercase/lowercase/.. depends on database) + */ + public String getAliasLookup() { + return aliasLookup; + } + + public Named setAliasLookup(String aliasLookup) { + this.aliasLookup = aliasLookup; + return this; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((alias == null) ? 0 : alias.hashCode()); + result = prime * result + fqn.hashCode(); + result = prime * result + namedObject.hashCode(); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Named other = (Named) obj; + if (alias == null) { + if (other.alias != null) { + return false; + } + } else if (!alias.equals(other.alias)) { + return false; + } + if (!fqn.equals(other.fqn)) { + return false; + } + if (namedObject != other.namedObject) { + return false; + } + return true; + } + + @Override + public String toString() { + return "Named [namedObject=" + namedObject + ", fqn=" + fqn + ", alias=" + alias + + ", parents=" + parents + "]"; + } + + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/metadata/NamedObject.java b/src/main/java/net/sf/jsqlparser/util/validation/metadata/NamedObject.java new file mode 100644 index 0000000..742f183 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/metadata/NamedObject.java @@ -0,0 +1,55 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.metadata; + +public enum NamedObject { + /** + * a name constisting of max. 1 identifiers, i.e. [database] + */ + database, + /** + * a name constisting of max. 2 identifiers, i.e. [database].[schema] + */ + schema, + /** + * a name constisting of max. 3 identifiers, i.e. [catalog].[schema].[table] + */ + table, + /** + * a name constisting of max. 3 identifiers, i.e. [catalog].[schema].[view] + */ + view, + /** + * a name constisting of min 2 (the table-reference) and max. 4 identifiers, i.e. + * [catalog].[schema].[table].[columnName] + */ + column, index, constraint, uniqueConstraint, + /** + * a name constisting of max. 3 identifiers, i.e. [catalog].[schema].[sequence] + */ + sequence, synonym, procedure, user, role, trigger, alias; + + /** + * @param name + * @return null, if not found, otherwise the {@link NamedObject} + */ + public static NamedObject forName(String name) { + for (NamedObject o : values()) { + if (o.equalsIgnoreCase(name)) { + return o; + } + } + return null; + } + + public boolean equalsIgnoreCase(String name) { + return name().equalsIgnoreCase(name); + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/metadata/NamesLookup.java b/src/main/java/net/sf/jsqlparser/util/validation/metadata/NamesLookup.java new file mode 100644 index 0000000..46c2aae --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/metadata/NamesLookup.java @@ -0,0 +1,32 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.metadata; + +import java.util.function.Function; +import java.util.function.UnaryOperator; + +/** + * A strategy for transformation of database-names before lookup in database-catalog-metadata + */ +public enum NamesLookup implements UnaryOperator { + UPPERCASE(String::toUpperCase), LOWERCASE(String::toLowerCase), NO_TRANSFORMATION( + UnaryOperator.identity()); + + private Function strategy; + + NamesLookup(UnaryOperator strategy) { + this.strategy = strategy; + } + + @Override + public String apply(String name) { + return name == null ? null : strategy.apply(name); + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/AbstractValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/AbstractValidator.java new file mode 100644 index 0000000..9e810ca --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/AbstractValidator.java @@ -0,0 +1,439 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.select.FromItem; +import net.sf.jsqlparser.statement.select.OrderByElement; +import net.sf.jsqlparser.util.validation.ValidationCapability; +import net.sf.jsqlparser.util.validation.ValidationContext; +import net.sf.jsqlparser.util.validation.ValidationException; +import net.sf.jsqlparser.util.validation.Validator; +import net.sf.jsqlparser.util.validation.feature.FeatureContext; +import net.sf.jsqlparser.util.validation.feature.FeatureSetValidation; +import net.sf.jsqlparser.util.validation.metadata.DatabaseMetaDataValidation; +import net.sf.jsqlparser.util.validation.metadata.MetadataContext; +import net.sf.jsqlparser.util.validation.metadata.Named; +import net.sf.jsqlparser.util.validation.metadata.NamedObject; + +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * A abstract base for a Validation + * + * @param the type of statement this DeParser supports + * @author gitmotte + */ +public abstract class AbstractValidator implements Validator { + + private final Map> errors = new HashMap<>(); + private final Map>, AbstractValidator> validatorForwards = + new HashMap<>(); + private ValidationContext context = new ValidationContext(); + + public > T getValidator(Class type) { + return type.cast(validatorForwards.computeIfAbsent(type, this::newObject)); + } + + private > E newObject(Class type) { + try { + E e = type.cast(type.getConstructor().newInstance()); + e.setContext(context()); + return e; + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException + | InvocationTargetException | NoSuchMethodException | SecurityException e) { + throw new IllegalStateException( + "Type " + type + " cannot be constructed by empty constructor!"); + } + } + + protected Consumer getMessageConsumer(ValidationCapability c) { + return s -> putError(c, s); + } + + protected ValidationContext context() { + return context(true); + } + + protected ValidationContext context(boolean reInit) { + return context.reinit(reInit); + } + + /** + * adds an error for this {@link ValidationCapability} + * + * @param capability + * @param error + */ + protected void putError(ValidationCapability capability, ValidationException error) { + errors.computeIfAbsent(capability, k -> new HashSet<>()).add(error); + } + + @Override + public final Map> getValidationErrors() { + Map> map = new HashMap<>(); + map.putAll(errors); + for (AbstractValidator v : validatorForwards.values()) { + for (Entry> e : v.getValidationErrors() + .entrySet()) { + Set set = map.get(e.getKey()); + if (set == null) { + map.put(e.getKey(), e.getValue()); + } else { + set.addAll(e.getValue()); + } + } + } + return map; + } + + public Collection getCapabilities() { + return context().getCapabilities(); + } + + @Override + public final void setContext(ValidationContext context) { + this.context = context; + } + + protected void validateOptional(E element, Consumer elementConsumer) { + if (element != null) { + elementConsumer.accept(element); + } + } + + protected > void validateOptionalList(List elementList, + Supplier validatorSupplier, BiConsumer elementConsumer) { + if (isNotEmpty(elementList)) { + V validator = validatorSupplier.get(); + elementList.forEach(e -> elementConsumer.accept(e, validator)); + } + } + + protected void validateOptionalExpression(Expression expression) { + validateOptional(expression, e -> e.accept(getValidator(ExpressionValidator.class), null)); + } + + protected void validateOptionalExpression(Expression expression, ExpressionValidator v) { + validateOptional(expression, e -> e.accept(v, null)); + } + + protected void validateOptionalExpressions(List expressions) { + validateOptionalList(expressions, () -> getValidator(ExpressionValidator.class), + (o, v) -> o.accept(v, null)); + } + + protected void validateOptionalFromItems(FromItem... fromItems) { + validateOptionalFromItems(Arrays.asList(fromItems)); + } + + protected void validateOptionalFromItems(List fromItems) { + validateOptionalList(fromItems, () -> getValidator(SelectValidator.class), + this::validateOptionalFromItem); + } + + protected void validateOptionalOrderByElements(List orderByElements) { + validateOptionalList(orderByElements, () -> getValidator(OrderByValidator.class), + (o, v) -> o.accept(v, null)); + } + + protected void validateOptionalFromItem(FromItem fromItem) { + validateOptional(fromItem, i -> i.accept(getValidator(SelectValidator.class), null)); + } + + protected void validateOptionalFromItem(FromItem fromItem, SelectValidator v) { + validateOptional(fromItem, i -> i.accept(v, null)); + } + + /** + * Iterates through all {@link ValidationCapability} and validates the feature with + * {@link #validateFeature(ValidationCapability, Feature)} + * + * @param feature + */ + protected void validateFeature(Feature feature) { + for (ValidationCapability c : getCapabilities()) { + validateFeature(c, feature); + } + } + + /** + * Iterates through all {@link ValidationCapability} and validates + *
    + *
  • the name with {@link #validateName(ValidationCapability, NamedObject, String)}
  • + *
  • the feature with {@link #validateFeature(ValidationCapability, Feature)}
  • + *
+ * + * @param feature + * @param namedObject + * @param fqn - fully qualified name of named object + */ + protected void validateFeatureAndName(Feature feature, NamedObject namedObject, String fqn) { + validateFeatureAndNameWithAlias(feature, namedObject, fqn, null); + } + + /** + * Iterates through all {@link ValidationCapability} and validates + *
    + *
  • the name with {@link #validateName(ValidationCapability, NamedObject, String)}
  • + *
  • the feature with {@link #validateFeature(ValidationCapability, Feature)}
  • + *
+ * + * @param feature + * @param namedObject + * @param fqn - fully qualified name of named object + * @param alias + */ + protected void validateFeatureAndNameWithAlias(Feature feature, NamedObject namedObject, + String fqn, String alias) { + for (ValidationCapability c : getCapabilities()) { + validateFeature(c, feature); + validateNameWithAlias(c, namedObject, fqn, alias, true); + } + } + + /** + * Iterates through all {@link ValidationCapability} and validates for the name with + * {@link #validateName(ValidationCapability, NamedObject, String)} + * + * @param namedObject + * @param fqn - fully qualified name of named object + */ + protected void validateName(NamedObject namedObject, String fqn) { + validateNameWithAlias(namedObject, fqn, null); + } + + /** + * Iterates through all {@link ValidationCapability} and validates for the name with + * {@link #validateName(ValidationCapability, NamedObject, String)} + * + * @param namedObject + * @param fqn - fully qualified name of named object + * @param alias + */ + protected void validateNameWithAlias(NamedObject namedObject, String fqn, String alias) { + for (ValidationCapability c : getCapabilities()) { + validateNameWithAlias(c, namedObject, fqn, alias, true); + } + } + + /** + * Validates the feature if given {@link ValidationCapability} is a {@link FeatureSetValidation} + * and condition is true + * + * @param capability + * @param condition + * @param feature + */ + protected void validateFeature(ValidationCapability capability, boolean condition, + Feature feature) { + if (condition) { + validateFeature(capability, feature); + } + } + + /** + * validates for the feature if given elements is not empty - see + * {@link #isNotEmpty(Collection)} + * + * @param capability + * @param elements + * @param feature + */ + protected void validateOptionalFeature(ValidationCapability capability, List elements, + Feature feature) { + validateFeature(capability, isNotEmpty(elements), feature); + } + + /** + * Validates for the feature if given element is not null + * + * @param capability + * @param element + * @param feature + */ + protected void validateOptionalFeature(ValidationCapability capability, Object element, + Feature feature) { + validateFeature(capability, element != null, feature); + } + + /** + * Validates if given {@link ValidationCapability} is a {@link FeatureSetValidation} + * + * @param capability + * @param feature + */ + protected void validateFeature(ValidationCapability capability, Feature feature) { + if (capability instanceof FeatureSetValidation) { + capability.validate(context().put(FeatureContext.feature, feature), + getMessageConsumer(capability)); + } + } + + /** + * Validates if given {@link ValidationCapability} is a {@link DatabaseMetaDataValidation} + * + * @param capability + * @param namedObject + * @param fqn - fully qualified name of named object + * @param alias + */ + protected void validateNameWithAlias(ValidationCapability capability, NamedObject namedObject, + String fqn, String alias) { + validateNameWithAlias(capability, namedObject, fqn, alias, true); + } + + /** + * @param capability + * @param namedObject + * @param fqn - fully qualified name of named object + */ + protected void validateName(ValidationCapability capability, NamedObject namedObject, + String fqn) { + validateNameWithAlias(capability, namedObject, fqn, null, true); + } + + /** + * Validates if given {@link ValidationCapability} is a {@link DatabaseMetaDataValidation} + * + * @param capability + * @param namedObject + * @param fqn - fully qualified name of named object + * @param alias + * @param exists - true, check for existence, false, check for + * non-existence + */ + protected void validateNameWithAlias(ValidationCapability capability, NamedObject namedObject, + String fqn, String alias, boolean exists, NamedObject... parents) { + if (capability instanceof DatabaseMetaDataValidation) { + capability + .validate( + context() + .put(MetadataContext.named, + new Named(namedObject, fqn).setAlias(alias) + .setParents(Arrays.asList(parents))) // + .put(MetadataContext.exists, exists), + getMessageConsumer(capability)); + } + } + + /** + * @param capability + * @param namedObject + * @param fqn - fully qualified name of named object + * @param exists + * @param parents + */ + protected void validateName(ValidationCapability capability, NamedObject namedObject, + String fqn, boolean exists, NamedObject... parents) { + validateNameWithAlias(capability, namedObject, fqn, null, exists, parents); + } + + /** + * @param capability + * @param name + */ + protected void validateOptionalColumnName(ValidationCapability capability, String name) { + validateOptionalName(capability, NamedObject.column, name, null, true); + } + + /** + * @param capability + * @param name + * @param alias + */ + protected void validateOptionalColumnNameWithAlias(ValidationCapability capability, String name, + String alias) { + validateOptionalName(capability, NamedObject.column, name, alias, true); + } + + /** + * @param capability + * @param columnNames + * @param parents + */ + protected void validateOptionalColumnNames(ValidationCapability capability, + List columnNames, NamedObject... parents) { + validateOptionalColumnNames(capability, columnNames, true, parents); + } + + /** + * @param capability + * @param columnNames + * @param exists + * @param parents + */ + protected void validateOptionalColumnNames(ValidationCapability capability, + List columnNames, boolean exists, NamedObject... parents) { + if (columnNames != null) { + columnNames.forEach(n -> validateOptionalName(capability, NamedObject.column, n, null, + exists, parents)); + } + } + + /** + * @param capability + * @param namedObject + * @param name + * @param alias + * @param parents + */ + protected void validateOptionalNameWithAlias(ValidationCapability capability, + NamedObject namedObject, String name, String alias, NamedObject... parents) { + validateOptionalName(capability, namedObject, name, alias, true, parents); + } + + /** + * @param capability + * @param namedObject + * @param name + * @param parents + */ + protected void validateOptionalName(ValidationCapability capability, NamedObject namedObject, + String name, NamedObject... parents) { + validateOptionalNameWithAlias(capability, namedObject, name, (String) null, parents); + } + + /** + * @param capability + * @param namedObject + * @param name + * @param alias + * @param exists + * @param parents + */ + protected void validateOptionalName(ValidationCapability capability, NamedObject namedObject, + String name, String alias, boolean exists, NamedObject... parents) { + if (name != null) { + validateNameWithAlias(capability, namedObject, name, alias, exists, parents); + } + } + + protected boolean isNotEmpty(Collection c) { + return c != null && !c.isEmpty(); + } + + protected boolean isNotEmpty(String c) { + return c != null && !c.isEmpty(); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/AlterSequenceValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/AlterSequenceValidator.java new file mode 100644 index 0000000..80d9e73 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/AlterSequenceValidator.java @@ -0,0 +1,29 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.alter.sequence.AlterSequence; +import net.sf.jsqlparser.util.validation.metadata.NamedObject; + +/** + * @author gitmotte + */ +public class AlterSequenceValidator extends AbstractValidator { + + + @Override + public void validate(AlterSequence statement) { + validateFeatureAndName(Feature.alterSequence, NamedObject.sequence, + statement.getSequence().getFullyQualifiedName()); + } + + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/AlterSessionValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/AlterSessionValidator.java new file mode 100644 index 0000000..1f45828 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/AlterSessionValidator.java @@ -0,0 +1,22 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.statement.alter.AlterSession; + +/** + * @author gitmotte + */ +public class AlterSessionValidator extends AbstractValidator { + @Override + public void validate(AlterSession statement) { + // @todo: implement this method + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/AlterValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/AlterValidator.java new file mode 100644 index 0000000..e8dc84b --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/AlterValidator.java @@ -0,0 +1,85 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import java.util.EnumSet; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.alter.Alter; +import net.sf.jsqlparser.statement.alter.AlterExpression; +import net.sf.jsqlparser.statement.alter.AlterExpression.ColumnDataType; +import net.sf.jsqlparser.statement.alter.AlterExpression.ColumnDropNotNull; +import net.sf.jsqlparser.statement.alter.AlterOperation; +import net.sf.jsqlparser.util.validation.ValidationCapability; +import net.sf.jsqlparser.util.validation.ValidationUtil; +import net.sf.jsqlparser.util.validation.metadata.NamedObject; + +/** + * @author gitmotte + */ +public class AlterValidator extends AbstractValidator { + + @Override + public void validate(Alter alter) { + validateFeature(Feature.alterTable); + + validateOptionalFromItem(alter.getTable()); + + alter.getAlterExpressions().forEach(e -> validate(alter, e)); + } + + public void validate(Alter alter, AlterExpression e) { + for (ValidationCapability c : getCapabilities()) { + + validateOptionalColumnName(c, e.getColumnOldName()); + validateOptionalColumnName(c, e.getColumnName()); + + if (e.getColumnDropNotNullList() != null) { + validateOptionalColumnNames(c, ValidationUtil.map(e.getColumnDropNotNullList(), + ColumnDropNotNull::getColumnName)); + } + + if (e.getColDataTypeList() != null) { + boolean validateForExist = + !EnumSet.of(AlterOperation.ADD).contains(e.getOperation()); + validateOptionalColumnNames(c, + ValidationUtil.map(e.getColDataTypeList(), ColumnDataType::getColumnName), + validateForExist, + NamedObject.table); + } + + validateOptionalName(c, NamedObject.constraint, e.getConstraintName()); + if (e.getPkColumns() != null) { + validateOptionalColumnNames(c, e.getPkColumns()); + } + + if (e.getFkColumns() != null) { + validateName(c, NamedObject.table, e.getFkSourceTable()); + validateOptionalColumnNames(c, e.getFkColumns()); + validateOptionalColumnNames(c, e.getFkSourceColumns()); + } + + if (e.getUk()) { + validateName(c, NamedObject.uniqueConstraint, e.getUkName()); + validateOptionalColumnNames(c, e.getUkColumns(), NamedObject.uniqueConstraint); + } + + if (e.getIndex() != null) { + validateName(c, NamedObject.index, e.getIndex().getName()); + if (e.getIndex().getColumns() != null) { + validateOptionalColumnNames(c, e.getIndex().getColumnsNames(), + NamedObject.index); + } + } + } + } + + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/AlterViewValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/AlterViewValidator.java new file mode 100644 index 0000000..db390af --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/AlterViewValidator.java @@ -0,0 +1,33 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.create.view.AlterView; +import net.sf.jsqlparser.util.validation.ValidationCapability; +import net.sf.jsqlparser.util.validation.metadata.NamedObject; + +/** + * @author gitmotte + */ +public class AlterViewValidator extends AbstractValidator { + + @Override + public void validate(AlterView alterView) { + for (ValidationCapability c : getCapabilities()) { + validateFeature(Feature.alterView); + validateFeature(c, alterView.isUseReplace(), Feature.alterViewReplace); + validateName(c, NamedObject.view, alterView.getView().getFullyQualifiedName()); + validateOptionalColumnNames(c, alterView.getColumnNames()); + } + alterView.getSelect().accept(getValidator(SelectValidator.class), null); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/AnalyzeValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/AnalyzeValidator.java new file mode 100644 index 0000000..ac5de0c --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/AnalyzeValidator.java @@ -0,0 +1,25 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2022 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.analyze.Analyze; +import net.sf.jsqlparser.util.validation.ValidationCapability; +import net.sf.jsqlparser.util.validation.metadata.NamedObject; + +public class AnalyzeValidator extends AbstractValidator { + @Override + public void validate(Analyze analyze) { + for (ValidationCapability c : getCapabilities()) { + validateFeature(c, Feature.analyze); + validateName(c, NamedObject.table, analyze.getTable().getFullyQualifiedName(), true); + } + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/CreateIndexValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/CreateIndexValidator.java new file mode 100644 index 0000000..0a08601 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/CreateIndexValidator.java @@ -0,0 +1,34 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.create.index.CreateIndex; +import net.sf.jsqlparser.statement.create.table.Index; +import net.sf.jsqlparser.util.validation.metadata.NamedObject; +import net.sf.jsqlparser.util.validation.ValidationCapability; + +/** + * @author gitmotte + */ +public class CreateIndexValidator extends AbstractValidator { + + @Override + public void validate(CreateIndex createIndex) { + Index index = createIndex.getIndex(); + for (ValidationCapability c : getCapabilities()) { + validateFeature(c, Feature.createIndex); + validateName(c, NamedObject.table, createIndex.getTable().getFullyQualifiedName()); + validateName(c, NamedObject.index, index.getName(), false); + validateOptionalColumnNames(c, index.getColumnsNames(), NamedObject.table); + } + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/CreateSequenceValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/CreateSequenceValidator.java new file mode 100644 index 0000000..404c58c --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/CreateSequenceValidator.java @@ -0,0 +1,26 @@ +/* + * #%L JSQLParser library %% Copyright (C) 2004 - 2020 JSQLParser %% Dual licensed under GNU LGPL + * 2.1 or Apache License 2.0 #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.create.sequence.CreateSequence; +import net.sf.jsqlparser.util.validation.ValidationCapability; +import net.sf.jsqlparser.util.validation.metadata.NamedObject; + +/** + * @author gitmotte + */ +public class CreateSequenceValidator extends AbstractValidator { + + + @Override + public void validate(CreateSequence statement) { + for (ValidationCapability c : getCapabilities()) { + validateFeature(Feature.createSequence); + validateName(c, NamedObject.sequence, statement.getSequence().getFullyQualifiedName(), + false); + } + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/CreateSynonymValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/CreateSynonymValidator.java new file mode 100644 index 0000000..7ef2476 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/CreateSynonymValidator.java @@ -0,0 +1,25 @@ +/* + * #%L JSQLParser library %% Copyright (C) 2004 - 2020 JSQLParser %% Dual licensed under GNU LGPL + * 2.1 or Apache License 2.0 #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.create.synonym.CreateSynonym; +import net.sf.jsqlparser.util.validation.ValidationCapability; +import net.sf.jsqlparser.util.validation.metadata.NamedObject; + +/** + * @author gitmotte + */ +public class CreateSynonymValidator extends AbstractValidator { + + @Override + public void validate(CreateSynonym statement) { + for (ValidationCapability c : getCapabilities()) { + validateFeature(Feature.createSynonym); + validateName(c, NamedObject.synonym, statement.getSynonym().getFullyQualifiedName(), + false); + } + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/CreateTableValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/CreateTableValidator.java new file mode 100644 index 0000000..e4b7a3c --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/CreateTableValidator.java @@ -0,0 +1,51 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.create.table.CreateTable; +import net.sf.jsqlparser.statement.create.table.Index; +import net.sf.jsqlparser.util.validation.ValidationCapability; +import net.sf.jsqlparser.util.validation.metadata.NamedObject; + +/** + * @author gitmotte + */ +public class CreateTableValidator extends AbstractValidator { + + + @Override + public void validate(CreateTable createTable) { + for (ValidationCapability c : getCapabilities()) { + validateFeature(c, Feature.createTable); + validateFeature(c, createTable.isUnlogged(), Feature.createTableUnlogged); + validateOptionalFeature(c, createTable.getCreateOptionsStrings(), + Feature.createTableCreateOptionStrings); + validateOptionalFeature(c, createTable.getTableOptionsStrings(), + Feature.createTableTableOptionStrings); + validateFeature(c, createTable.isIfNotExists(), Feature.createTableIfNotExists); + validateOptionalFeature(c, createTable.getRowMovement(), + Feature.createTableRowMovement); + validateOptionalFeature(c, createTable.getSelect(), Feature.createTableFromSelect); + if (isNotEmpty(createTable.getIndexes())) { + for (Index i : createTable.getIndexes()) { + validateName(c, NamedObject.index, i.getName()); + } + } + validateName(c, NamedObject.table, createTable.getTable().getFullyQualifiedName(), + false); + } + + if (createTable.getSelect() != null) { + getValidator(StatementValidator.class).validate(createTable.getSelect()); + } + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/CreateViewValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/CreateViewValidator.java new file mode 100644 index 0000000..262bb4e --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/CreateViewValidator.java @@ -0,0 +1,44 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.create.view.CreateView; +import net.sf.jsqlparser.statement.create.view.ForceOption; +import net.sf.jsqlparser.statement.create.view.TemporaryOption; +import net.sf.jsqlparser.statement.select.Select; +import net.sf.jsqlparser.util.validation.ValidationCapability; +import net.sf.jsqlparser.util.validation.metadata.NamedObject; + +/** + * @author gitmotte + */ +public class CreateViewValidator extends AbstractValidator { + + @Override + public void validate(CreateView createView) { + for (ValidationCapability c : getCapabilities()) { + validateFeature(c, Feature.createView); + validateFeature(c, createView.isOrReplace(), Feature.createOrReplaceView); + validateFeature(c, !ForceOption.NONE.equals(createView.getForce()), + Feature.createViewForce); + validateFeature(c, !TemporaryOption.NONE.equals(createView.getTemporary()), + Feature.createViewTemporary); + validateFeature(c, createView.isMaterialized(), Feature.createViewMaterialized); + validateName(c, NamedObject.view, createView.getView().getFullyQualifiedName(), false); + validateFeature(c, createView.getViewCommentOptions() != null, + Feature.createViewWithComment); + } + SelectValidator v = getValidator(SelectValidator.class); + Select select = createView.getSelect(); + select.accept(v, null); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/DeclareStatementValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/DeclareStatementValidator.java new file mode 100644 index 0000000..813a98b --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/DeclareStatementValidator.java @@ -0,0 +1,29 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.DeclareStatement; +import net.sf.jsqlparser.util.validation.ValidationCapability; + +/** + * @author gitmotte + */ +public class DeclareStatementValidator extends AbstractValidator { + + @Override + public void validate(DeclareStatement declare) { + for (ValidationCapability c : getCapabilities()) { + validateFeature(c, Feature.declare); + } + validateOptionalExpression(declare.getUserVariable()); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/DeleteValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/DeleteValidator.java new file mode 100644 index 0000000..19588e3 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/DeleteValidator.java @@ -0,0 +1,57 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.delete.Delete; +import net.sf.jsqlparser.util.validation.ValidationCapability; + +/** + * @author gitmotte + */ +public class DeleteValidator extends AbstractValidator { + + + @Override + public void validate(Delete delete) { + for (ValidationCapability c : getCapabilities()) { + validateFeature(c, Feature.delete); + + validateOptionalFeature(c, delete.getTables(), Feature.deleteTables); + validateOptionalFeature(c, delete.getJoins(), Feature.deleteJoin); + validateOptionalFeature(c, delete.getLimit(), Feature.deleteLimit); + validateOptionalFeature(c, delete.getOrderByElements(), Feature.deleteOrderBy); + validateOptionalFeature(c, delete.getReturningClause(), + Feature.deleteReturningExpressionList); + } + + SelectValidator v = getValidator(SelectValidator.class); + delete.getTable().accept(v, null); + + if (isNotEmpty(delete.getTables())) { + delete.getTables().forEach(t -> t.accept(v, null)); + } + + validateOptionalExpression(delete.getWhere()); + validateOptionalOrderByElements(delete.getOrderByElements()); + + v.validateOptionalJoins(delete.getJoins()); + + if (delete.getLimit() != null) { + getValidator(LimitValidator.class).validate(delete.getLimit()); + } + + if (delete.getReturningClause() != null) { + delete.getReturningClause().forEach(c -> c.accept(v, null)); + } + + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/DropValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/DropValidator.java new file mode 100644 index 0000000..04ea1cf --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/DropValidator.java @@ -0,0 +1,54 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import java.util.Arrays; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.drop.Drop; +import net.sf.jsqlparser.util.validation.ValidationCapability; +import net.sf.jsqlparser.util.validation.metadata.NamedObject; + +/** + * @author gitmotte + */ +public class DropValidator extends AbstractValidator { + + + @Override + public void validate(Drop drop) { + String type = drop.getType(); + for (ValidationCapability c : getCapabilities()) { + validateFeature(c, Feature.drop); + validateFeature(c, NamedObject.table.name().equalsIgnoreCase(type), Feature.dropTable); + validateFeature(c, NamedObject.index.equalsIgnoreCase(type), Feature.dropIndex); + validateFeature(c, NamedObject.view.equalsIgnoreCase(type), Feature.dropView); + validateFeature(c, NamedObject.schema.equalsIgnoreCase(type), Feature.dropSchema); + validateFeature(c, NamedObject.sequence.equalsIgnoreCase(type), Feature.dropSequence); + + validateFeature(c, drop.isIfExists() && NamedObject.table.name().equalsIgnoreCase(type), + Feature.dropTableIfExists); + validateFeature(c, drop.isIfExists() && NamedObject.index.equalsIgnoreCase(type), + Feature.dropIndexIfExists); + validateFeature(c, drop.isIfExists() && NamedObject.view.equalsIgnoreCase(type), + Feature.dropViewIfExists); + validateFeature(c, drop.isIfExists() && NamedObject.schema.equalsIgnoreCase(type), + Feature.dropSchemaIfExists); + validateFeature(c, drop.isIfExists() && NamedObject.sequence.equalsIgnoreCase(type), + Feature.dropSequenceIfExists); + } + + NamedObject named = NamedObject.forName(type); + if (Arrays.asList(NamedObject.table, NamedObject.view).contains(named)) { + validateName(named, drop.getName().getFullyQualifiedName()); + } + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/ExecuteValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/ExecuteValidator.java new file mode 100644 index 0000000..3c91b91 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/ExecuteValidator.java @@ -0,0 +1,38 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.execute.Execute; +import net.sf.jsqlparser.statement.execute.Execute.ExecType; +import net.sf.jsqlparser.util.validation.ValidationCapability; +import net.sf.jsqlparser.util.validation.metadata.NamedObject; + +/** + * @author gitmotte + */ +public class ExecuteValidator extends AbstractValidator { + + + @Override + public void validate(Execute execute) { + for (ValidationCapability c : getCapabilities()) { + validateFeature(c, Feature.execute); + validateFeature(c, ExecType.EXECUTE.equals(execute.getExecType()), + Feature.executeExecute); + validateFeature(c, ExecType.EXEC.equals(execute.getExecType()), Feature.executeExec); + validateFeature(c, ExecType.CALL.equals(execute.getExecType()), Feature.executeCall); + validateName(NamedObject.procedure, execute.getName()); + } + + validateOptionalExpression(execute.getExprList()); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/ExpressionValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/ExpressionValidator.java new file mode 100644 index 0000000..ae82e70 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/ExpressionValidator.java @@ -0,0 +1,1213 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.expression.AllValue; +import net.sf.jsqlparser.expression.AnalyticExpression; +import net.sf.jsqlparser.expression.AnyComparisonExpression; +import net.sf.jsqlparser.expression.ArrayConstructor; +import net.sf.jsqlparser.expression.ArrayExpression; +import net.sf.jsqlparser.expression.BinaryExpression; +import net.sf.jsqlparser.expression.CaseExpression; +import net.sf.jsqlparser.expression.CastExpression; +import net.sf.jsqlparser.expression.CollateExpression; +import net.sf.jsqlparser.expression.ConnectByRootOperator; +import net.sf.jsqlparser.expression.DateTimeLiteralExpression; +import net.sf.jsqlparser.expression.DateValue; +import net.sf.jsqlparser.expression.DoubleValue; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; +import net.sf.jsqlparser.expression.ExtractExpression; +import net.sf.jsqlparser.expression.Function; +import net.sf.jsqlparser.expression.HexValue; +import net.sf.jsqlparser.expression.IntervalExpression; +import net.sf.jsqlparser.expression.JdbcNamedParameter; +import net.sf.jsqlparser.expression.JdbcParameter; +import net.sf.jsqlparser.expression.JsonAggregateFunction; +import net.sf.jsqlparser.expression.JsonExpression; +import net.sf.jsqlparser.expression.JsonFunction; +import net.sf.jsqlparser.expression.KeepExpression; +import net.sf.jsqlparser.expression.LambdaExpression; +import net.sf.jsqlparser.expression.LongValue; +import net.sf.jsqlparser.expression.MySQLGroupConcat; +import net.sf.jsqlparser.expression.NextValExpression; +import net.sf.jsqlparser.expression.NotExpression; +import net.sf.jsqlparser.expression.NullValue; +import net.sf.jsqlparser.expression.NumericBind; +import net.sf.jsqlparser.expression.OracleHierarchicalExpression; +import net.sf.jsqlparser.expression.OracleHint; +import net.sf.jsqlparser.expression.OracleNamedFunctionParameter; +import net.sf.jsqlparser.expression.OverlapsCondition; +import net.sf.jsqlparser.expression.RangeExpression; +import net.sf.jsqlparser.expression.RowConstructor; +import net.sf.jsqlparser.expression.RowGetExpression; +import net.sf.jsqlparser.expression.SignedExpression; +import net.sf.jsqlparser.expression.StringValue; +import net.sf.jsqlparser.expression.StructType; +import net.sf.jsqlparser.expression.TimeKeyExpression; +import net.sf.jsqlparser.expression.TimeValue; +import net.sf.jsqlparser.expression.TimestampValue; +import net.sf.jsqlparser.expression.TimezoneExpression; +import net.sf.jsqlparser.expression.TranscodingFunction; +import net.sf.jsqlparser.expression.TrimFunction; +import net.sf.jsqlparser.expression.UserVariable; +import net.sf.jsqlparser.expression.VariableAssignment; +import net.sf.jsqlparser.expression.WhenClause; +import net.sf.jsqlparser.expression.WindowElement; +import net.sf.jsqlparser.expression.WindowOffset; +import net.sf.jsqlparser.expression.WindowRange; +import net.sf.jsqlparser.expression.XMLSerializeExpr; +import net.sf.jsqlparser.expression.operators.arithmetic.Addition; +import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseAnd; +import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseLeftShift; +import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseOr; +import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseRightShift; +import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseXor; +import net.sf.jsqlparser.expression.operators.arithmetic.Concat; +import net.sf.jsqlparser.expression.operators.arithmetic.Division; +import net.sf.jsqlparser.expression.operators.arithmetic.IntegerDivision; +import net.sf.jsqlparser.expression.operators.arithmetic.Modulo; +import net.sf.jsqlparser.expression.operators.arithmetic.Multiplication; +import net.sf.jsqlparser.expression.operators.arithmetic.Subtraction; +import net.sf.jsqlparser.expression.operators.conditional.AndExpression; +import net.sf.jsqlparser.expression.operators.conditional.OrExpression; +import net.sf.jsqlparser.expression.operators.conditional.XorExpression; +import net.sf.jsqlparser.expression.operators.relational.Between; +import net.sf.jsqlparser.expression.operators.relational.ContainedBy; +import net.sf.jsqlparser.expression.operators.relational.Contains; +import net.sf.jsqlparser.expression.operators.relational.DoubleAnd; +import net.sf.jsqlparser.expression.operators.relational.EqualsTo; +import net.sf.jsqlparser.expression.operators.relational.ExcludesExpression; +import net.sf.jsqlparser.expression.operators.relational.ExistsExpression; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.expression.operators.relational.FullTextSearch; +import net.sf.jsqlparser.expression.operators.relational.GeometryDistance; +import net.sf.jsqlparser.expression.operators.relational.GreaterThan; +import net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals; +import net.sf.jsqlparser.expression.operators.relational.InExpression; +import net.sf.jsqlparser.expression.operators.relational.IncludesExpression; +import net.sf.jsqlparser.expression.operators.relational.IsBooleanExpression; +import net.sf.jsqlparser.expression.operators.relational.IsDistinctExpression; +import net.sf.jsqlparser.expression.operators.relational.IsNullExpression; +import net.sf.jsqlparser.expression.operators.relational.JsonOperator; +import net.sf.jsqlparser.expression.operators.relational.LikeExpression; +import net.sf.jsqlparser.expression.operators.relational.Matches; +import net.sf.jsqlparser.expression.operators.relational.MemberOfExpression; +import net.sf.jsqlparser.expression.operators.relational.MinorThan; +import net.sf.jsqlparser.expression.operators.relational.MinorThanEquals; +import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo; +import net.sf.jsqlparser.expression.operators.relational.OldOracleJoinBinaryExpression; +import net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator; +import net.sf.jsqlparser.expression.operators.relational.SimilarToExpression; +import net.sf.jsqlparser.expression.operators.relational.SupportsOldOracleJoinSyntax; +import net.sf.jsqlparser.expression.operators.relational.TSQLLeftJoin; +import net.sf.jsqlparser.expression.operators.relational.TSQLRightJoin; +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.schema.Column; +import net.sf.jsqlparser.statement.select.AllColumns; +import net.sf.jsqlparser.statement.select.AllTableColumns; +import net.sf.jsqlparser.statement.select.ParenthesedSelect; +import net.sf.jsqlparser.statement.select.PlainSelect; +import net.sf.jsqlparser.statement.select.Select; +import net.sf.jsqlparser.statement.select.SelectItem; +import net.sf.jsqlparser.util.validation.ValidationCapability; +import net.sf.jsqlparser.util.validation.metadata.NamedObject; + +/** + * @author gitmotte + */ +@SuppressWarnings({"PMD.CyclomaticComplexity"}) +public class ExpressionValidator extends AbstractValidator + implements ExpressionVisitor { + @Override + public Void visit(Addition addition, S context) { + visitBinaryExpression(addition, " + "); + return null; + } + + @Override + public Void visit(AndExpression andExpression, S context) { + visitBinaryExpression(andExpression, andExpression.isUseOperator() ? " && " : " AND "); + return null; + } + + @Override + public Void visit(Between between, S context) { + between.getLeftExpression().accept(this, context); + between.getBetweenExpressionStart().accept(this, context); + between.getBetweenExpressionEnd().accept(this, context); + return null; + } + + @Override + public Void visit(OverlapsCondition overlapsCondition, S context) { + validateOptionalExpressionList(overlapsCondition.getLeft()); + validateOptionalExpressionList(overlapsCondition.getRight()); + return null; + } + + + @Override + public Void visit(EqualsTo equalsTo, S context) { + validateOldOracleJoinBinaryExpression(equalsTo, " = ", context); + return null; + } + + @Override + public Void visit(Division division, S context) { + visitBinaryExpression(division, " / "); + return null; + } + + @Override + public Void visit(IntegerDivision division, S context) { + visitBinaryExpression(division, " DIV "); + return null; + } + + @Override + public Void visit(DoubleValue doubleValue, S context) { + // nothing to validate + return null; + } + + @Override + public Void visit(HexValue hexValue, S context) { + // nothing to validate + return null; + } + + @Override + public Void visit(NotExpression notExpr, S context) { + notExpr.getExpression().accept(this, context); + return null; + } + + @Override + public Void visit(BitwiseRightShift expr, S context) { + visitBinaryExpression(expr, " >> "); + return null; + } + + @Override + public Void visit(BitwiseLeftShift expr, S context) { + visitBinaryExpression(expr, " << "); + return null; + } + + public void validateOldOracleJoinBinaryExpression(OldOracleJoinBinaryExpression expression, + String operator, S context) { + for (ValidationCapability c : getCapabilities()) { + validateOptionalExpression(expression.getLeftExpression(), this); + if (expression.getOldOracleJoinSyntax() != SupportsOldOracleJoinSyntax.NO_ORACLE_JOIN) { + validateFeature(c, Feature.oracleOldJoinSyntax); + } + validateOptionalExpression(expression.getRightExpression(), this); + if (expression + .getOraclePriorPosition() != SupportsOldOracleJoinSyntax.NO_ORACLE_PRIOR) { + validateFeature(c, Feature.oraclePriorPosition); + } + } + } + + @Override + public Void visit(GreaterThan greaterThan, S context) { + validateOldOracleJoinBinaryExpression(greaterThan, " > ", context); + return null; + } + + @Override + public Void visit(GreaterThanEquals greaterThanEquals, S context) { + validateOldOracleJoinBinaryExpression(greaterThanEquals, " >= ", context); + + return null; + } + + @Override + public Void visit(InExpression inExpression, S context) { + for (ValidationCapability c : getCapabilities()) { + validateOptionalExpression(inExpression.getLeftExpression(), this); + if (inExpression + .getOldOracleJoinSyntax() != SupportsOldOracleJoinSyntax.NO_ORACLE_JOIN) { + validateFeature(c, Feature.oracleOldJoinSyntax); + } + } + validateOptionalExpression(inExpression.getRightExpression(), this); + return null; + } + + @Override + public Void visit(IncludesExpression includesExpression, S context) { + validateOptionalExpression(includesExpression.getLeftExpression(), this); + validateOptionalExpression(includesExpression.getRightExpression(), this); + return null; + } + + @Override + public Void visit(ExcludesExpression excludesExpression, S context) { + validateOptionalExpression(excludesExpression.getLeftExpression(), this); + validateOptionalExpression(excludesExpression.getRightExpression(), this); + return null; + } + + @Override + public Void visit(FullTextSearch fullTextSearch, S context) { + validateOptionalExpressions(fullTextSearch.getMatchColumns()); + return null; + } + + @Override + public Void visit(SignedExpression signedExpression, S context) { + signedExpression.getExpression().accept(this, context); + return null; + } + + @Override + public Void visit(IsNullExpression isNullExpression, S context) { + isNullExpression.getLeftExpression().accept(this, context); + return null; + } + + @Override + public Void visit(IsBooleanExpression isBooleanExpression, S context) { + isBooleanExpression.getLeftExpression().accept(this, context); + return null; + } + + @Override + public Void visit(JdbcParameter jdbcParameter, S context) { + validateFeature(Feature.jdbcParameter); + return null; + } + + public void visit(PlainSelect plainSelect) { + visit(plainSelect, null); // Call the parametrized visit method with null context + } + + public void visit(Addition addition) { + visit(addition, null); // Call the parametrized visit method with null context + } + + public void visit(AndExpression andExpression) { + visit(andExpression, null); // Call the parametrized visit method with null context + } + + public void visit(Between between) { + visit(between, null); // Call the parametrized visit method with null context + } + + public void visit(OverlapsCondition overlapsCondition) { + visit(overlapsCondition, null); // Call the parametrized visit method with null context + } + + public void visit(EqualsTo equalsTo) { + visit(equalsTo, null); // Call the parametrized visit method with null context + } + + public void visit(Division division) { + visit(division, null); // Call the parametrized visit method with null context + } + + public void visit(IntegerDivision division) { + visit(division, null); // Call the parametrized visit method with null context + } + + public void visit(DoubleValue doubleValue) { + visit(doubleValue, null); // Call the parametrized visit method with null context + } + + public void visit(HexValue hexValue) { + visit(hexValue, null); // Call the parametrized visit method with null context + } + + public void visit(NotExpression notExpr) { + visit(notExpr, null); // Call the parametrized visit method with null context + } + + public void visit(BitwiseRightShift expr) { + visit(expr, null); // Call the parametrized visit method with null context + } + + public void visit(BitwiseLeftShift expr) { + visit(expr, null); // Call the parametrized visit method with null context + } + + public void visit(GreaterThan greaterThan) { + visit(greaterThan, null); // Call the parametrized visit method with null context + } + + public void visit(GreaterThanEquals greaterThanEquals) { + visit(greaterThanEquals, null); // Call the parametrized visit method with null context + } + + public void visit(InExpression inExpression) { + visit(inExpression, null); // Call the parametrized visit method with null context + } + + public void visit(IncludesExpression includesExpression) { + visit(includesExpression, null); // Call the parametrized visit method with null context + } + + public void visit(ExcludesExpression excludesExpression) { + visit(excludesExpression, null); // Call the parametrized visit method with null context + } + + public void visit(FullTextSearch fullTextSearch) { + visit(fullTextSearch, null); // Call the parametrized visit method with null context + } + + public void visit(SignedExpression signedExpression) { + visit(signedExpression, null); // Call the parametrized visit method with null context + } + + public void visit(IsNullExpression isNullExpression) { + visit(isNullExpression, null); // Call the parametrized visit method with null context + } + + public void visit(IsBooleanExpression isBooleanExpression) { + visit(isBooleanExpression, null); // Call the parametrized visit method with null context + } + + public void visit(JdbcParameter jdbcParameter) { + visit(jdbcParameter, null); // Call the parametrized visit method with null context + } + + + @Override + public Void visit(LikeExpression likeExpression, S context) { + validateFeature(Feature.exprLike); + visitBinaryExpression(likeExpression, (likeExpression.isNot() ? " NOT" : "") + + (likeExpression.isCaseInsensitive() ? " ILIKE " : " LIKE ")); + return null; + } + + @Override + public Void visit(ExistsExpression existsExpression, S context) { + existsExpression.getRightExpression().accept(this, context); + return null; + } + + @Override + public Void visit(MemberOfExpression memberOfExpression, S context) { + memberOfExpression.getLeftExpression().accept(this, context); + memberOfExpression.getRightExpression().accept(this, context); + return null; + } + + @Override + public Void visit(LongValue longValue, S context) { + // nothing to validate + return null; + } + + @Override + public Void visit(MinorThan minorThan, S context) { + validateOldOracleJoinBinaryExpression(minorThan, " < ", context); + + return null; + } + + @Override + public Void visit(MinorThanEquals minorThanEquals, S context) { + validateOldOracleJoinBinaryExpression(minorThanEquals, " <= ", context); + + return null; + } + + @Override + public Void visit(Multiplication multiplication, S context) { + visitBinaryExpression(multiplication, " * "); + + return null; + } + + @Override + public Void visit(NotEqualsTo notEqualsTo, S context) { + validateOldOracleJoinBinaryExpression(notEqualsTo, + " " + notEqualsTo.getStringExpression() + " ", context); + return null; + } + + @Override + public Void visit(DoubleAnd doubleAnd, S context) { + + return null; + } + + @Override + public Void visit(Contains contains, S context) { + + return null; + } + + @Override + public Void visit(ContainedBy containedBy, S context) { + + return null; + } + + @Override + public Void visit(NullValue nullValue, S context) { + // nothing to validate + return null; + } + + @Override + public Void visit(OrExpression orExpression, S context) { + visitBinaryExpression(orExpression, " OR "); + + return null; + } + + @Override + public Void visit(XorExpression xorExpression, S context) { + visitBinaryExpression(xorExpression, " XOR "); + + return null; + } + + @Override + public Void visit(StringValue stringValue, S context) { + // nothing to validate + return null; + } + + @Override + public Void visit(Subtraction subtraction, S context) { + visitBinaryExpression(subtraction, " - "); + return null; + } + + protected void visitBinaryExpression(BinaryExpression binaryExpression, String operator) { + binaryExpression.getLeftExpression().accept(this, null); + binaryExpression.getRightExpression().accept(this, null); + } + + @Override + public Void visit(ParenthesedSelect selectBody, S context) { + validateOptionalFromItem(selectBody); + return null; + } + + @Override + public Void visit(Column tableColumn, S context) { + validateName(NamedObject.column, tableColumn.getFullyQualifiedName()); + return null; + } + + @Override + public Void visit(Function function, S context) { + validateFeature(Feature.function); + + validateOptionalExpressionList(function.getNamedParameters()); + validateOptionalExpressionList(function.getParameters()); + + Object attribute = function.getAttribute(); + if (attribute instanceof Expression) { + validateOptionalExpression((Expression) attribute, this); + } + + validateOptionalExpression(function.getKeep(), this); + validateOptionalOrderByElements(function.getOrderByElements()); + return null; + } + + @Override + public Void visit(DateValue dateValue, S context) { + // nothing to validate + return null; + } + + @Override + public Void visit(TimestampValue timestampValue, S context) { + // nothing to validate + return null; + } + + @Override + public Void visit(TimeValue timeValue, S context) { + // nothing to validate + return null; + } + + @Override + public Void visit(CaseExpression caseExpression, S context) { + Expression switchExp = caseExpression.getSwitchExpression(); + if (switchExp != null) { + switchExp.accept(this, context); + } + + caseExpression.getWhenClauses().forEach(wc -> wc.accept(this, context)); + + Expression elseExp = caseExpression.getElseExpression(); + if (elseExp != null) { + elseExp.accept(this, context); + } + return null; + } + + public void visit(LikeExpression likeExpression) { + visit(likeExpression, null); + } + + public void visit(ExistsExpression existsExpression) { + visit(existsExpression, null); + } + + public void visit(MemberOfExpression memberOfExpression) { + visit(memberOfExpression, null); + } + + public void visit(LongValue longValue) { + visit(longValue, null); + } + + public void visit(MinorThan minorThan) { + visit(minorThan, null); + } + + public void visit(MinorThanEquals minorThanEquals) { + visit(minorThanEquals, null); + } + + public void visit(Multiplication multiplication) { + visit(multiplication, null); + } + + public void visit(NotEqualsTo notEqualsTo) { + visit(notEqualsTo, null); + } + + public void visit(DoubleAnd doubleAnd) { + visit(doubleAnd, null); + } + + public void visit(Contains contains) { + visit(contains, null); + } + + public void visit(ContainedBy containedBy) { + visit(containedBy, null); + } + + public void visit(NullValue nullValue) { + visit(nullValue, null); + } + + public void visit(OrExpression orExpression) { + visit(orExpression, null); + } + + public void visit(XorExpression xorExpression) { + visit(xorExpression, null); + } + + public void visit(StringValue stringValue) { + visit(stringValue, null); + } + + public void visit(Subtraction subtraction) { + visit(subtraction, null); + } + + public void visit(ParenthesedSelect selectBody) { + visit(selectBody, null); + } + + public void visit(Column tableColumn) { + visit(tableColumn, null); + } + + public void visit(Function function) { + visit(function, null); + } + + public void visit(DateValue dateValue) { + visit(dateValue, null); + } + + public void visit(TimestampValue timestampValue) { + visit(timestampValue, null); + } + + public void visit(TimeValue timeValue) { + visit(timeValue, null); + } + + public void visit(CaseExpression caseExpression) { + visit(caseExpression, null); + } + + + @Override + public Void visit(WhenClause whenClause, S context) { + whenClause.getWhenExpression().accept(this, context); + whenClause.getThenExpression().accept(this, context); + return null; + } + + @Override + public Void visit(AnyComparisonExpression anyComparisonExpression, S context) { + anyComparisonExpression.getSelect().accept(this, context); + return null; + } + + @Override + public Void visit(Concat concat, S context) { + visitBinaryExpression(concat, " || "); + return null; + } + + @Override + public Void visit(Matches matches, S context) { + validateOldOracleJoinBinaryExpression(matches, " @@ ", context); + return null; + } + + @Override + public Void visit(BitwiseAnd bitwiseAnd, S context) { + visitBinaryExpression(bitwiseAnd, " & "); + return null; + } + + @Override + public Void visit(BitwiseOr bitwiseOr, S context) { + visitBinaryExpression(bitwiseOr, " | "); + return null; + } + + @Override + public Void visit(BitwiseXor bitwiseXor, S context) { + visitBinaryExpression(bitwiseXor, " ^ "); + return null; + } + + @Override + public Void visit(CastExpression cast, S context) { + cast.getLeftExpression().accept(this, context); + return null; + } + + @Override + public Void visit(Modulo modulo, S context) { + visitBinaryExpression(modulo, " % "); + return null; + } + + @Override + public Void visit(AnalyticExpression aexpr, S context) { + validateOptionalExpression(aexpr.getExpression(), this); + validateOptionalExpression(aexpr.getOffset(), this); + validateOptionalExpression(aexpr.getDefaultValue(), this); + validateOptionalExpression(aexpr.getKeep(), this); + validateOptionalExpressionList(aexpr.getPartitionExpressionList()); + validateOptionalOrderByElements(aexpr.getOrderByElements()); + WindowElement windowElement = aexpr.getWindowElement(); + if (windowElement != null) { + validateOptionalWindowOffset(windowElement.getOffset()); + WindowRange range = windowElement.getRange(); + if (range != null) { + validateOptionalWindowOffset(range.getStart()); + validateOptionalWindowOffset(range.getEnd()); + } + } + validateOptionalExpression(aexpr.getFilterExpression()); + return null; + } + + private void validateOptionalWindowOffset(WindowOffset offset) { + if (offset != null) { + validateOptionalExpression(offset.getExpression()); + } + } + + @Override + public Void visit(ExtractExpression eexpr, S context) { + eexpr.getExpression().accept(this, context); + return null; + } + + @Override + public Void visit(IntervalExpression iexpr, S context) { + validateOptionalExpression(iexpr.getExpression()); + return null; + } + + @Override + public Void visit(JdbcNamedParameter jdbcNamedParameter, S context) { + validateFeature(Feature.jdbcNamedParameter); + return null; + } + + @Override + public Void visit(OracleHierarchicalExpression oexpr, S context) { + validateFeature(Feature.oracleHierarchicalExpression); + return null; + } + + @Override + public Void visit(RegExpMatchOperator rexpr, S context) { + visitBinaryExpression(rexpr, " " + rexpr.getStringExpression() + " "); + return null; + } + + @Override + public Void visit(JsonExpression jsonExpr, S context) { + validateOptionalExpression(jsonExpr.getExpression()); + return null; + } + + @Override + public Void visit(JsonOperator jsonExpr, S context) { + visitBinaryExpression(jsonExpr, " " + jsonExpr.getStringExpression() + " "); + return null; + } + + @Override + public Void visit(UserVariable var, S context) { + // nothing to validate + return null; + } + + @Override + public Void visit(NumericBind bind, S context) { + // nothing to validate + return null; + } + + @Override + public Void visit(KeepExpression aexpr, S context) { + validateOptionalOrderByElements(aexpr.getOrderByElements()); + return null; + } + + @Override + public Void visit(MySQLGroupConcat groupConcat, S context) { + validateOptionalExpressionList(groupConcat.getExpressionList()); + validateOptionalOrderByElements(groupConcat.getOrderByElements()); + return null; + } + + private void validateOptionalExpressionList(ExpressionList expressionList) { + if (expressionList != null) { + for (Expression expression : expressionList) { + expression.accept(this, null); + } + } + } + + public void visit(WhenClause whenClause) { + visit(whenClause, null); + } + + public void visit(AnyComparisonExpression anyComparisonExpression) { + visit(anyComparisonExpression, null); + } + + public void visit(Concat concat) { + visit(concat, null); + } + + public void visit(Matches matches) { + visit(matches, null); + } + + public void visit(BitwiseAnd bitwiseAnd) { + visit(bitwiseAnd, null); + } + + public void visit(BitwiseOr bitwiseOr) { + visit(bitwiseOr, null); + } + + public void visit(BitwiseXor bitwiseXor) { + visit(bitwiseXor, null); + } + + public void visit(CastExpression cast) { + visit(cast, null); + } + + public void visit(Modulo modulo) { + visit(modulo, null); + } + + public void visit(AnalyticExpression aexpr) { + visit(aexpr, null); + } + + public void visit(ExtractExpression eexpr) { + visit(eexpr, null); + } + + public void visit(IntervalExpression iexpr) { + visit(iexpr, null); + } + + public void visit(JdbcNamedParameter jdbcNamedParameter) { + visit(jdbcNamedParameter, null); + } + + public void visit(OracleHierarchicalExpression oexpr) { + visit(oexpr, null); + } + + public void visit(RegExpMatchOperator rexpr) { + visit(rexpr, null); + } + + public void visit(JsonExpression jsonExpr) { + visit(jsonExpr, null); + } + + public void visit(JsonOperator jsonExpr) { + visit(jsonExpr, null); + } + + public void visit(UserVariable var) { + visit(var, null); + } + + public void visit(NumericBind bind) { + visit(bind, null); + } + + public void visit(KeepExpression aexpr) { + visit(aexpr, null); + } + + public void visit(MySQLGroupConcat groupConcat) { + visit(groupConcat, null); + } + + @Override + public Void visit(ExpressionList expressionList, S context) { + validateOptionalExpressionList(expressionList); + return null; + } + + @Override + public Void visit(RowConstructor rowConstructor, S context) { + validateOptionalExpressionList(rowConstructor); + return null; + } + + @Override + public Void visit(RowGetExpression rowGetExpression, S context) { + rowGetExpression.getExpression().accept(this, context); + return null; + } + + @Override + public Void visit(OracleHint hint, S context) { + // nothing to validate + return null; + } + + @Override + public Void visit(TimeKeyExpression timeKeyExpression, S context) { + // nothing to validate + return null; + } + + @Override + public Void visit(DateTimeLiteralExpression literal, S context) { + // nothing to validate + return null; + } + + @Override + public Void visit(NextValExpression nextVal, S context) { + validateName(NamedObject.sequence, nextVal.getName()); + return null; + } + + @Override + public Void visit(CollateExpression col, S context) { + validateOptionalExpression(col.getLeftExpression()); + return null; + } + + @Override + public Void visit(SimilarToExpression expr, S context) { + validateFeature(Feature.exprSimilarTo); + visitBinaryExpression(expr, (expr.isNot() ? " NOT" : "") + " SIMILAR TO "); + return null; + } + + @Override + public Void visit(ArrayExpression array, S context) { + array.getObjExpression().accept(this, context); + if (array.getIndexExpression() != null) { + array.getIndexExpression().accept(this, context); + } + if (array.getStartIndexExpression() != null) { + array.getStartIndexExpression().accept(this, context); + } + if (array.getStopIndexExpression() != null) { + array.getStopIndexExpression().accept(this, context); + } + return null; + } + + @Override + public Void visit(ArrayConstructor aThis, S context) { + for (Expression expression : aThis.getExpressions()) { + expression.accept(this, context); + } + return null; + } + + @Override + public void validate(Expression expression) { + expression.accept(this, null); + } + + @Override + public Void visit(VariableAssignment a, S context) { + validateOptionalExpression(a.getExpression()); + if (a.getVariable() != null) { + a.getVariable().accept(this, context); + } + return null; + } + + @Override + public Void visit(TimezoneExpression a, S context) { + validateOptionalExpression(a.getLeftExpression()); + return null; + } + + @Override + public Void visit(XMLSerializeExpr xml, S context) { + return null; + } + + @Override + public Void visit(JsonAggregateFunction expression, S context) { + // no idea what this is good for + return null; + } + + @Override + public Void visit(JsonFunction expression, S context) { + // no idea what this is good for + return null; + } + + @Override + public Void visit(ConnectByRootOperator connectByRootOperator, S context) { + connectByRootOperator.getColumn().accept(this, context); + return null; + } + + @Override + public Void visit(OracleNamedFunctionParameter oracleNamedFunctionParameter, S context) { + oracleNamedFunctionParameter.getExpression().accept(this, context); + return null; + } + + @Override + public Void visit(AllColumns allColumns, S context) { + return null; + } + + @Override + public Void visit(AllTableColumns allTableColumns, S context) { + return null; + } + + @Override + public Void visit(AllValue allValue, S context) { + return null; + } + + @Override + public Void visit(IsDistinctExpression isDistinctExpression, S context) { + isDistinctExpression.getLeftExpression().accept(this, context); + isDistinctExpression.getRightExpression().accept(this, context); + return null; + } + + @Override + public Void visit(GeometryDistance geometryDistance, S context) { + validateOldOracleJoinBinaryExpression(geometryDistance, " <-> ", context); + return null; + } + + @Override + public Void visit(Select select, S context) { + return null; + } + + @Override + public Void visit(TranscodingFunction transcodingFunction, S context) { + transcodingFunction.getExpression().accept(this, context); + return null; + } + + @Override + public Void visit(TrimFunction trimFunction, S context) { + if (trimFunction.getExpression() != null) { + trimFunction.getExpression().accept(this, context); + } + if (trimFunction.getFromExpression() != null) { + trimFunction.getFromExpression().accept(this, context); + } + return null; + } + + @Override + public Void visit(RangeExpression rangeExpression, S context) { + rangeExpression.getStartExpression().accept(this, context); + rangeExpression.getEndExpression().accept(this, context); + return null; + } + + @Override + public Void visit(TSQLLeftJoin tsqlLeftJoin, S context) { + tsqlLeftJoin.getLeftExpression().accept(this, context); + tsqlLeftJoin.getRightExpression().accept(this, context); + return null; + } + + @Override + public Void visit(TSQLRightJoin tsqlRightJoin, S context) { + tsqlRightJoin.getLeftExpression().accept(this, context); + tsqlRightJoin.getRightExpression().accept(this, context); + return null; + } + + @Override + public Void visit(StructType structType, S context) { + if (structType.getArguments() != null) { + for (SelectItem selectItem : structType.getArguments()) { + selectItem.getExpression().accept(this, context); + } + } + return null; + } + + @Override + public Void visit(LambdaExpression lambdaExpression, S context) { + lambdaExpression.getExpression().accept(this, context); + return null; + } + + public void visit(TimeKeyExpression timeKeyExpression) { + visit(timeKeyExpression, null); + } + + public void visit(DateTimeLiteralExpression literal) { + visit(literal, null); + } + + public void visit(NextValExpression nextVal) { + visit(nextVal, null); + } + + public void visit(CollateExpression col) { + visit(col, null); + } + + public void visit(SimilarToExpression expr) { + visit(expr, null); + } + + public void visit(ArrayExpression array) { + visit(array, null); + } + + public void visit(ArrayConstructor aThis) { + visit(aThis, null); + } + + + public void visit(VariableAssignment a) { + visit(a, null); + } + + public void visit(TimezoneExpression a) { + visit(a, null); + } + + public void visit(XMLSerializeExpr xml) { + visit(xml, null); + } + + public void visit(JsonAggregateFunction expression) { + visit(expression, null); + } + + public void visit(JsonFunction expression) { + visit(expression, null); + } + + public void visit(ConnectByRootOperator connectByRootOperator) { + visit(connectByRootOperator, null); + } + + public void visit(OracleNamedFunctionParameter oracleNamedFunctionParameter) { + visit(oracleNamedFunctionParameter, null); + } + + public void visit(AllColumns allColumns) { + visit(allColumns, null); + } + + public void visit(AllTableColumns allTableColumns) { + visit(allTableColumns, null); + } + + public void visit(AllValue allValue) { + visit(allValue, null); + } + + public void visit(IsDistinctExpression isDistinctExpression) { + visit(isDistinctExpression, null); + } + + public void visit(GeometryDistance geometryDistance) { + visit(geometryDistance, null); + } + + public void visit(Select select) { + visit(select, null); + } + + public void visit(TranscodingFunction transcodingFunction) { + visit(transcodingFunction, null); + } + + public void visit(TrimFunction trimFunction) { + visit(trimFunction, null); + } + + public void visit(RangeExpression rangeExpression) { + visit(rangeExpression, null); + } + + public void visit(TSQLLeftJoin tsqlLeftJoin) { + visit(tsqlLeftJoin, null); + } + + public void visit(TSQLRightJoin tsqlRightJoin) { + visit(tsqlRightJoin, null); + } + + public void visit(StructType structType) { + visit(structType, null); + } + + public void visit(LambdaExpression lambdaExpression) { + visit(lambdaExpression, null); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/GrantValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/GrantValidator.java new file mode 100644 index 0000000..ceb7bd9 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/GrantValidator.java @@ -0,0 +1,37 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.grant.Grant; +import net.sf.jsqlparser.util.validation.ValidationCapability; +import net.sf.jsqlparser.util.validation.metadata.NamedObject; + +/** + * @author gitmotte + */ +public class GrantValidator extends AbstractValidator { + + @Override + public void validate(Grant grant) { + for (ValidationCapability c : getCapabilities()) { + validateFeature(c, Feature.grant); + if (isNotEmpty(grant.getUsers())) { + grant.getUsers().forEach(u -> validateName(NamedObject.user, u)); + } + if (grant.getRole() != null) { + validateName(NamedObject.role, grant.getRole()); + } + + // can't validate grant.getObjectName() - don't know the kind of this object. + } + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/GroupByValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/GroupByValidator.java new file mode 100644 index 0000000..ee2d715 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/GroupByValidator.java @@ -0,0 +1,53 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.select.GroupByElement; +import net.sf.jsqlparser.statement.select.GroupByVisitor; +import net.sf.jsqlparser.util.validation.ValidationCapability; + +/** + * @author gitmotte + */ +public class GroupByValidator extends AbstractValidator + implements GroupByVisitor { + + @Override + public void validate(GroupByElement groupBy) { + groupBy.accept(this, null); + } + + @Override + public Void visit(GroupByElement groupBy, S context) { + for (ValidationCapability c : getCapabilities()) { + validateFeature(c, Feature.selectGroupBy); + if (isNotEmpty(groupBy.getGroupingSets())) { + validateFeature(c, Feature.selectGroupByGroupingSets); + } + } + + validateOptionalExpressions(groupBy.getGroupByExpressions()); + + if (isNotEmpty(groupBy.getGroupingSets())) { + for (Object o : groupBy.getGroupingSets()) { + if (o instanceof Expression) { + validateOptionalExpression((Expression) o); + } else if (o instanceof ExpressionList) { + validateOptionalExpressions(((ExpressionList) o).getExpressions()); + } + } + } + return null; + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/InsertValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/InsertValidator.java new file mode 100644 index 0000000..c1186ba --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/InsertValidator.java @@ -0,0 +1,80 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.insert.Insert; +import net.sf.jsqlparser.statement.select.Values; +import net.sf.jsqlparser.statement.update.UpdateSet; +import net.sf.jsqlparser.util.validation.ValidationCapability; + +/** + * @author gitmotte + */ +public class InsertValidator extends AbstractValidator { + + + @Override + public void validate(Insert insert) { + for (ValidationCapability c : getCapabilities()) { + validateFeature(c, Feature.insert); + + if (insert.getSelect() instanceof Values) { + validateOptionalFeature(c, insert.getSelect().as(Values.class), + Feature.insertValues); + } + + validateOptionalFeature(c, insert.getModifierPriority(), + Feature.insertModifierPriority); + validateFeature(c, insert.isModifierIgnore(), Feature.insertModifierIgnore); + validateOptionalFeature(c, insert.getSelect(), Feature.insertFromSelect); + validateFeature(c, insert.isUseSet(), Feature.insertUseSet); + validateFeature(c, insert.isUseDuplicate(), Feature.insertUseDuplicateKeyUpdate); + validateOptionalFeature(c, insert.getReturningClause(), + Feature.insertReturningExpressionList); + } + + validateOptionalFromItem(insert.getTable()); + validateOptionalExpressions(insert.getColumns()); + + if (insert.getSelect() instanceof Values) { + insert.getSelect().accept(getValidator(StatementValidator.class), null); + validateOptionalExpressions(insert.getValues().getExpressions()); + } + + if (insert.getSetUpdateSets() != null) { + ExpressionValidator v = getValidator(ExpressionValidator.class); + // TODO is this useful? + // validateModelCondition (insert.getSetColumns().size() != + // insert.getSetExpressionList().size(), "model-error"); + for (UpdateSet updateSet : insert.getSetUpdateSets()) { + updateSet.getColumns().forEach(c -> c.accept(v, null)); + updateSet.getValues().forEach(c -> c.accept(v, null)); + } + } + + if (insert.getDuplicateUpdateSets() != null) { + ExpressionValidator v = getValidator(ExpressionValidator.class); + // TODO is this useful? + // validateModelCondition (insert.getSetColumns().size() != + // insert.getSetExpressionList().size(), "model-error"); + for (UpdateSet updateSet : insert.getDuplicateUpdateSets()) { + updateSet.getColumns().forEach(c -> c.accept(v, null)); + updateSet.getValues().forEach(c -> c.accept(v, null)); + } + } + + if (insert.getReturningClause() != null) { + SelectValidator v = getValidator(SelectValidator.class); + insert.getReturningClause().forEach(c -> c.accept(v, null)); + } + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/LimitValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/LimitValidator.java new file mode 100644 index 0000000..7089d67 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/LimitValidator.java @@ -0,0 +1,31 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.select.Limit; +import net.sf.jsqlparser.util.validation.ValidationCapability; + +/** + * @author gitmotte + */ +public class LimitValidator extends AbstractValidator { + + + @Override + public void validate(Limit limit) { + for (ValidationCapability c : getCapabilities()) { + validateFeature(c, Feature.limit); + validateFeature(c, limit.isLimitNull(), Feature.limitNull); + validateFeature(c, limit.isLimitAll(), Feature.limitAll); + validateOptionalFeature(c, limit.getOffset(), Feature.limitOffset); + } + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/MergeValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/MergeValidator.java new file mode 100644 index 0000000..e82ebc9 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/MergeValidator.java @@ -0,0 +1,74 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.merge.*; +import net.sf.jsqlparser.statement.update.UpdateSet; +import net.sf.jsqlparser.util.validation.ValidationCapability; + +/** + * @author gitmotte + */ +public class MergeValidator extends AbstractValidator + implements MergeOperationVisitor { + + + @Override + public void validate(Merge merge) { + for (ValidationCapability c : getCapabilities()) { + validateFeature(c, Feature.merge); + } + validateOptionalExpression(merge.getOnCondition()); + if (merge.getOperations() != null) { + merge.getOperations().forEach(operation -> operation.accept(this, null)); + } + validateOptionalFromItems(merge.getFromItem()); + } + + @Override + public Void visit(MergeDelete mergeDelete, S context) { + validateOptionalExpression(mergeDelete.getAndPredicate()); + return null; + } + + public void visit(MergeDelete mergeDelete) { + visit(mergeDelete, null); + } + + @Override + public Void visit(MergeUpdate mergeUpdate, S context) { + validateOptionalExpression(mergeUpdate.getAndPredicate()); + for (UpdateSet updateSet : mergeUpdate.getUpdateSets()) { + validateOptionalExpressions(updateSet.getColumns()); + validateOptionalExpressions(updateSet.getValues()); + } + validateOptionalExpression(mergeUpdate.getDeleteWhereCondition()); + validateOptionalExpression(mergeUpdate.getWhereCondition()); + return null; + } + + public void visit(MergeUpdate mergeUpdate) { + visit(mergeUpdate, null); + } + + @Override + public Void visit(MergeInsert mergeInsert, S context) { + validateOptionalExpression(mergeInsert.getAndPredicate()); + validateOptionalExpressions(mergeInsert.getColumns()); + validateOptionalExpressions(mergeInsert.getValues()); + + return null; + } + + public void visit(MergeInsert mergeInsert) { + visit(mergeInsert, null); + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/OrderByValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/OrderByValidator.java new file mode 100644 index 0000000..858702d --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/OrderByValidator.java @@ -0,0 +1,42 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.select.OrderByElement; +import net.sf.jsqlparser.statement.select.OrderByVisitor; +import net.sf.jsqlparser.util.validation.ValidationCapability; + +/** + * @author gitmotte + */ +public class OrderByValidator extends AbstractValidator + implements OrderByVisitor { + + @Override + public void validate(OrderByElement element) { + element.accept(this, null); + } + + @Override + public Void visit(OrderByElement orderBy, S context) { + for (ValidationCapability c : getCapabilities()) { + validateFeature(c, Feature.orderBy); + validateOptionalFeature(c, orderBy.getNullOrdering(), Feature.orderByNullOrdering); + } + getValidator(ExpressionValidator.class).validate(orderBy.getExpression()); + return null; + } + + public void visit(OrderByElement orderBy) { + visit(orderBy, null); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/RefreshMaterializedViewStatementValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/RefreshMaterializedViewStatementValidator.java new file mode 100644 index 0000000..cca8785 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/RefreshMaterializedViewStatementValidator.java @@ -0,0 +1,39 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement; +import net.sf.jsqlparser.util.validation.ValidationCapability; +import net.sf.jsqlparser.util.validation.metadata.NamedObject; + +/** + * @author jxnu-liguobin + */ + +public class RefreshMaterializedViewStatementValidator + extends AbstractValidator { + + @Override + public void validate(RefreshMaterializedViewStatement viewStatement) { + validateFeatureAndName(Feature.refreshMaterializedView, NamedObject.table, + viewStatement.getView().getName()); + for (ValidationCapability c : getCapabilities()) { + // default + validateFeature(c, viewStatement.getRefreshMode() == null, + Feature.refreshMaterializedView); + // specify WITH DATA + validateOptionalFeature(c, viewStatement.getRefreshMode(), + Feature.refreshMaterializedWithDataView); + validateOptionalFeature(c, viewStatement.getRefreshMode(), + Feature.refreshMaterializedWithNoDataView); + } + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/ReplaceValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/ReplaceValidator.java new file mode 100644 index 0000000..0c13233 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/ReplaceValidator.java @@ -0,0 +1,18 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +@Deprecated +/** + * @author gitmotte + */ +public class ReplaceValidator extends UpsertValidator { + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/ResetStatementValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/ResetStatementValidator.java new file mode 100644 index 0000000..3a4efb9 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/ResetStatementValidator.java @@ -0,0 +1,24 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2021 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.ResetStatement; +import net.sf.jsqlparser.util.validation.ValidationCapability; + +public class ResetStatementValidator extends AbstractValidator { + + @Override + public void validate(ResetStatement reset) { + for (ValidationCapability c : getCapabilities()) { + validateFeature(c, Feature.reset); + } + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/SelectValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/SelectValidator.java new file mode 100644 index 0000000..4ed464d --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/SelectValidator.java @@ -0,0 +1,419 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import java.util.List; + +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.MySQLIndexHint; +import net.sf.jsqlparser.expression.SQLServerHints; +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.select.ExceptOp; +import net.sf.jsqlparser.statement.select.Fetch; +import net.sf.jsqlparser.statement.select.ForMode; +import net.sf.jsqlparser.statement.select.FromItemVisitor; +import net.sf.jsqlparser.statement.select.IntersectOp; +import net.sf.jsqlparser.statement.select.Join; +import net.sf.jsqlparser.statement.select.LateralSubSelect; +import net.sf.jsqlparser.statement.select.MinusOp; +import net.sf.jsqlparser.statement.select.Offset; +import net.sf.jsqlparser.statement.select.ParenthesedFromItem; +import net.sf.jsqlparser.statement.select.ParenthesedSelect; +import net.sf.jsqlparser.statement.select.Pivot; +import net.sf.jsqlparser.statement.select.PivotVisitor; +import net.sf.jsqlparser.statement.select.PivotXml; +import net.sf.jsqlparser.statement.select.PlainSelect; +import net.sf.jsqlparser.statement.select.SelectItem; +import net.sf.jsqlparser.statement.select.SelectItemVisitor; +import net.sf.jsqlparser.statement.select.SelectVisitor; +import net.sf.jsqlparser.statement.select.SetOperationList; +import net.sf.jsqlparser.statement.select.TableFunction; +import net.sf.jsqlparser.statement.select.TableStatement; +import net.sf.jsqlparser.statement.select.UnPivot; +import net.sf.jsqlparser.statement.select.UnionOp; +import net.sf.jsqlparser.statement.select.Values; +import net.sf.jsqlparser.statement.select.WithItem; +import net.sf.jsqlparser.util.validation.ValidationCapability; +import net.sf.jsqlparser.util.validation.ValidationUtil; +import net.sf.jsqlparser.util.validation.metadata.NamedObject; + +/** + * @author gitmotte + */ +public class SelectValidator extends AbstractValidator> + implements SelectVisitor, SelectItemVisitor, FromItemVisitor, + PivotVisitor { + + @SuppressWarnings({"PMD.CyclomaticComplexity"}) + @Override + public Void visit(PlainSelect plainSelect, S context) { + if (isNotEmpty(plainSelect.getWithItemsList())) { + plainSelect.getWithItemsList() + .forEach(withItem -> withItem.accept((SelectVisitor) this, context)); + } + + for (ValidationCapability c : getCapabilities()) { + validateFeature(c, Feature.select); + validateFeature(c, plainSelect.getMySqlHintStraightJoin(), + Feature.mySqlHintStraightJoin); + validateOptionalFeature(c, plainSelect.getOracleHint(), Feature.oracleHint); + validateOptionalFeature(c, plainSelect.getSkip(), Feature.skip); + validateOptionalFeature(c, plainSelect.getFirst(), Feature.first); + + if (plainSelect.getDistinct() != null) { + if (plainSelect.getDistinct().isUseUnique()) { + validateFeature(c, Feature.selectUnique); + } else { + validateFeature(c, Feature.distinct); + } + validateOptionalFeature(c, plainSelect.getDistinct().getOnSelectItems(), + Feature.distinctOn); + } + + validateOptionalFeature(c, plainSelect.getTop(), Feature.top); + validateFeature(c, plainSelect.getMySqlSqlCacheFlag() != null, + Feature.mysqlSqlCacheFlag); + validateFeature(c, plainSelect.getMySqlSqlCalcFoundRows(), Feature.mysqlCalcFoundRows); + validateOptionalFeature(c, plainSelect.getIntoTables(), Feature.selectInto); + validateOptionalFeature(c, plainSelect.getKsqlWindow(), Feature.kSqlWindow); + validateFeature(c, + isNotEmpty(plainSelect.getOrderByElements()) && plainSelect.isOracleSiblings(), + Feature.oracleOrderBySiblings); + + if (plainSelect.getForMode() != null) { + validateFeature(c, Feature.selectForUpdate); + validateFeature(c, plainSelect.getForMode() == ForMode.KEY_SHARE, + Feature.selectForKeyShare); + validateFeature(c, plainSelect.getForMode() == ForMode.NO_KEY_UPDATE, + Feature.selectForNoKeyUpdate); + validateFeature(c, plainSelect.getForMode() == ForMode.SHARE, + Feature.selectForShare); + + validateOptionalFeature(c, plainSelect.getForUpdateTable(), + Feature.selectForUpdateOfTable); + validateOptionalFeature(c, plainSelect.getWait(), Feature.selectForUpdateWait); + validateFeature(c, plainSelect.isNoWait(), Feature.selectForUpdateNoWait); + validateFeature(c, plainSelect.isSkipLocked(), Feature.selectForUpdateSkipLocked); + } + + validateOptionalFeature(c, plainSelect.getForXmlPath(), Feature.selectForXmlPath); + validateOptionalFeature(c, plainSelect.getOptimizeFor(), Feature.optimizeFor); + } // end for + + validateOptionalFromItem(plainSelect.getFromItem()); + validateOptionalFromItems(plainSelect.getIntoTables()); + validateOptionalJoins(plainSelect.getJoins()); + + // to correctly recognize aliased tables + // @todo: fix this properly, I don't understand functional syntax + // validateOptionalList(plainSelect.getSelectItems(), () -> this, SelectItem::accept, + // context); + + validateOptionalExpression(plainSelect.getWhere()); + validateOptionalExpression(plainSelect.getOracleHierarchical()); + + if (plainSelect.getGroupBy() != null) { + plainSelect.getGroupBy().accept(getValidator(GroupByValidator.class), context); + } + + validateOptionalExpression(plainSelect.getHaving()); + validateOptionalOrderByElements(plainSelect.getOrderByElements()); + + if (plainSelect.getLimit() != null) { + getValidator(LimitValidator.class).validate(plainSelect.getLimit()); + } + + if (plainSelect.getOffset() != null) { + validateOffset(plainSelect.getOffset()); + } + + if (plainSelect.getFetch() != null) { + validateFetch(plainSelect.getFetch()); + } + + return null; + } + + @Override + public Void visit(SelectItem selectExpressionItem, S context) { + selectExpressionItem.getExpression().accept(getValidator(ExpressionValidator.class), + context); + return null; + } + + @Override + public Void visit(ParenthesedSelect selectBody, S context) { + if (isNotEmpty(selectBody.getWithItemsList())) { + selectBody.getWithItemsList() + .forEach(withItem -> withItem.accept((SelectVisitor) this, context)); + } + selectBody.getSelect().accept(this, context); + validateOptional(selectBody.getPivot(), p -> p.accept(this, context)); + return null; + } + + @Override + public Void visit(Table table, S context) { + validateNameWithAlias(NamedObject.table, table.getFullyQualifiedName(), + ValidationUtil.getAlias(table.getAlias())); + + validateOptional(table.getPivot(), p -> p.accept(this, context)); + validateOptional(table.getUnPivot(), up -> up.accept(this, context)); + + MySQLIndexHint indexHint = table.getIndexHint(); + if (indexHint != null && isNotEmpty(indexHint.getIndexNames())) { + indexHint.getIndexNames().forEach(i -> validateName(NamedObject.index, i)); + } + SQLServerHints sqlServerHints = table.getSqlServerHints(); + if (sqlServerHints != null) { + validateName(NamedObject.index, sqlServerHints.getIndexName()); + } + return null; + } + + @Override + public Void visit(Pivot pivot, S context) { + validateFeature(Feature.pivot); + validateOptionalExpressions(pivot.getForColumns()); + return null; + } + + @Override + public Void visit(UnPivot unpivot, S context) { + validateFeature(Feature.unpivot); + + validateOptionalExpressions(unpivot.getUnPivotForClause()); + validateOptionalExpressions(unpivot.getUnPivotClause()); + return null; + } + + @Override + public Void visit(PivotXml pivot, S context) { + validateFeature(Feature.pivotXml); + validateOptionalExpressions(pivot.getForColumns()); + if (isNotEmpty(pivot.getFunctionItems())) { + ExpressionValidator v = getValidator(ExpressionValidator.class); + pivot.getFunctionItems().forEach(f -> f.getExpression().accept(v, context)); + } + if (pivot.getInSelect() != null) { + pivot.getInSelect().accept(this, context); + } + return null; + } + + public void validateOffset(Offset offset) { + for (ValidationCapability c : getCapabilities()) { + validateFeature(c, Feature.offset); + validateOptionalFeature(c, offset.getOffsetParam(), Feature.offsetParam); + } + } + + public void validateFetch(Fetch fetch) { + for (ValidationCapability c : getCapabilities()) { + validateFeature(c, Feature.fetch); + validateFeature(c, fetch.isFetchParamFirst(), Feature.fetchFirst); + validateFeature(c, !fetch.isFetchParamFirst(), Feature.fetchNext); + } + + validateOptionalExpression(fetch.getFetchJdbcParameter()); + } + + public void validateOptionalJoins(List joins) { + if (joins != null) { + for (Join join : joins) { + validateOptionalJoin(join); + } + } + } + + public void validateOptionalJoin(Join join) { + for (ValidationCapability c : getCapabilities()) { + validateFeature(c, Feature.join); + validateFeature(c, join.isSimple() && join.isOuter(), Feature.joinOuterSimple); + validateFeature(c, join.isSimple(), Feature.joinSimple); + validateFeature(c, join.isRight(), Feature.joinRight); + validateFeature(c, join.isNatural(), Feature.joinNatural); + validateFeature(c, join.isFull(), Feature.joinFull); + validateFeature(c, join.isLeft(), Feature.joinLeft); + validateFeature(c, join.isCross(), Feature.joinCross); + validateFeature(c, join.isOuter(), Feature.joinOuter); + validateFeature(c, join.isInner(), Feature.joinInner); + validateFeature(c, join.isSemi(), Feature.joinSemi); + validateFeature(c, join.isStraight(), Feature.joinStraight); + validateFeature(c, join.isApply(), Feature.joinApply); + validateFeature(c, join.isWindowJoin(), Feature.joinWindow); + validateOptionalFeature(c, join.getUsingColumns(), Feature.joinUsingColumns); + } + + validateOptionalFromItem(join.getFromItem()); + for (Expression onExpression : join.getOnExpressions()) { + validateOptionalExpression(onExpression); + } + validateOptionalExpressions(join.getUsingColumns()); + } + + @Override + public Void visit(SetOperationList setOperation, S context) { + if (isNotEmpty(setOperation.getWithItemsList())) { + setOperation.getWithItemsList() + .forEach(withItem -> withItem.accept((SelectVisitor) this, context)); + } + for (ValidationCapability c : getCapabilities()) { + validateFeature(c, Feature.setOperation); + validateFeature(c, + setOperation.getOperations().stream().anyMatch(o -> o instanceof UnionOp), + Feature.setOperationUnion); + validateFeature(c, + setOperation.getOperations().stream().anyMatch(o -> o instanceof IntersectOp), + Feature.setOperationIntersect); + validateFeature(c, + setOperation.getOperations().stream().anyMatch(o -> o instanceof ExceptOp), + Feature.setOperationExcept); + validateFeature(c, + setOperation.getOperations().stream().anyMatch(o -> o instanceof MinusOp), + Feature.setOperationMinus); + } + + if (isNotEmpty(setOperation.getSelects())) { + setOperation.getSelects().forEach(s -> s.accept(this, context)); + } + + validateOptionalOrderByElements(setOperation.getOrderByElements()); + + if (setOperation.getLimit() != null) { + getValidator(LimitValidator.class).validate(setOperation.getLimit()); + } + + if (setOperation.getOffset() != null) { + validateOffset(setOperation.getOffset()); + } + + if (setOperation.getFetch() != null) { + validateFetch(setOperation.getFetch()); + } + return null; + } + + @Override + public Void visit(WithItem withItem, S context) { + for (ValidationCapability c : getCapabilities()) { + validateFeature(c, Feature.withItem); + validateFeature(c, withItem.isRecursive(), Feature.withItemRecursive); + } + if (isNotEmpty(withItem.getWithItemList())) { + withItem.getWithItemList().forEach(wi -> wi.accept(this, context)); + } + withItem.getSelect().accept(this, context); + return null; + } + + @Override + public Void visit(LateralSubSelect lateralSubSelect, S context) { + if (isNotEmpty(lateralSubSelect.getWithItemsList())) { + lateralSubSelect.getWithItemsList() + .forEach(withItem -> withItem.accept((SelectVisitor) this, context)); + } + + validateFeature(Feature.lateralSubSelect); + validateOptional(lateralSubSelect.getPivot(), p -> p.accept(this, context)); + validateOptional(lateralSubSelect.getUnPivot(), up -> up.accept(this, context)); + validateOptional(lateralSubSelect.getSelect(), e -> e.accept(this, context)); + return null; + } + + @Override + public Void visit(TableStatement tableStatement, S context) { + getValidator(TableStatementValidator.class).validate(tableStatement); + return null; + } + + @Override + public Void visit(TableFunction tableFunction, S context) { + validateFeature(Feature.tableFunction); + + validateOptional(tableFunction.getPivot(), p -> p.accept(this, context)); + validateOptional(tableFunction.getUnPivot(), up -> up.accept(this, context)); + return null; + } + + @Override + public Void visit(ParenthesedFromItem parenthesis, S context) { + validateOptional(parenthesis.getFromItem(), e -> e.accept(this, context)); + return null; + } + + @Override + public Void visit(Values values, S context) { + getValidator(ValuesStatementValidator.class).validate(values); + return null; + } + + @Override + public void validate(SelectItem statement) { + statement.accept(this, null); + } + + public void visit(PlainSelect plainSelect) { + visit(plainSelect, null); + } + + public void visit(SelectItem selectExpressionItem) { + visit(selectExpressionItem, null); + } + + public void visit(ParenthesedSelect selectBody) { + visit(selectBody, null); + } + + public void visit(Table table) { + visit(table, null); + } + + public void visit(Pivot pivot) { + visit(pivot, null); + } + + public void visit(UnPivot unpivot) { + visit(unpivot, null); + } + + public void visit(PivotXml pivot) { + visit(pivot, null); + } + + public void visit(SetOperationList setOperation) { + visit(setOperation, null); + } + + public void visit(WithItem withItem) { + visit(withItem, null); + } + + public void visit(LateralSubSelect lateralSubSelect) { + visit(lateralSubSelect, null); + } + + public void visit(TableStatement tableStatement) { + visit(tableStatement, null); + } + + public void visit(TableFunction tableFunction) { + visit(tableFunction, null); + } + + public void visit(ParenthesedFromItem parenthesis) { + visit(parenthesis, null); + } + + public void visit(Values values) { + visit(values, null); + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/SetStatementValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/SetStatementValidator.java new file mode 100644 index 0000000..9e66b47 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/SetStatementValidator.java @@ -0,0 +1,32 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.SetStatement; +import net.sf.jsqlparser.util.validation.ValidationCapability; + +/** + * @author gitmotte + */ +public class SetStatementValidator extends AbstractValidator { + + + @Override + public void validate(SetStatement set) { + for (ValidationCapability c : getCapabilities()) { + validateFeature(c, Feature.set); + } + for (int i = 0; i < set.getCount(); i++) { + validateOptionalExpressions(set.getExpressions(i)); + } + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/ShowColumnsStatementValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/ShowColumnsStatementValidator.java new file mode 100644 index 0000000..e917d9e --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/ShowColumnsStatementValidator.java @@ -0,0 +1,25 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.ShowColumnsStatement; +import net.sf.jsqlparser.util.validation.metadata.NamedObject; + +/** + * @author gitmotte + */ +public class ShowColumnsStatementValidator extends AbstractValidator { + + @Override + public void validate(ShowColumnsStatement show) { + validateFeatureAndName(Feature.showColumns, NamedObject.table, show.getTableName()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/ShowIndexStatementValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/ShowIndexStatementValidator.java new file mode 100644 index 0000000..8a35faf --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/ShowIndexStatementValidator.java @@ -0,0 +1,26 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.show.ShowIndexStatement; +import net.sf.jsqlparser.util.validation.metadata.NamedObject; + +/** + * @author Jayant Kumar Yadav + */ + +public class ShowIndexStatementValidator extends AbstractValidator { + + @Override + public void validate(ShowIndexStatement show) { + validateFeatureAndName(Feature.showIndex, NamedObject.table, show.getTableName()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/ShowStatementValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/ShowStatementValidator.java new file mode 100644 index 0000000..d328e67 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/ShowStatementValidator.java @@ -0,0 +1,24 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.ShowStatement; + +/** + * @author gitmotte + */ +public class ShowStatementValidator extends AbstractValidator { + + @Override + public void validate(ShowStatement show) { + validateFeature(Feature.show); + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/ShowTablesStatementValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/ShowTablesStatementValidator.java new file mode 100644 index 0000000..0501f0c --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/ShowTablesStatementValidator.java @@ -0,0 +1,25 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.show.ShowTablesStatement; +import net.sf.jsqlparser.util.validation.metadata.NamedObject; + +/** + * @author gitmotte + */ +public class ShowTablesStatementValidator extends AbstractValidator { + + @Override + public void validate(ShowTablesStatement show) { + validateFeatureAndName(Feature.showTables, NamedObject.database, show.getDbName()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/StatementValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/StatementValidator.java new file mode 100644 index 0000000..44e2f1f --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/StatementValidator.java @@ -0,0 +1,539 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.Block; +import net.sf.jsqlparser.statement.Commit; +import net.sf.jsqlparser.statement.CreateFunctionalStatement; +import net.sf.jsqlparser.statement.DeclareStatement; +import net.sf.jsqlparser.statement.DescribeStatement; +import net.sf.jsqlparser.statement.ExplainStatement; +import net.sf.jsqlparser.statement.IfElseStatement; +import net.sf.jsqlparser.statement.PurgeStatement; +import net.sf.jsqlparser.statement.ResetStatement; +import net.sf.jsqlparser.statement.RollbackStatement; +import net.sf.jsqlparser.statement.SavepointStatement; +import net.sf.jsqlparser.statement.SetStatement; +import net.sf.jsqlparser.statement.ShowColumnsStatement; +import net.sf.jsqlparser.statement.ShowStatement; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.StatementVisitor; +import net.sf.jsqlparser.statement.Statements; +import net.sf.jsqlparser.statement.UnsupportedStatement; +import net.sf.jsqlparser.statement.UseStatement; +import net.sf.jsqlparser.statement.alter.Alter; +import net.sf.jsqlparser.statement.alter.AlterSession; +import net.sf.jsqlparser.statement.alter.AlterSystemStatement; +import net.sf.jsqlparser.statement.alter.RenameTableStatement; +import net.sf.jsqlparser.statement.alter.sequence.AlterSequence; +import net.sf.jsqlparser.statement.analyze.Analyze; +import net.sf.jsqlparser.statement.comment.Comment; +import net.sf.jsqlparser.statement.create.function.CreateFunction; +import net.sf.jsqlparser.statement.create.index.CreateIndex; +import net.sf.jsqlparser.statement.create.procedure.CreateProcedure; +import net.sf.jsqlparser.statement.create.schema.CreateSchema; +import net.sf.jsqlparser.statement.create.sequence.CreateSequence; +import net.sf.jsqlparser.statement.create.synonym.CreateSynonym; +import net.sf.jsqlparser.statement.create.table.CreateTable; +import net.sf.jsqlparser.statement.create.view.AlterView; +import net.sf.jsqlparser.statement.create.view.CreateView; +import net.sf.jsqlparser.statement.delete.Delete; +import net.sf.jsqlparser.statement.drop.Drop; +import net.sf.jsqlparser.statement.execute.Execute; +import net.sf.jsqlparser.statement.grant.Grant; +import net.sf.jsqlparser.statement.insert.Insert; +import net.sf.jsqlparser.statement.merge.Merge; +import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement; +import net.sf.jsqlparser.statement.select.Select; +import net.sf.jsqlparser.statement.show.ShowIndexStatement; +import net.sf.jsqlparser.statement.show.ShowTablesStatement; +import net.sf.jsqlparser.statement.truncate.Truncate; +import net.sf.jsqlparser.statement.update.Update; +import net.sf.jsqlparser.statement.upsert.Upsert; +import net.sf.jsqlparser.util.validation.ValidationCapability; +import net.sf.jsqlparser.util.validation.metadata.NamedObject; + +/** + * @author gitmotte + */ +public class StatementValidator extends AbstractValidator + implements StatementVisitor { + + @Override + public Void visit(CreateIndex createIndex, S context) { + getValidator(CreateIndexValidator.class).validate(createIndex); + return null; + } + + @Override + public Void visit(CreateTable createTable, S context) { + getValidator(CreateTableValidator.class).validate(createTable); + return null; + } + + @Override + public Void visit(CreateView createView, S context) { + getValidator(CreateViewValidator.class).validate(createView); + return null; + } + + @Override + public Void visit(AlterView alterView, S context) { + getValidator(AlterViewValidator.class).validate(alterView); + return null; + } + + @Override + public Void visit(RefreshMaterializedViewStatement materializedView, S context) { + getValidator(RefreshMaterializedViewStatementValidator.class).validate(materializedView); + return null; + } + + @Override + public Void visit(Delete delete, S context) { + getValidator(DeleteValidator.class).validate(delete); + return null; + } + + @Override + public Void visit(Drop drop, S context) { + getValidator(DropValidator.class).validate(drop); + return null; + } + + @Override + public Void visit(Insert insert, S context) { + getValidator(InsertValidator.class).validate(insert); + return null; + } + + @Override + public Void visit(Select select, S context) { + validateFeature(Feature.select); + + SelectValidator selectValidator = getValidator(SelectValidator.class); + select.accept(selectValidator, null); + return null; + } + + @Override + public Void visit(Truncate truncate, S context) { + validateFeature(Feature.truncate); + validateOptionalFromItem(truncate.getTable()); + return null; + } + + @Override + public Void visit(Update update, S context) { + getValidator(UpdateValidator.class).validate(update); + return null; + } + + @Override + public Void visit(Alter alter, S context) { + getValidator(AlterValidator.class).validate(alter); + return null; + } + + @Override + public Void visit(Statements statements, S context) { + statements.forEach(s -> s.accept(this, context)); + return null; + } + + @Override + public Void visit(Execute execute, S context) { + getValidator(ExecuteValidator.class).validate(execute); + return null; + } + + @Override + public Void visit(SetStatement set, S context) { + getValidator(SetStatementValidator.class).validate(set); + return null; + } + + @Override + public Void visit(ResetStatement reset, S context) { + getValidator(ResetStatementValidator.class).validate(reset); + return null; + } + + @Override + public Void visit(Merge merge, S context) { + getValidator(MergeValidator.class).validate(merge); + return null; + } + + @Override + public Void visit(Commit commit, S context) { + validateFeature(Feature.commit); + return null; + } + + @Override + public Void visit(Upsert upsert, S context) { + getValidator(UpsertValidator.class).validate(upsert); + return null; + } + + @Override + public Void visit(UseStatement use, S context) { + getValidator(UseStatementValidator.class).validate(use); + return null; + } + + @Override + public Void visit(ShowStatement showStatement, S context) { + getValidator(ShowStatementValidator.class).validate(showStatement); + return null; + } + + @Override + public Void visit(ShowColumnsStatement show, S context) { + getValidator(ShowColumnsStatementValidator.class).validate(show); + return null; + } + + @Override + public Void visit(ShowIndexStatement show, S context) { + getValidator(ShowIndexStatementValidator.class).validate(show); + return null; + } + + @Override + public Void visit(ShowTablesStatement showTables, S context) { + getValidator(ShowTablesStatementValidator.class).validate(showTables); + return null; + } + + @Override + public Void visit(Block block, S context) { + validateFeature(Feature.block); + block.getStatements().accept(this, context); + return null; + } + + @Override + public Void visit(Comment comment, S context) { + for (ValidationCapability c : getCapabilities()) { + validateFeature(c, Feature.comment); + validateOptionalFeature(c, comment.getTable(), Feature.commentOnTable); + validateOptionalFeature(c, comment.getColumn(), Feature.commentOnColumn); + validateOptionalFeature(c, comment.getView(), Feature.commentOnView); + } + return null; + } + + @Override + public Void visit(DescribeStatement describe, S context) { + validateFeature(Feature.describe); + validateFeature(Feature.desc); + validateOptionalFromItem(describe.getTable()); + return null; + } + + @Override + public Void visit(ExplainStatement explainStatement, S context) { + validateFeature(Feature.explain); + if (explainStatement.getStatement() != null) { + explainStatement.getStatement().accept(this, context); + } + return null; + } + + + @Override + public Void visit(DeclareStatement declareStatement, S context) { + getValidator(DeclareStatementValidator.class).validate(declareStatement); + return null; + } + + @Override + public Void visit(Grant grant, S context) { + getValidator(GrantValidator.class).validate(grant); + return null; + } + + @Override + public Void visit(CreateSchema aThis, S context) { + validateFeatureAndName(Feature.createSchema, NamedObject.schema, aThis.getSchemaName()); + aThis.getStatements().forEach(s -> s.accept(this, context)); + return null; + } + + @Override + public Void visit(CreateSequence createSequence, S context) { + getValidator(CreateSequenceValidator.class).validate(createSequence); + return null; + } + + @Override + public Void visit(AlterSequence alterSequence, S context) { + getValidator(AlterSequenceValidator.class).validate(alterSequence); + return null; + } + + @Override + public Void visit(CreateFunctionalStatement createFunctionalStatement, S context) { + validateFeature(Feature.functionalStatement); + if (createFunctionalStatement instanceof CreateFunction) { + validateFeature(Feature.createFunction); + } else if (createFunctionalStatement instanceof CreateProcedure) { + validateFeature(Feature.createProcedure); + } + return null; + } + + @Override + public void validate(Statement statement) { + statement.accept(this, null); + } + + @Override + public Void visit(CreateSynonym createSynonym, S context) { + getValidator(CreateSynonymValidator.class).validate(createSynonym); + return null; + } + + @Override + public Void visit(Analyze analyze, S context) { + getValidator(AnalyzeValidator.class).validate(analyze); + return null; + } + + @Override + public Void visit(SavepointStatement savepointStatement, S context) { + // TODO: not yet implemented + return null; + } + + @Override + public Void visit(RollbackStatement rollbackStatement, S context) { + // TODO: not yet implemented + return null; + } + + @Override + public Void visit(AlterSession alterSession, S context) { + // TODO: not yet implemented + return null; + } + + @Override + public Void visit(IfElseStatement ifElseStatement, S context) { + ifElseStatement.getIfStatement().accept(this, context); + if (ifElseStatement.getElseStatement() != null) { + ifElseStatement.getElseStatement().accept(this, context); + } + return null; + } + + public Void visit(RenameTableStatement renameTableStatement, S context) { + // TODO: not yet implemented + return null; + } + + @Override + public Void visit(PurgeStatement purgeStatement, S context) { + // TODO: not yet implemented + return null; + } + + @Override + public Void visit(AlterSystemStatement alterSystemStatement, S context) { + // TODO: not yet implemented + return null; + } + + @Override + public Void visit(UnsupportedStatement unsupportedStatement, S context) { + + return null; + } + + public void visit(CreateIndex createIndex) { + visit(createIndex, null); + } + + public void visit(CreateTable createTable) { + visit(createTable, null); + } + + public void visit(CreateView createView) { + visit(createView, null); + } + + public void visit(AlterView alterView) { + visit(alterView, null); + } + + public void visit(RefreshMaterializedViewStatement materializedView) { + visit(materializedView, null); + } + + public void visit(Delete delete) { + visit(delete, null); + } + + public void visit(Drop drop) { + visit(drop, null); + } + + public void visit(Insert insert) { + visit(insert, null); + } + + public void visit(Select select) { + visit(select, null); + } + + public void visit(Truncate truncate) { + visit(truncate, null); + } + + public void visit(Update update) { + visit(update, null); + } + + public void visit(Alter alter) { + visit(alter, null); + } + + public void visit(Statements statements) { + visit(statements, null); + } + + public void visit(Execute execute) { + visit(execute, null); + } + + public void visit(SetStatement set) { + visit(set, null); + } + + public void visit(ResetStatement reset) { + visit(reset, null); + } + + public void visit(Merge merge) { + visit(merge, null); + } + + public void visit(Commit commit) { + visit(commit, null); + } + + public void visit(Upsert upsert) { + visit(upsert, null); + } + + public void visit(UseStatement use) { + visit(use, null); + } + + public void visit(ShowStatement showStatement) { + visit(showStatement, null); + } + + public void visit(ShowColumnsStatement show) { + visit(show, null); + } + + public void visit(ShowIndexStatement show) { + visit(show, null); + } + + public void visit(ShowTablesStatement showTables) { + visit(showTables, null); + } + + public void visit(Block block) { + visit(block, null); + } + + public void visit(Comment comment) { + visit(comment, null); + } + + public void visit(DescribeStatement describe) { + visit(describe, null); + } + + public void visit(ExplainStatement explainStatement) { + visit(explainStatement, null); + } + + public void visit(DeclareStatement declareStatement) { + visit(declareStatement, null); + } + + public void visit(Grant grant) { + visit(grant, null); + } + + public void visit(CreateSchema aThis) { + visit(aThis, null); + } + + public void visit(CreateSequence createSequence) { + visit(createSequence, null); + } + + public void visit(AlterSequence alterSequence) { + visit(alterSequence, null); + } + + public void visit(CreateFunctionalStatement createFunctionalStatement) { + visit(createFunctionalStatement, null); + } + + public void visit(CreateSynonym createSynonym) { + visit(createSynonym, null); + } + + public void visit(Analyze analyze) { + visit(analyze, null); + } + + public void visit(SavepointStatement savepointStatement) { + visit(savepointStatement, null); + } + + public void visit(RollbackStatement rollbackStatement) { + visit(rollbackStatement, null); + } + + public void visit(AlterSession alterSession) { + visit(alterSession, null); + } + + public void visit(IfElseStatement ifElseStatement) { + visit(ifElseStatement, null); + } + + public void visit(RenameTableStatement renameTableStatement) { + visit(renameTableStatement, null); + } + + public void visit(PurgeStatement purgeStatement) { + visit(purgeStatement, null); + } + + public void visit(AlterSystemStatement alterSystemStatement) { + visit(alterSystemStatement, null); + } + + public void visit(UnsupportedStatement unsupportedStatement) { + visit(unsupportedStatement, null); + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/TableStatementValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/TableStatementValidator.java new file mode 100644 index 0000000..4b95412 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/TableStatementValidator.java @@ -0,0 +1,27 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.select.TableStatement; +import net.sf.jsqlparser.util.validation.ValidationCapability; + +/** + * @author jxnu-liguobin + */ +public class TableStatementValidator extends AbstractValidator { + + @Override + public void validate(TableStatement statement) { + for (ValidationCapability c : getCapabilities()) { + validateFeature(c, Feature.tableStatement); + } + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/UpdateValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/UpdateValidator.java new file mode 100644 index 0000000..aabfaf3 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/UpdateValidator.java @@ -0,0 +1,68 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.update.Update; +import net.sf.jsqlparser.util.validation.ValidationCapability; + +/** + * @author gitmotte + */ +public class UpdateValidator extends AbstractValidator { + + @Override + public void validate(Update update) { + + for (ValidationCapability c : getCapabilities()) { + validateFeature(c, Feature.update); + validateOptionalFeature(c, update.getFromItem(), Feature.updateFrom); + validateOptionalFeature(c, update.getStartJoins(), Feature.updateJoins); + validateFeature(c, update.isUseSelect(), Feature.updateUseSelect); + validateOptionalFeature(c, update.getOrderByElements(), Feature.updateOrderBy); + validateOptionalFeature(c, update.getLimit(), Feature.updateLimit); + validateOptionalFeature(c, update.getReturningClause(), + Feature.updateReturning); + } + + validateOptionalFromItem(update.getTable()); + + validateOptional(update.getStartJoins(), + j -> getValidator(SelectValidator.class).validateOptionalJoins(j)); + + if (update.isUseSelect()) { + validateOptionalExpressions(update.getColumns()); + validateOptional(update.getSelect(), + e -> e.accept(getValidator(SelectValidator.class), null)); + } else { + validateOptionalExpressions(update.getColumns()); + validateOptionalExpressions(update.getExpressions()); + } + + if (update.getFromItem() != null) { + validateOptionalFromItem(update.getFromItem()); + validateOptional(update.getJoins(), + j -> getValidator(SelectValidator.class).validateOptionalJoins(j)); + } + + validateOptionalExpression(update.getWhere()); + validateOptionalOrderByElements(update.getOrderByElements()); + + if (update.getLimit() != null) { + getValidator(LimitValidator.class).validate(update.getLimit()); + } + + if (update.getReturningClause() != null) { + SelectValidator v = getValidator(SelectValidator.class); + update.getReturningClause().forEach(c -> c.accept(v, null)); + } + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/UpsertValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/UpsertValidator.java new file mode 100644 index 0000000..0d0fb9b --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/UpsertValidator.java @@ -0,0 +1,51 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.select.Select; +import net.sf.jsqlparser.statement.update.UpdateSet; +import net.sf.jsqlparser.statement.upsert.Upsert; +import net.sf.jsqlparser.util.validation.ValidationCapability; + +/** + * @author gitmotte + */ +public class UpsertValidator extends AbstractValidator { + + @Override + public void validate(Upsert upsert) { + for (ValidationCapability c : getCapabilities()) { + validateFeature(c, Feature.upsert); + } + validateOptionalFromItem(upsert.getTable()); + validateOptionalExpressions(upsert.getColumns()); + validateOptionalExpressions(upsert.getExpressions()); + validateOptionalSelect(upsert.getSelect()); + validateDuplicate(upsert); + } + + private void validateOptionalSelect(Select select) { + if (select != null) { + SelectValidator v = getValidator(SelectValidator.class); + select.accept(v, null); + } + } + + private void validateDuplicate(Upsert upsert) { + if (upsert.getDuplicateUpdateSets() != null) { + for (UpdateSet updateSet : upsert.getDuplicateUpdateSets()) { + validateOptionalExpressions(updateSet.getColumns()); + validateOptionalExpressions(updateSet.getValues()); + } + } + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/UseStatementValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/UseStatementValidator.java new file mode 100644 index 0000000..7d7cd8f --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/UseStatementValidator.java @@ -0,0 +1,25 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.UseStatement; +import net.sf.jsqlparser.util.validation.metadata.NamedObject; + +/** + * @author gitmotte + */ +public class UseStatementValidator extends AbstractValidator { + + @Override + public void validate(UseStatement set) { + validateFeatureAndName(Feature.use, NamedObject.schema, set.getName()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/ValuesStatementValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/ValuesStatementValidator.java new file mode 100644 index 0000000..79643c8 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/ValuesStatementValidator.java @@ -0,0 +1,25 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.select.Values; + +/** + * @author gitmotte + */ +public class ValuesStatementValidator extends AbstractValidator { + + @Override + public void validate(Values values) { + validateFeature(Feature.values); + validateOptionalExpression(values.getExpressions()); + } +} diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt new file mode 100644 index 0000000..581999a --- /dev/null +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -0,0 +1,7861 @@ +/* + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2021 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ + +options { + IGNORE_CASE = true; + STATIC = false; + DEBUG_PARSER = false; + DEBUG_LOOKAHEAD = false; + DEBUG_TOKEN_MANAGER = false; + CACHE_TOKENS = false; +// FORCE_LA_CHECK = true; + UNICODE_INPUT = true; + JAVA_TEMPLATE_TYPE = "modern"; + JDK_VERSION = "1.8"; + TOKEN_EXTENDS = "BaseToken"; + COMMON_TOKEN_ACTION = true; + NODE_DEFAULT_VOID = true; + TRACK_TOKENS = true; + VISITOR = true; + GRAMMAR_ENCODING = "UTF-8"; + KEEP_LINE_COLUMN = true; +} + +PARSER_BEGIN(CCJSqlParser) + +package net.sf.jsqlparser.parser; + +import java.lang.reflect.Field; +import java.lang.Integer; + +import net.sf.jsqlparser.parser.feature.*; +import net.sf.jsqlparser.expression.*; +import net.sf.jsqlparser.expression.operators.arithmetic.*; +import net.sf.jsqlparser.expression.operators.conditional.*; +import net.sf.jsqlparser.expression.operators.relational.*; +import net.sf.jsqlparser.schema.*; +import net.sf.jsqlparser.statement.*; +import net.sf.jsqlparser.statement.analyze.*; +import net.sf.jsqlparser.statement.alter.*; +import net.sf.jsqlparser.statement.alter.sequence.*; +import net.sf.jsqlparser.statement.comment.*; +import net.sf.jsqlparser.statement.create.function.*; +import net.sf.jsqlparser.statement.create.index.*; +import net.sf.jsqlparser.statement.create.procedure.*; +import net.sf.jsqlparser.statement.create.schema.*; +import net.sf.jsqlparser.statement.create.synonym.*; +import net.sf.jsqlparser.statement.create.sequence.*; +import net.sf.jsqlparser.statement.create.table.*; +import net.sf.jsqlparser.statement.create.view.*; +import net.sf.jsqlparser.statement.delete.*; +import net.sf.jsqlparser.statement.drop.*; +import net.sf.jsqlparser.statement.insert.*; +import net.sf.jsqlparser.statement.execute.*; +import net.sf.jsqlparser.statement.select.*; +import net.sf.jsqlparser.statement.refresh.*; +import net.sf.jsqlparser.statement.show.*; +import net.sf.jsqlparser.statement.truncate.*; +import net.sf.jsqlparser.statement.update.*; +import net.sf.jsqlparser.statement.upsert.*; +import net.sf.jsqlparser.statement.merge.*; +import net.sf.jsqlparser.statement.grant.*; +import java.util.*; +import java.util.AbstractMap.SimpleEntry; + +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * The parser generated by JavaCC + */ +public class CCJSqlParser extends AbstractJSqlParser { + public final static Logger LOGGER = Logger.getLogger(CCJSqlParser.class.getName()); + public int bracketsCounter = 0; + public int caseCounter = 0; + public boolean interrupted = false; + + public CCJSqlParser withConfiguration(FeatureConfiguration configuration) { + token_source.configuration = configuration; + return this; + } + + public FeatureConfiguration getConfiguration() { + return token_source.configuration; + } + + public CCJSqlParser me () { + return this; + } + + private void linkAST(ASTNodeAccess access, SimpleNode node) { + access.setASTNode(node); + node.jjtSetValue(access); + } + + public Node getASTRoot() { + return jjtree.rootNode(); + } + + private static class ObjectNames { + + private final List names; + private final List delimiters; + + public ObjectNames(List names, List delimiters) { + this.names = names; + this.delimiters = delimiters; + } + + public List getNames() { + return names; + } + + public List getDelimiters() { + return delimiters; + } + } +} + +PARSER_END(CCJSqlParser) + +TOKEN_MGR_DECLS : { + public FeatureConfiguration configuration = new FeatureConfiguration(); + + public void CommonTokenAction(Token t) + { + t.absoluteBegin = getCurrentTokenAbsolutePosition(); + t.absoluteEnd = t.absoluteBegin + t.image.length(); + } + + public int getCurrentTokenAbsolutePosition() + { + if (input_stream instanceof SimpleCharStream) + return ((SimpleCharStream)input_stream).getAbsoluteTokenBegin(); + return -1; + } +} + +SKIP: +{ + +} + +// http://www.h2database.com/html/advanced.html#keywords + +TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ +{ + +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| /* H2 casewhen function */ +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| /* Salesforce SOQL */ +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| /* Salesforce SOQL */ +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| "> +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| + +| +| +| +| (" ")+ ) > +} + +TOKEN : /* Statement Separators */ +{ + +} + +TOKEN : /* Operators */ +{ + " ()* "="> +| )* "="> +| )* ">"> +| )* "="> +| )* "|"> +| +| "> +| +} + +TOKEN : /* Date/Time with time zones */ +{ + ()* ("(" ")")? ()* ( | ) (()+ )? ()+ "TIME" ()+ > +} + +TOKEN : /* Data Types */ +{ + | | | + | | | | | | + | | | | | + | | | | + | | | + ) > + + | <#TYPE_BIT: "BISTRING"> + | <#TYPE_BLOB: "BLOB" | "BYTEA" | | "VARBINARY" | > + | <#TYPE_BOOLEAN: "BOOLEAN" | "BOOL" > + | <#TYPE_ENUM: "ENUM" > + | <#TYPE_MAP: "MAP" > + | <#TYPE_DECIMAL: "DECIMAL" | "NUMBER" | "NUMERIC" > + | <#TYPE_TINYINT: "TINYINT" | "INT1" > + | <#TYPE_SMALLINT: "SMALLINT" | "INT2" | "SHORT" > + | <#TYPE_INTEGER: ( "INTEGER" | "INT" | "INT4" | | ) > + | <#TYPE_BIGINT: "BIGINT" | "INT8" | "LONG" > + | <#TYPE_HUGEINT: "HUGEINT" > + | <#TYPE_UTINYINT: "UTINYINT" > + | <#TYPE_USMALLINT: "USMALLINT" > + | <#TYPE_UINTEGER: "UINTEGER" > + | <#TYPE_UBIGINT: "UBIGINT" > + | <#TYPE_UHUGEINT: "UHUGEINT" > + | <#TYPE_REAL: "REAL" | "FLOAT4" | "FLOAT"> + | <#TYPE_DOUBLE: "DOUBLE" | "PRECISION" | "FLOAT8" | "FLOAT64"> + | <#TYPE_VARCHAR: "NVARCHAR" | "VARCHAR" | "NCHAR" | | "BPCHAR" | "STRING" | "TEXT" | | "VARYING"> + | <#TYPE_TIME: "TIMETZ" > + | <#TYPE_TIMESTAMP: "TIMESTAMP_NS" | "TIMESTAMP_MS" | "TIMESTAMP_S" > + + | <#TYPE_UUID: "UUID"> +} + +TOKEN : /* Numeric Constants */ +{ + < S_DOUBLE: (()? "." ( ["e","E"] (["+", "-"])? )? + | + "." (["e","E"] (["+", "-"])? )? + | + ["e","E"] (["+", "-"])? + )> + | < S_LONG: ( )+ > + | < #DIGIT: ["0" - "9"] > + | < S_HEX: ("X" ("'" ( )* "'" (" ")*)+ | "0x" ( )+ ) > + | < #HEX_VALUE: ["0"-"9","A"-"F", " "] > +} + +SPECIAL_TOKEN: +{ + < LINE_COMMENT: ("--" | "//") (~["\r","\n"])*> +| < MULTI_LINE_COMMENT: "/*" (~["*"])* "*" ("*" | (~["*","/"] (~["*"])* "*"))* "/"> +} + +TOKEN: +{ + +| + ()*> +| <#LETTER: + | | [ "$" , "#", "_" ] // Not SQL:2016 compliant! + > +| <#PART_LETTER: | | [ "$" , "#", "_" , "@" ] > + +// Unicode characters and categories are defined here: https://www.unicode.org/Public/UNIDATA/UnicodeData.txt +// SQL:2016 states: +// An is any character in the Unicode General Category classes “Lu”, “Ll”, “Lt”, “Lm”, “Lo”, or “Nl”. +// An is U+00B7, “Middle Dot”, or any character in the Unicode General Category classes “Mn”, “Mc”, “Nd”, “Pc”, or “Cf”. + +// unicode_identifier_start +| <#UnicodeIdentifierStart: ("\u00B7" | | | | | | |) > +| <#Ll: ["a"-"z","µ","ß"-"ö","ø"-"ÿ","ā","ă","ą","ć","ĉ","ċ","č","ď","đ","ē","ĕ","ė","ę","ě","ĝ","ğ","ġ","ģ","ĥ","ħ","ĩ","ī","ĭ","į","ı","ij","ĵ","ķ"-"ĸ","ĺ","ļ","ľ","ŀ","ł","ń","ņ","ň"-"ʼn","ŋ","ō","ŏ","ő","œ","ŕ","ŗ","ř","ś","ŝ","ş","š","ţ","ť","ŧ","ũ","ū","ŭ","ů","ű","ų","ŵ","ŷ","ź","ż","ž"-"ƀ","ƃ","ƅ","ƈ","ƌ"-"ƍ","ƒ","ƕ","ƙ"-"ƛ","ƞ","ơ","ƣ","ƥ","ƨ","ƪ"-"ƫ","ƭ","ư","ƴ","ƶ","ƹ"-"ƺ","ƽ"-"ƿ","dž","lj","nj","ǎ","ǐ","ǒ","ǔ","ǖ","ǘ","ǚ","ǜ"-"ǝ","ǟ","ǡ","ǣ","ǥ","ǧ","ǩ","ǫ","ǭ","ǯ"-"ǰ","dz","ǵ","ǹ","ǻ","ǽ","ǿ","ȁ","ȃ","ȅ","ȇ","ȉ","ȋ","ȍ","ȏ","ȑ","ȓ","ȕ","ȗ","ș","ț","ȝ","ȟ","ȡ","ȣ","ȥ","ȧ","ȩ","ȫ","ȭ","ȯ","ȱ","ȳ"-"ȹ","ȼ","ȿ"-"ɀ","ɂ","ɇ","ɉ","ɋ","ɍ","ɏ"-"ʓ","ʕ"-"ʯ","ͱ","ͳ","ͷ","ͻ"-"ͽ","ΐ","ά"-"ώ","ϐ"-"ϑ","ϕ"-"ϗ","ϙ","ϛ","ϝ","ϟ","ϡ","ϣ","ϥ","ϧ","ϩ","ϫ","ϭ","ϯ"-"ϳ","ϵ","ϸ","ϻ"-"ϼ","а"-"џ","ѡ","ѣ","ѥ","ѧ","ѩ","ѫ","ѭ","ѯ","ѱ","ѳ","ѵ","ѷ","ѹ","ѻ","ѽ","ѿ","ҁ","ҋ","ҍ","ҏ","ґ","ғ","ҕ","җ","ҙ","қ","ҝ","ҟ","ҡ","ң","ҥ","ҧ","ҩ","ҫ","ҭ","ү","ұ","ҳ","ҵ","ҷ","ҹ","һ","ҽ","ҿ","ӂ","ӄ","ӆ","ӈ","ӊ","ӌ","ӎ"-"ӏ","ӑ","ӓ","ӕ","ӗ","ә","ӛ","ӝ","ӟ","ӡ","ӣ","ӥ","ӧ","ө","ӫ","ӭ","ӯ","ӱ","ӳ","ӵ","ӷ","ӹ","ӻ","ӽ","ӿ","ԁ","ԃ","ԅ","ԇ","ԉ","ԋ","ԍ","ԏ","ԑ","ԓ","ԕ","ԗ","ԙ","ԛ","ԝ","ԟ","ԡ","ԣ","ԥ","ԧ","ԩ","ԫ","ԭ","ԯ","ՠ"-"ֈ","ა"-"ჺ","ჽ"-"ჿ","ᏸ"-"ᏽ","ᲀ"-"ᲈ","ᴀ"-"ᴫ","ᵫ"-"ᵷ","ᵹ"-"ᶚ","ḁ","ḃ","ḅ","ḇ","ḉ","ḋ","ḍ","ḏ","ḑ","ḓ","ḕ","ḗ","ḙ","ḛ","ḝ","ḟ","ḡ","ḣ","ḥ","ḧ","ḩ","ḫ","ḭ","ḯ","ḱ","ḳ","ḵ","ḷ","ḹ","ḻ","ḽ","ḿ","ṁ","ṃ","ṅ","ṇ","ṉ","ṋ","ṍ","ṏ","ṑ","ṓ","ṕ","ṗ","ṙ","ṛ","ṝ","ṟ","ṡ","ṣ","ṥ","ṧ","ṩ","ṫ","ṭ","ṯ","ṱ","ṳ","ṵ","ṷ","ṹ","ṻ","ṽ","ṿ","ẁ","ẃ","ẅ","ẇ","ẉ","ẋ","ẍ","ẏ","ẑ","ẓ","ẕ"-"ẝ","ẟ","ạ","ả","ấ","ầ","ẩ","ẫ","ậ","ắ","ằ","ẳ","ẵ","ặ","ẹ","ẻ","ẽ","ế","ề","ể","ễ","ệ","ỉ","ị","ọ","ỏ","ố","ồ","ổ","ỗ","ộ","ớ","ờ","ở","ỡ","ợ","ụ","ủ","ứ","ừ","ử","ữ","ự","ỳ","ỵ","ỷ","ỹ","ỻ","ỽ","ỿ"-"ἇ","ἐ"-"ἕ","ἠ"-"ἧ","ἰ"-"ἷ","ὀ"-"ὅ","ὐ"-"ὗ","ὠ"-"ὧ","ὰ"-"ώ","ᾀ"-"ᾇ","ᾐ"-"ᾗ","ᾠ"-"ᾧ","ᾰ"-"ᾴ","ᾶ"-"ᾷ","ι","ῂ"-"ῄ","ῆ"-"ῇ","ῐ"-"ΐ","ῖ"-"ῗ","ῠ"-"ῧ","ῲ"-"ῴ","ῶ"-"ῷ","ℊ","ℎ"-"ℏ","ℓ","ℯ","ℴ","ℹ","ℼ"-"ℽ","ⅆ"-"ⅉ","ⅎ","ↄ","ⰰ"-"ⱟ","ⱡ","ⱥ"-"ⱦ","ⱨ","ⱪ","ⱬ","ⱱ","ⱳ"-"ⱴ","ⱶ"-"ⱻ","ⲁ","ⲃ","ⲅ","ⲇ","ⲉ","ⲋ","ⲍ","ⲏ","ⲑ","ⲓ","ⲕ","ⲗ","ⲙ","ⲛ","ⲝ","ⲟ","ⲡ","ⲣ","ⲥ","ⲧ","ⲩ","ⲫ","ⲭ","ⲯ","ⲱ","ⲳ","ⲵ","ⲷ","ⲹ","ⲻ","ⲽ","ⲿ","ⳁ","ⳃ","ⳅ","ⳇ","ⳉ","ⳋ","ⳍ","ⳏ","ⳑ","ⳓ","ⳕ","ⳗ","ⳙ","ⳛ","ⳝ","ⳟ","ⳡ","ⳣ"-"ⳤ","ⳬ","ⳮ","ⳳ","ⴀ"-"ⴥ","ⴧ","ⴭ","ꙁ","ꙃ","ꙅ","ꙇ","ꙉ","ꙋ","ꙍ","ꙏ","ꙑ","ꙓ","ꙕ","ꙗ","ꙙ","ꙛ","ꙝ","ꙟ","ꙡ","ꙣ","ꙥ","ꙧ","ꙩ","ꙫ","ꙭ","ꚁ","ꚃ","ꚅ","ꚇ","ꚉ","ꚋ","ꚍ","ꚏ","ꚑ","ꚓ","ꚕ","ꚗ","ꚙ","ꚛ","ꜣ","ꜥ","ꜧ","ꜩ","ꜫ","ꜭ","ꜯ"-"ꜱ","ꜳ","ꜵ","ꜷ","ꜹ","ꜻ","ꜽ","ꜿ","ꝁ","ꝃ","ꝅ","ꝇ","ꝉ","ꝋ","ꝍ","ꝏ","ꝑ","ꝓ","ꝕ","ꝗ","ꝙ","ꝛ","ꝝ","ꝟ","ꝡ","ꝣ","ꝥ","ꝧ","ꝩ","ꝫ","ꝭ","ꝯ","ꝱ"-"ꝸ","ꝺ","ꝼ","ꝿ","ꞁ","ꞃ","ꞅ","ꞇ","ꞌ","ꞎ","ꞑ","ꞓ"-"ꞕ","ꞗ","ꞙ","ꞛ","ꞝ","ꞟ","ꞡ","ꞣ","ꞥ","ꞧ","ꞩ","ꞯ","ꞵ","ꞷ","ꞹ","ꞻ","ꞽ","ꞿ","ꟁ","ꟃ","ꟈ","ꟊ","ꟑ","ꟓ","ꟕ","ꟗ","ꟙ","ꟶ","ꟺ","ꬰ"-"ꭚ","ꭠ"-"ꭨ","ꭰ"-"ꮿ","ff"-"st","ﬓ"-"ﬗ","a"-"z"]> +| <#Lm: ["ʰ"-"ˁ","ˆ"-"ˑ","ˠ"-"ˤ","ˬ","ˮ","ʹ","ͺ","ՙ","ـ","ۥ"-"ۦ","ߴ"-"ߵ","ߺ","ࠚ","ࠤ","ࠨ","ࣉ","ॱ","ๆ","ໆ","ჼ","ៗ","ᡃ","ᪧ","ᱸ"-"ᱽ","ᴬ"-"ᵪ","ᵸ","ᶛ"-"ᶿ","ⁱ","ⁿ","ₐ"-"ₜ","ⱼ"-"ⱽ","ⵯ","ⸯ","々","〱"-"〵","〻","ゝ"-"ゞ","ー"-"ヾ","ꀕ","ꓸ"-"ꓽ","ꘌ","ꙿ","ꚜ"-"ꚝ","ꜗ"-"ꜟ","ꝰ","ꞈ","ꟲ"-"ꟴ","ꟸ"-"ꟹ","ꧏ","ꧦ","ꩰ","ꫝ","ꫳ"-"ꫴ","ꭜ"-"ꭟ","ꭩ","ー","゙"-"゚"]> +| <#Lo: ["ª","º","ƻ","ǀ"-"ǃ","ʔ","א"-"ת","ׯ"-"ײ","ؠ"-"ؿ","ف"-"ي","ٮ"-"ٯ","ٱ"-"ۓ","ە","ۮ"-"ۯ","ۺ"-"ۼ","ۿ","ܐ","ܒ"-"ܯ","ݍ"-"ޥ","ޱ","ߊ"-"ߪ","ࠀ"-"ࠕ","ࡀ"-"ࡘ","ࡠ"-"ࡪ","ࡰ"-"ࢇ","ࢉ"-"ࢎ","ࢠ"-"ࣈ","ऄ"-"ह","ऽ","ॐ","क़"-"ॡ","ॲ"-"ঀ","অ"-"ঌ","এ"-"ঐ","ও"-"ন","প"-"র","ল","শ"-"হ","ঽ","ৎ","ড়"-"ঢ়","য়"-"ৡ","ৰ"-"ৱ","ৼ","ਅ"-"ਊ","ਏ"-"ਐ","ਓ"-"ਨ","ਪ"-"ਰ","ਲ"-"ਲ਼","ਵ"-"ਸ਼","ਸ"-"ਹ","ਖ਼"-"ੜ","ਫ਼","ੲ"-"ੴ","અ"-"ઍ","એ"-"ઑ","ઓ"-"ન","પ"-"ર","લ"-"ળ","વ"-"હ","ઽ","ૐ","ૠ"-"ૡ","ૹ","ଅ"-"ଌ","ଏ"-"ଐ","ଓ"-"ନ","ପ"-"ର","ଲ"-"ଳ","ଵ"-"ହ","ଽ","ଡ଼"-"ଢ଼","ୟ"-"ୡ","ୱ","ஃ","அ"-"ஊ","எ"-"ஐ","ஒ"-"க","ங"-"ச","ஜ","ஞ"-"ட","ண"-"த","ந"-"ப","ம"-"ஹ","ௐ","అ"-"ఌ","ఎ"-"ఐ","ఒ"-"న","ప"-"హ","ఽ","ౘ"-"ౚ","ౝ","ౠ"-"ౡ","ಀ","ಅ"-"ಌ","ಎ"-"ಐ","ಒ"-"ನ","ಪ"-"ಳ","ವ"-"ಹ","ಽ","ೝ"-"ೞ","ೠ"-"ೡ","ೱ"-"ೲ","ഄ"-"ഌ","എ"-"ഐ","ഒ"-"ഺ","ഽ","ൎ","ൔ"-"ൖ","ൟ"-"ൡ","ൺ"-"ൿ","අ"-"ඖ","ක"-"න","ඳ"-"ර","ල","ව"-"ෆ","ก"-"ะ","า"-"ำ","เ"-"ๅ","ກ"-"ຂ","ຄ","ຆ"-"ຊ","ຌ"-"ຣ","ລ","ວ"-"ະ","າ"-"ຳ","ຽ","ເ"-"ໄ","ໜ"-"ໟ","ༀ","ཀ"-"ཇ","ཉ"-"ཬ","ྈ"-"ྌ","က"-"ဪ","ဿ","ၐ"-"ၕ","ၚ"-"ၝ","ၡ","ၥ"-"ၦ","ၮ"-"ၰ","ၵ"-"ႁ","ႎ","ᄀ"-"ቈ","ቊ"-"ቍ","ቐ"-"ቖ","ቘ","ቚ"-"ቝ","በ"-"ኈ","ኊ"-"ኍ","ነ"-"ኰ","ኲ"-"ኵ","ኸ"-"ኾ","ዀ","ዂ"-"ዅ","ወ"-"ዖ","ዘ"-"ጐ","ጒ"-"ጕ","ጘ"-"ፚ","ᎀ"-"ᎏ","ᐁ"-"ᙬ","ᙯ"-"ᙿ","ᚁ"-"ᚚ","ᚠ"-"ᛪ","ᛱ"-"ᛸ","ᜀ"-"ᜑ","ᜟ"-"ᜱ","ᝀ"-"ᝑ","ᝠ"-"ᝬ","ᝮ"-"ᝰ","ក"-"ឳ","ៜ","ᠠ"-"ᡂ","ᡄ"-"ᡸ","ᢀ"-"ᢄ","ᢇ"-"ᢨ","ᢪ","ᢰ"-"ᣵ","ᤀ"-"ᤞ","ᥐ"-"ᥭ","ᥰ"-"ᥴ","ᦀ"-"ᦫ","ᦰ"-"ᧉ","ᨀ"-"ᨖ","ᨠ"-"ᩔ","ᬅ"-"ᬳ","ᭅ"-"ᭌ","ᮃ"-"ᮠ","ᮮ"-"ᮯ","ᮺ"-"ᯥ","ᰀ"-"ᰣ","ᱍ"-"ᱏ","ᱚ"-"ᱷ","ᳩ"-"ᳬ","ᳮ"-"ᳳ","ᳵ"-"ᳶ","ᳺ","ℵ"-"ℸ","ⴰ"-"ⵧ","ⶀ"-"ⶖ","ⶠ"-"ⶦ","ⶨ"-"ⶮ","ⶰ"-"ⶶ","ⶸ"-"ⶾ","ⷀ"-"ⷆ","ⷈ"-"ⷎ","ⷐ"-"ⷖ","ⷘ"-"ⷞ","〆","〼","ぁ"-"ゖ","ゟ","ァ"-"ヺ","ヿ","ㄅ"-"ㄯ","ㄱ"-"ㆎ","ㆠ"-"ㆿ","ㇰ"-"ㇿ","䶿","鿿"-"ꀔ","ꀖ"-"ꒌ","ꓐ"-"ꓷ","ꔀ"-"ꘋ","ꘐ"-"ꘟ","ꘪ"-"ꘫ","ꙮ","ꚠ"-"ꛥ","ꞏ","ꟷ","ꟻ"-"ꠁ","ꠃ"-"ꠅ","ꠇ"-"ꠊ","ꠌ"-"ꠢ","ꡀ"-"ꡳ","ꢂ"-"ꢳ","ꣲ"-"ꣷ","ꣻ","ꣽ"-"ꣾ","ꤊ"-"ꤥ","ꤰ"-"ꥆ","ꥠ"-"ꥼ","ꦄ"-"ꦲ","ꧠ"-"ꧤ","ꧧ"-"ꧯ","ꧺ"-"ꧾ","ꨀ"-"ꨨ","ꩀ"-"ꩂ","ꩄ"-"ꩋ","ꩠ"-"ꩯ","ꩱ"-"ꩶ","ꩺ","ꩾ"-"ꪯ","ꪱ","ꪵ"-"ꪶ","ꪹ"-"ꪽ","ꫀ","ꫂ","ꫛ"-"ꫜ","ꫠ"-"ꫪ","ꫲ","ꬁ"-"ꬆ","ꬉ"-"ꬎ","ꬑ"-"ꬖ","ꬠ"-"ꬦ","ꬨ"-"ꬮ","ꯀ"-"ꯢ","힣","ힰ"-"ퟆ","ퟋ"-"ퟻ","豈"-"舘","並"-"龎","יִ","ײַ"-"ﬨ","שׁ"-"זּ","טּ"-"לּ","מּ","נּ"-"סּ","ףּ"-"פּ","צּ"-"ﮱ","ﯓ"-"ﴽ","ﵐ"-"ﶏ","ﶒ"-"ﷇ","ﷰ"-"ﷻ","ﹰ"-"ﹴ","ﹶ"-"ﻼ","ヲ"-"ッ","ア"-"ン","ᅠ"-"ᄒ","ᅡ"-"ᅦ","ᅧ"-"ᅬ","ᅭ"-"ᅲ","ᅳ"-"ᅵ"]> +| <#Lt: ["Dž","Lj","Nj","Dz","ᾈ"-"ᾏ","ᾘ"-"ᾟ","ᾨ"-"ᾯ","ᾼ","ῌ","ῼ"]> +| <#Lu: ["A"-"Z","À"-"Ö","Ø"-"Þ","Ā","Ă","Ą","Ć","Ĉ","Ċ","Č","Ď","Đ","Ē","Ĕ","Ė","Ę","Ě","Ĝ","Ğ","Ġ","Ģ","Ĥ","Ħ","Ĩ","Ī","Ĭ","Į","İ","IJ","Ĵ","Ķ","Ĺ","Ļ","Ľ","Ŀ","Ł","Ń","Ņ","Ň","Ŋ","Ō","Ŏ","Ő","Œ","Ŕ","Ŗ","Ř","Ś","Ŝ","Ş","Š","Ţ","Ť","Ŧ","Ũ","Ū","Ŭ","Ů","Ű","Ų","Ŵ","Ŷ","Ÿ"-"Ź","Ż","Ž","Ɓ"-"Ƃ","Ƅ","Ɔ"-"Ƈ","Ɖ"-"Ƌ","Ǝ"-"Ƒ","Ɠ"-"Ɣ","Ɩ"-"Ƙ","Ɯ"-"Ɲ","Ɵ"-"Ơ","Ƣ","Ƥ","Ʀ"-"Ƨ","Ʃ","Ƭ","Ʈ"-"Ư","Ʊ"-"Ƴ","Ƶ","Ʒ"-"Ƹ","Ƽ","DŽ","LJ","NJ","Ǎ","Ǐ","Ǒ","Ǔ","Ǖ","Ǘ","Ǚ","Ǜ","Ǟ","Ǡ","Ǣ","Ǥ","Ǧ","Ǩ","Ǫ","Ǭ","Ǯ","DZ","Ǵ","Ƕ"-"Ǹ","Ǻ","Ǽ","Ǿ","Ȁ","Ȃ","Ȅ","Ȇ","Ȉ","Ȋ","Ȍ","Ȏ","Ȑ","Ȓ","Ȕ","Ȗ","Ș","Ț","Ȝ","Ȟ","Ƞ","Ȣ","Ȥ","Ȧ","Ȩ","Ȫ","Ȭ","Ȯ","Ȱ","Ȳ","Ⱥ"-"Ȼ","Ƚ"-"Ⱦ","Ɂ","Ƀ"-"Ɇ","Ɉ","Ɋ","Ɍ","Ɏ","Ͱ","Ͳ","Ͷ","Ϳ","Ά","Έ"-"Ί","Ό","Ύ"-"Ώ","Α"-"Ρ","Σ"-"Ϋ","Ϗ","ϒ"-"ϔ","Ϙ","Ϛ","Ϝ","Ϟ","Ϡ","Ϣ","Ϥ","Ϧ","Ϩ","Ϫ","Ϭ","Ϯ","ϴ","Ϸ","Ϲ"-"Ϻ","Ͻ"-"Я","Ѡ","Ѣ","Ѥ","Ѧ","Ѩ","Ѫ","Ѭ","Ѯ","Ѱ","Ѳ","Ѵ","Ѷ","Ѹ","Ѻ","Ѽ","Ѿ","Ҁ","Ҋ","Ҍ","Ҏ","Ґ","Ғ","Ҕ","Җ","Ҙ","Қ","Ҝ","Ҟ","Ҡ","Ң","Ҥ","Ҧ","Ҩ","Ҫ","Ҭ","Ү","Ұ","Ҳ","Ҵ","Ҷ","Ҹ","Һ","Ҽ","Ҿ","Ӏ"-"Ӂ","Ӄ","Ӆ","Ӈ","Ӊ","Ӌ","Ӎ","Ӑ","Ӓ","Ӕ","Ӗ","Ә","Ӛ","Ӝ","Ӟ","Ӡ","Ӣ","Ӥ","Ӧ","Ө","Ӫ","Ӭ","Ӯ","Ӱ","Ӳ","Ӵ","Ӷ","Ӹ","Ӻ","Ӽ","Ӿ","Ԁ","Ԃ","Ԅ","Ԇ","Ԉ","Ԋ","Ԍ","Ԏ","Ԑ","Ԓ","Ԕ","Ԗ","Ԙ","Ԛ","Ԝ","Ԟ","Ԡ","Ԣ","Ԥ","Ԧ","Ԩ","Ԫ","Ԭ","Ԯ","Ա"-"Ֆ","Ⴀ"-"Ⴥ","Ⴧ","Ⴭ","Ꭰ"-"Ᏽ","Ა"-"Ჺ","Ჽ"-"Ჿ","Ḁ","Ḃ","Ḅ","Ḇ","Ḉ","Ḋ","Ḍ","Ḏ","Ḑ","Ḓ","Ḕ","Ḗ","Ḙ","Ḛ","Ḝ","Ḟ","Ḡ","Ḣ","Ḥ","Ḧ","Ḩ","Ḫ","Ḭ","Ḯ","Ḱ","Ḳ","Ḵ","Ḷ","Ḹ","Ḻ","Ḽ","Ḿ","Ṁ","Ṃ","Ṅ","Ṇ","Ṉ","Ṋ","Ṍ","Ṏ","Ṑ","Ṓ","Ṕ","Ṗ","Ṙ","Ṛ","Ṝ","Ṟ","Ṡ","Ṣ","Ṥ","Ṧ","Ṩ","Ṫ","Ṭ","Ṯ","Ṱ","Ṳ","Ṵ","Ṷ","Ṹ","Ṻ","Ṽ","Ṿ","Ẁ","Ẃ","Ẅ","Ẇ","Ẉ","Ẋ","Ẍ","Ẏ","Ẑ","Ẓ","Ẕ","ẞ","Ạ","Ả","Ấ","Ầ","Ẩ","Ẫ","Ậ","Ắ","Ằ","Ẳ","Ẵ","Ặ","Ẹ","Ẻ","Ẽ","Ế","Ề","Ể","Ễ","Ệ","Ỉ","Ị","Ọ","Ỏ","Ố","Ồ","Ổ","Ỗ","Ộ","Ớ","Ờ","Ở","Ỡ","Ợ","Ụ","Ủ","Ứ","Ừ","Ử","Ữ","Ự","Ỳ","Ỵ","Ỷ","Ỹ","Ỻ","Ỽ","Ỿ","Ἀ"-"Ἇ","Ἐ"-"Ἕ","Ἠ"-"Ἧ","Ἰ"-"Ἷ","Ὀ"-"Ὅ","Ὑ","Ὓ","Ὕ","Ὗ","Ὠ"-"Ὧ","Ᾰ"-"Ά","Ὲ"-"Ή","Ῐ"-"Ί","Ῠ"-"Ῥ","Ὸ"-"Ώ","ℂ","ℇ","ℋ"-"ℍ","ℐ"-"ℒ","ℕ","ℙ"-"ℝ","ℤ","Ω","ℨ","K"-"ℭ","ℰ"-"ℳ","ℾ"-"ℿ","ⅅ","Ↄ","Ⰰ"-"Ⱟ","Ⱡ","Ɫ"-"Ɽ","Ⱨ","Ⱪ","Ⱬ","Ɑ"-"Ɒ","Ⱳ","Ⱶ","Ȿ"-"Ⲁ","Ⲃ","Ⲅ","Ⲇ","Ⲉ","Ⲋ","Ⲍ","Ⲏ","Ⲑ","Ⲓ","Ⲕ","Ⲗ","Ⲙ","Ⲛ","Ⲝ","Ⲟ","Ⲡ","Ⲣ","Ⲥ","Ⲧ","Ⲩ","Ⲫ","Ⲭ","Ⲯ","Ⲱ","Ⲳ","Ⲵ","Ⲷ","Ⲹ","Ⲻ","Ⲽ","Ⲿ","Ⳁ","Ⳃ","Ⳅ","Ⳇ","Ⳉ","Ⳋ","Ⳍ","Ⳏ","Ⳑ","Ⳓ","Ⳕ","Ⳗ","Ⳙ","Ⳛ","Ⳝ","Ⳟ","Ⳡ","Ⳣ","Ⳬ","Ⳮ","Ⳳ","Ꙁ","Ꙃ","Ꙅ","Ꙇ","Ꙉ","Ꙋ","Ꙍ","Ꙏ","Ꙑ","Ꙓ","Ꙕ","Ꙗ","Ꙙ","Ꙛ","Ꙝ","Ꙟ","Ꙡ","Ꙣ","Ꙥ","Ꙧ","Ꙩ","Ꙫ","Ꙭ","Ꚁ","Ꚃ","Ꚅ","Ꚇ","Ꚉ","Ꚋ","Ꚍ","Ꚏ","Ꚑ","Ꚓ","Ꚕ","Ꚗ","Ꚙ","Ꚛ","Ꜣ","Ꜥ","Ꜧ","Ꜩ","Ꜫ","Ꜭ","Ꜯ","Ꜳ","Ꜵ","Ꜷ","Ꜹ","Ꜻ","Ꜽ","Ꜿ","Ꝁ","Ꝃ","Ꝅ","Ꝇ","Ꝉ","Ꝋ","Ꝍ","Ꝏ","Ꝑ","Ꝓ","Ꝕ","Ꝗ","Ꝙ","Ꝛ","Ꝝ","Ꝟ","Ꝡ","Ꝣ","Ꝥ","Ꝧ","Ꝩ","Ꝫ","Ꝭ","Ꝯ","Ꝺ","Ꝼ","Ᵹ"-"Ꝿ","Ꞁ","Ꞃ","Ꞅ","Ꞇ","Ꞌ","Ɥ","Ꞑ","Ꞓ","Ꞗ","Ꞙ","Ꞛ","Ꞝ","Ꞟ","Ꞡ","Ꞣ","Ꞥ","Ꞧ","Ꞩ","Ɦ"-"Ɪ","Ʞ"-"Ꞵ","Ꞷ","Ꞹ","Ꞻ","Ꞽ","Ꞿ","Ꟁ","Ꟃ","Ꞔ"-"Ꟈ","Ꟊ","Ꟑ","Ꟗ","Ꟙ","Ꟶ","A"-"Z"]> +| <#Nl: ["ᛮ"-"ᛰ","Ⅰ"-"ↂ","ↅ"-"ↈ","〇","〡"-"〩","〸"-"〺","ꛦ"-"ꛯ"]> + +// unicode_identifier_extend +| <#UnicodeIdentifierExtend: (|||||)> +| <#Cf: ["­","؀"-"؅","؜","۝","܏","࢐"-"࢑","࣢","᠎","​"-"‏","‪"-"‮","⁠"-"⁤","⁦"-"","",""-""]> +| <#Mc: ["ः","ऻ","ा"-"ी","ॉ"-"ौ","ॎ"-"ॏ","ং"-"ঃ","া"-"ী","ে"-"ৈ","ো"-"ৌ","ৗ","ਃ","ਾ"-"ੀ","ઃ","ા"-"ી","ૉ","ો"-"ૌ","ଂ"-"ଃ","ା","ୀ","େ"-"ୈ","ୋ"-"ୌ","ୗ","ா"-"ி","ு"-"ூ","ெ"-"ை","ொ"-"ௌ","ௗ","ఁ"-"ః","ు"-"ౄ","ಂ"-"ಃ","ಾ","ೀ"-"ೄ","ೇ"-"ೈ","ೊ"-"ೋ","ೕ"-"ೖ","ೳ","ം"-"ഃ","ാ"-"ീ","െ"-"ൈ","ൊ"-"ൌ","ൗ","ං"-"ඃ","ා"-"ෑ","ෘ"-"ෟ","ෲ"-"ෳ","༾"-"༿","ཿ","ါ"-"ာ","ေ","း","ျ"-"ြ","ၖ"-"ၗ","ၢ"-"ၤ","ၧ"-"ၭ","ႃ"-"ႄ","ႇ"-"ႌ","ႏ","ႚ"-"ႜ","᜕","᜴","ា","ើ"-"ៅ","ះ"-"ៈ","ᤣ"-"ᤦ","ᤩ"-"ᤫ","ᤰ"-"ᤱ","ᤳ"-"ᤸ","ᨙ"-"ᨚ","ᩕ","ᩗ","ᩡ","ᩣ"-"ᩤ","ᩭ"-"ᩲ","ᬄ","ᬵ","ᬻ","ᬽ"-"ᭁ","ᭃ"-"᭄","ᮂ","ᮡ","ᮦ"-"ᮧ","᮪","ᯧ","ᯪ"-"ᯬ","ᯮ","᯲"-"᯳","ᰤ"-"ᰫ","ᰴ"-"ᰵ","᳡","᳷","〮"-"〯","ꠣ"-"ꠤ","ꠧ","ꢀ"-"ꢁ","ꢴ"-"ꣃ","ꥒ"-"꥓","ꦃ","ꦴ"-"ꦵ","ꦺ"-"ꦻ","ꦾ"-"꧀","ꨯ"-"ꨰ","ꨳ"-"ꨴ","ꩍ","ꩻ","ꩽ","ꫫ","ꫮ"-"ꫯ","ꫵ","ꯣ"-"ꯤ","ꯦ"-"ꯧ","ꯩ"-"ꯪ","꯬"]> +| <#Mn: ["̀"-"ͯ","҃"-"҇","֑"-"ֽ","ֿ","ׁ"-"ׂ","ׄ"-"ׅ","ׇ","ؐ"-"ؚ","ً"-"ٟ","ٰ","ۖ"-"ۜ","۟"-"ۤ","ۧ"-"ۨ","۪"-"ۭ","ܑ","ܰ"-"݊","ަ"-"ް","߫"-"߳","߽","ࠖ"-"࠙","ࠛ"-"ࠣ","ࠥ"-"ࠧ","ࠩ"-"࠭","࡙"-"࡛","࢘"-"࢟","࣊"-"࣡","ࣣ"-"ं","ऺ","़","ु"-"ै","्","॑"-"ॗ","ॢ"-"ॣ","ঁ","়","ু"-"ৄ","্","ৢ"-"ৣ","৾","ਁ"-"ਂ","਼","ੁ"-"ੂ","ੇ"-"ੈ","ੋ"-"੍","ੑ","ੰ"-"ੱ","ੵ","ઁ"-"ં","઼","ુ"-"ૅ","ે"-"ૈ","્","ૢ"-"ૣ","ૺ"-"૿","ଁ","଼","ି","ୁ"-"ୄ","୍","୕"-"ୖ","ୢ"-"ୣ","ஂ","ீ","்","ఀ","ఄ","఼","ా"-"ీ","ె"-"ై","ొ"-"్","ౕ"-"ౖ","ౢ"-"ౣ","ಁ","಼","ಿ","ೆ","ೌ"-"್","ೢ"-"ೣ","ഀ"-"ഁ","഻"-"഼","ു"-"ൄ","്","ൢ"-"ൣ","ඁ","්","ි"-"ු","ූ","ั","ิ"-"ฺ","็"-"๎","ັ","ິ"-"ຼ","່"-"໎","༘"-"༙","༵","༷","༹","ཱ"-"ཾ","ྀ"-"྄","྆"-"྇","ྍ"-"ྗ","ྙ"-"ྼ","࿆","ိ"-"ူ","ဲ"-"့","္"-"်","ွ"-"ှ","ၘ"-"ၙ","ၞ"-"ၠ","ၱ"-"ၴ","ႂ","ႅ"-"ႆ","ႍ","ႝ","፝"-"፟","ᜒ"-"᜔","ᜲ"-"ᜳ","ᝒ"-"ᝓ","ᝲ"-"ᝳ","឴"-"឵","ិ"-"ួ","ំ","៉"-"៓","៝","᠋"-"᠍","᠏","ᢅ"-"ᢆ","ᢩ","ᤠ"-"ᤢ","ᤧ"-"ᤨ","ᤲ","᤹"-"᤻","ᨗ"-"ᨘ","ᨛ","ᩖ","ᩘ"-"ᩞ","᩠","ᩢ","ᩥ"-"ᩬ","ᩳ"-"᩼","᩿","᪰"-"᪽","ᪿ"-"ᫎ","ᬀ"-"ᬃ","᬴","ᬶ"-"ᬺ","ᬼ","ᭂ","᭫"-"᭳","ᮀ"-"ᮁ","ᮢ"-"ᮥ","ᮨ"-"ᮩ","᮫"-"ᮭ","᯦","ᯨ"-"ᯩ","ᯭ","ᯯ"-"ᯱ","ᰬ"-"ᰳ","ᰶ"-"᰷","᳐"-"᳒","᳔"-"᳠","᳢"-"᳨","᳭","᳴","᳸"-"᳹","᷀"-"᷿","⃐"-"⃜","⃡","⃥"-"⃰","⳯"-"⳱","⵿","ⷠ"-"ⷿ","〪"-"〭","゙"-"゚","꙯","ꙴ"-"꙽","ꚞ"-"ꚟ","꛰"-"꛱","ꠂ","꠆","ꠋ","ꠥ"-"ꠦ","꠬","꣄"-"ꣅ","꣠"-"꣱","ꣿ","ꤦ"-"꤭","ꥇ"-"ꥑ","ꦀ"-"ꦂ","꦳","ꦶ"-"ꦹ","ꦼ"-"ꦽ","ꧥ","ꨩ"-"ꨮ","ꨱ"-"ꨲ","ꨵ"-"ꨶ","ꩃ","ꩌ","ꩼ","ꪰ","ꪲ"-"ꪴ","ꪷ"-"ꪸ","ꪾ"-"꪿","꫁","ꫬ"-"ꫭ","꫶","ꯥ","ꯨ","꯭","ﬞ","︀"-"️","︠"-"︯"]> +| <#Nd: ["0"-"9","٠"-"٩","۰"-"۹","߀"-"߉","०"-"९","০"-"৯","੦"-"੯","૦"-"૯","୦"-"୯","௦"-"௯","౦"-"౯","೦"-"೯","൦"-"൯","෦"-"෯","๐"-"๙","໐"-"໙","༠"-"༩","၀"-"၉","႐"-"႙","០"-"៩","᠐"-"᠙","᥆"-"᥏","᧐"-"᧙","᪀"-"᪉","᪐"-"᪙","᭐"-"᭙","᮰"-"᮹","᱀"-"᱉","᱐"-"᱙","꘠"-"꘩","꣐"-"꣙","꤀"-"꤉","꧐"-"꧙","꧰"-"꧹","꩐"-"꩙","꯰"-"꯹","0"-"9"]> +| <#Pc: ["‿"-"⁀","⁔","︳"-"︴","﹍"-"﹏","_"]> + +// CJK Unified Ideographs block according to https://en.wikipedia.org/wiki/CJK_Unified_Ideographs_(Unicode_block) +| <#CJK: ["\uAC00"-"\uD7A3", "\u4E00"-"\u9FFF"]> + +| < #SPECIAL_ESC: "\\'" > /* Allowing this will break LIKE ... ESCAPE ... */ +| < #ESC: "\\" ["n","t","b","r","f","\\","\""] > +| < S_CHAR_LITERAL: ( + (["U","E","N","R","B"]|"RB"|"_utf8")? + ( + ("'" ( | | ~["'", "\\"] )* "'") | ("'" ("''" | ~["'"])* "'") + // Alternative Oracle Escape Modes + | ("q'{" (~[])* "}'") + | ("q'(" (~[])* ")'") + | ("q'[" (~[])* "]'") + | ("q''" (~[])* "''") + // | ("q'\\" (~[])* "\\'") <--- Does not work + ) + ) > + { + // contains the token and always the longest match is returned + // So when Backslash is explicitly not allowed as an Escape Character and a is found + // which contains the , then we will need to + // 1) break the at close it with a "'" + // 2) continue tokenizing after that with a new or any other Token + if ( !configuration.getAsBoolean(Feature.allowBackslashEscapeCharacter) && matchedToken.image.contains("\\'") ) { + matchedToken.image = image.substring( 0, image.indexOf("\\'") + 1 ) + "'"; + for (int i=0;i") ) { + matchedToken.kind = i; + } + } + input_stream.backup(image.length() - matchedToken.image.length() ); + } else if ( configuration.getAsBoolean(Feature.allowBackslashEscapeCharacter) && matchedToken.image.contains("\\''") ) { + matchedToken.image = image.substring( 0, image.lastIndexOf("\\'") + 3); + for (int i=0;i") ) { + matchedToken.kind = i; + } + } + input_stream.backup(image.length() - matchedToken.image.length() ); + } + } +| < S_QUOTED_IDENTIFIER: "\"" ( "\"\"" | ~["\n","\r","\""])* "\"" | "$$" (~["$"])* "$$" | ("`" (~["\n","\r","`"])+ "`") | ( "[" (~["\n","\r","]"])* "]" ) > + { + if ( !configuration.getAsBoolean(Feature.allowSquareBracketQuotation) && matchedToken.image.charAt(0) == '[' ) { + matchedToken.image = "["; + for (int i=0;i condition=Condition() + ( stm = SingleStatement() | stm = Block() ) { ifElseStatement = new IfElseStatement(condition, stm); } + [ LOOKAHEAD(2) + [ { ifElseStatement.setUsingSemicolonForIfStatement(true); } ] + ( stm2 = SingleStatement() | stm2 = Block() ) { ifElseStatement.setElseStatement(stm2); } + ] + + [ + LOOKAHEAD(2) + { if (stm2!=null) + ifElseStatement.setUsingSemicolonForElseStatement(true); + else if (ifElseStatement!=null) + ifElseStatement.setUsingSemicolonForIfStatement(true); + } + ] + ) + | + ( + (stm = SingleStatement() + | stm = Block()) + + ( | ) + ) + | + LOOKAHEAD( { stm==null && getAsBoolean(Feature.allowUnsupportedStatements) } ) stm = UnsupportedStatement() + } catch (ParseException ex) { + if ( getAsBoolean(Feature.allowUnsupportedStatements) ) { + stm = new UnsupportedStatement( stm.toString(), error_skipto(ST_SEMICOLON) ); + } else if ( errorRecovery ) { + parseErrors.add(ex); + error_skipto(ST_SEMICOLON); + stm = null; + } else { + throw ex; + } + } + + ) + + { + return ifElseStatement!=null ? ifElseStatement : stm; + } +} + +Statement SingleStatement() : +{ + Statement stm = null; + List with = null; +} +{ + ( + LOOKAHEAD(3) ( + [ LOOKAHEAD(2) with=WithList() ] + ( + stm = SelectWithWithItems( with ) + | + stm = Insert( with ) + | + stm = Update( with ) + | + stm = Delete( with ) + | + stm = Merge( with) + ) + ) + | + stm = Select() + | + stm = TableStatement() + | + LOOKAHEAD(3) stm = Upsert() + | + LOOKAHEAD(2) stm = Alter() + | + // @todo: merge this into the ALTER TABLE statement + stm = RenameTableStatement() + | + stm = Create() + | + stm = Drop() + | + stm = Analyze() + | + stm = Truncate() + | + stm = Execute() + | + stm = Set() + | + stm = Reset() + | + stm = Show() + | + stm = RefreshMaterializedView() + | + stm = Use() + | + stm = SavepointStatement() + | + stm = RollbackStatement() + | + stm = Commit() + | + stm = Comment() + | + stm = Describe() + | + stm = Explain() + | + stm = Declare() + | + stm = Grant() + | + stm = PurgeStatement() + ) + { return stm; } +} + +Block Block() #Block : { + Statements stmts = new Statements(); + List list = new ArrayList(); + Statement stm; + Block block = new Block(); +} +{ + + ()* + ( + stm = SingleStatement() + | stm = Block() + ) + + { list.add(stm); } + + ( + ( + ( + stm = SingleStatement() + | stm = Block() + ) + + { list.add(stm); } + ) + )* + + { + stmts.setStatements(list); + block.setStatements(stmts); + } + + [LOOKAHEAD(2) { block.setSemicolonAfterEnd(true); } ] + { + return block; + } +} + +Statements Statements() #Statements: { + Statements stmts = new Statements(); + IfElseStatement ifElseStatement = null; + Statement stm = null; + Statement stm2 = null; + Expression condition; +} +{ + ( + ( + ( )* + + // todo: allow also first statement to be an `UnsupportedStatement` + try { + ( + condition=Condition() + ( stm = SingleStatement() | stm = Block() ) { ifElseStatement = new IfElseStatement(condition, stm); } + [ LOOKAHEAD(2) + [ { ifElseStatement.setUsingSemicolonForIfStatement(true); } ] + ( stm2 = SingleStatement() | stm2 = Block() ) { ifElseStatement.setElseStatement(stm2); } + ] + + { stmts.add( ifElseStatement ); } + + [ + LOOKAHEAD(2) + { if (stm2!=null) + ifElseStatement.setUsingSemicolonForElseStatement(true); + else if (ifElseStatement!=null) + ifElseStatement.setUsingSemicolonForIfStatement(true); + } + ] + ) + | + ( + (stm = SingleStatement() + | stm = Block()) + + ( | ) + ) + { + stmts.add(stm); stm=null; + } + + } catch (ParseException ex) { + if ( getAsBoolean(Feature.allowUnsupportedStatements) ) { + UnsupportedStatement unsupportedStatement = new UnsupportedStatement( stm!=null ? stm.toString() : "", error_skipto(ST_SEMICOLON) ); + if (!unsupportedStatement.isEmpty()) { + stmts.add( unsupportedStatement ); + } + } else if ( errorRecovery ) { + parseErrors.add(ex); + error_skipto(ST_SEMICOLON); + stmts.add( null ); + } else { + throw ex; + } + } + + ) + + ( + ( )* + try { + ( + condition=Condition() + ( stm = SingleStatement() | stm = Block() ) { ifElseStatement = new IfElseStatement(condition, stm); } + [ LOOKAHEAD(2) + [ { ifElseStatement.setUsingSemicolonForIfStatement(true); } ] + ( stm2 = SingleStatement() | stm2 = Block() ) { ifElseStatement.setElseStatement(stm2); } + ] + + { stmts.add( ifElseStatement ); } + ) + | + ( + stm = SingleStatement() + | stm = Block() + ) { stmts.add(stm); stm=null; } + } catch (ParseException ex) { + if ( getAsBoolean(Feature.allowUnsupportedStatements) ) { + UnsupportedStatement unsupportedStatement = new UnsupportedStatement( stm!=null ? stm.toString() : "" , error_skipto(ST_SEMICOLON) ); + if (!unsupportedStatement.isEmpty()) { + stmts.add( unsupportedStatement ); + } + } else if ( errorRecovery ) { + parseErrors.add(ex); + error_skipto(ST_SEMICOLON); + stmts.add( null ); + } else { + throw ex; + } + } + + ( LOOKAHEAD(2) { if (stm2!=null) + ifElseStatement.setUsingSemicolonForElseStatement(true); + else if (ifElseStatement!=null) + ifElseStatement.setUsingSemicolonForIfStatement(true); + } )* + + )* + + [ + LOOKAHEAD( { getAsBoolean(Feature.allowUnsupportedStatements) } ) + stm = UnsupportedStatement() + { + if (!( (UnsupportedStatement) stm).isEmpty()) { + stmts.add( stm ); + } + } + ] + + )* + + + { + return stmts; + } +} + +JAVACODE +List error_skipto(int kind) { + ArrayList tokenImages = new ArrayList(); + ParseException e = generateParseException(); + Token t; + do { + t = getNextToken(); + if (t.kind != kind && t.kind != EOF) { + tokenImages.add(t.image); + } + } while (t.kind != kind && t.kind != EOF); + return tokenImages; +} + +DeclareStatement Declare(): { + UserVariable userVariable; + ColDataType colDataType; + Expression defaultExpr = null; + DeclareStatement stmt = new DeclareStatement(); + String typeName; + String columnName; + ColumnDefinition colDef; +} { + userVariable = UserVariable() + ( + LOOKAHEAD(2) ( + "(" colDef = ColumnDefinition() + { + stmt.withUserVariable(userVariable) + .withDeclareType(DeclareType.TABLE) + .addColumnDefinition(colDef); + } + ("," colDef = ColumnDefinition() { stmt.addColumnDefinition(colDef); })* ")" + ) + | + typeName = RelObjectName() + { + stmt.withUserVariable(userVariable) + .withDeclareType(DeclareType.AS) + .withTypeName(typeName); + } + | + (colDataType = ColDataType() ["=" defaultExpr = Expression()] + { + stmt.withDeclareType(DeclareType.TYPE) + .addType(userVariable, colDataType, defaultExpr); + } + ("," userVariable = UserVariable() colDataType = ColDataType() { defaultExpr = null; } + ["=" defaultExpr = Expression()] { stmt.addType(userVariable, colDataType, defaultExpr); } )* + ) + ) + { + return stmt; + } +} + + + +SetStatement Set(): { + String namePart; + Object name; + ExpressionList expList; + boolean useEqual = false; + SetStatement set; + Expression exp = null; + Token tk = null; + String effectParameter = null; +} +{ + + [LOOKAHEAD(3) (tk = | tk = ) {effectParameter = tk.image; } ] + ( + LOOKAHEAD(2) + { name = "Time Zone"; useEqual=false; } + | + ( + name = UserVariable() ["=" { useEqual=true; } ] + ) + | + ( + name = IdentifierChain() + ["=" { useEqual=true; } ] + ) + ) + exp=Expression() + { + expList = new ExpressionList(); + expList.add(exp); + set = new SetStatement(name, expList) + .withUseEqual(useEqual) + .withEffectParameter(effectParameter); + } + + ( + { useEqual=false; } + "," + (LOOKAHEAD(3) + ( + ( LOOKAHEAD(2) + { name = "Time Zone"; useEqual=false; } + | + (name = RelObjectNameExt() ["=" { useEqual=true; } ]) + ) + exp=Expression() + { + expList = new ExpressionList(); + expList.add(exp); + set.add(name, expList, useEqual); + } + ) + | + exp=Expression() { expList.add(exp); } + ) + )* + { return set; } +} + +ResetStatement Reset(): { + String name; + ResetStatement reset; + Token all; +} +{ + ( LOOKAHEAD(2) {name = "Time Zone"; } | name = RelObjectName() | all = {name = all.image; } ) + { reset = new ResetStatement(name); return reset; } +} + +RenameTableStatement RenameTableStatement(): { + RenameTableStatement renameTableStatement; + Table oldName; + Table newName; + boolean usingTableKeyword=false; + boolean usesIfExistsKeyword=false; + String waitDirective = ""; + Token token; +} +{ + + [ LOOKAHEAD(2) { usingTableKeyword = true; } ] + [ LOOKAHEAD(2) { usesIfExistsKeyword = true; } ] + oldName = Table() + [ ( + token= { waitDirective = "WAIT " + token.image; } + | + { waitDirective = "NOWAIT"; } + ) ] + + newName = Table() + + { + renameTableStatement = new RenameTableStatement(oldName, newName, usingTableKeyword, usesIfExistsKeyword, waitDirective); + } + + ( + "," + oldName = Table() + + newName = Table() + { + renameTableStatement.addTableNames(oldName, newName); + } + )* + + { + return renameTableStatement; + } +} + +PurgeStatement PurgeStatement(): { + PurgeStatement purgeStatement = null; + Table table; + Index index; + Token tableSpaceToken; + Token userToken = null; +} +{ + + ( + table=Table() { purgeStatement = new PurgeStatement(table); } + | + index=Index() { purgeStatement = new PurgeStatement(index); } + | + { purgeStatement = new PurgeStatement(PurgeObjectType.RECYCLEBIN); } + | + { purgeStatement = new PurgeStatement(PurgeObjectType.DBA_RECYCLEBIN); } + | + tableSpaceToken= [ userToken= ] { + purgeStatement = new PurgeStatement( + PurgeObjectType.TABLESPACE + , tableSpaceToken.image + , userToken!=null ? userToken.image : null); + } + ) + + { + return purgeStatement; + } +} + +DescribeStatement Describe(): { + Table table; + DescribeStatement stmt = new DescribeStatement(); + Token tk = null; +} { + (tk= | tk=) + table = Table() { stmt.setDescribeType(tk.image).setTable(table); } + { + return stmt; + } +} + +ExplainStatement Explain(): { + Select select; + Table table = null; + List options = null; + ExplainStatement es = new ExplainStatement(); +} { + + ( + LOOKAHEAD(3)( + options=ExplainStatementOptions() + select = Select( ) + { + es.setStatement(select); + if (options != null && !options.isEmpty()) { + for(ExplainStatement.Option o : options) { + es.addOption(o); + } + } + } + ) + | + ( + table=Table( ) { es.setTable(table); } + ) + + ) + { return es; } +} + +/** + * Postgres supports TRUE,ON,1,FALSE,OFF,0 as values + */ +String ExplainOptionBoolean(): +{ + Token tk = null; +} +{ + // intentionally not supporting 0,1 at the moment + [( tk= | tk= | tk= | tk= )] // optional + { + return tk != null ? tk.image : null; + } +} + +/** + * The output format, which can be TEXT, XML, JSON, or YAML + */ +String ExplainFormatOption(): +{ + Token tk = null; +} +{ + // TODO support Text + [( tk= | tk= | tk= )] // optional + { + return tk != null ? tk.image : null; + } +} + +/** + * Options for explain, see https://www.postgresql.org/docs/9.1/sql-explain.html + */ +List ExplainStatementOptions(): +{ + List options = new ArrayList(); + ExplainStatement.Option option = null; + Token token = null; + String value = null; +} +{ + ( + ( value=ExplainOptionBoolean() + { + option = new ExplainStatement.Option(ExplainStatement.OptionType.ANALYZE); + option.setValue(value); + options.add(option); + } + ) + | + ( value=ExplainOptionBoolean() + { + option = new ExplainStatement.Option(ExplainStatement.OptionType.BUFFERS); + option.setValue(value); + options.add(option); + } + ) + | + ( value=ExplainOptionBoolean() + { + option = new ExplainStatement.Option(ExplainStatement.OptionType.COSTS); + option.setValue(value); + options.add(option); + } + ) + | + ( value=ExplainOptionBoolean() + { + option = new ExplainStatement.Option(ExplainStatement.OptionType.VERBOSE); + option.setValue(value); + options.add(option); + } + ) + | + ( value=ExplainFormatOption() + { + option = new ExplainStatement.Option(ExplainStatement.OptionType.FORMAT); + option.setValue(value); + options.add(option); + } + ) + )* //zero or many times those productions + { + return options; + } +} + +UseStatement Use(): { + String name; + boolean hasSchemaKeyword = false; +} +{ + [ LOOKAHEAD(2) { hasSchemaKeyword = true; } ] name = RelObjectNameExt() + { + return new UseStatement(name, hasSchemaKeyword); + } +} + +Statement Show(): +{ + Statement statement; + List captureRest; +} +{ + + ( + LOOKAHEAD(2) statement = ShowColumns() + | + LOOKAHEAD(2) statement = ShowIndex() + | + LOOKAHEAD(2) statement = ShowTables() + | + // any of the RDBMS specific SHOW syntax + captureRest = captureRest() + { + if (captureRest.size()==1) { + statement = new ShowStatement(captureRest.get(0)); + } else { + statement = new UnsupportedStatement("SHOW", captureRest); + } + } + ) + { + return statement; + } +} + +ShowColumnsStatement ShowColumns(): { + String tableName; +} +{ + tableName = RelObjectNameExt() + { + return new ShowColumnsStatement(tableName); + } +} + +ShowIndexStatement ShowIndex(): { + String tableName; +} +{ + tableName = RelObjectNameExt() + { + return new ShowIndexStatement(tableName); + } +} + +Statement RefreshMaterializedView(): { + Table view = null; + boolean concurrently = false; + RefreshMode refreshMode = null; + List captureRest; +} +{ + + [ LOOKAHEAD(2) { concurrently = true; } ] + view = Table() + [ + { refreshMode = RefreshMode.WITH_DATA; } + [ + { refreshMode = RefreshMode.WITH_NO_DATA; } + ] + + ] + captureRest = captureRest() + { + if (concurrently && refreshMode == RefreshMode.WITH_NO_DATA) { + return new UnsupportedStatement("REFRESH", captureRest); + } else { + return new RefreshMaterializedViewStatement(view, concurrently, refreshMode); + } + } +} +// https://dev.mysql.com/doc/refman/8.0/en/show-tables.html +ShowTablesStatement ShowTables(): { + ShowTablesStatement showTablesStatement; + EnumSet modifiers = EnumSet.noneOf(ShowTablesStatement.Modifiers.class); + ShowTablesStatement.SelectionMode selectionMode = null; + String dbName = null; + Expression likeExpression = null; + Expression whereCondition = null; +} +{ + [ { modifiers.add(ShowTablesStatement.Modifiers.EXTENDED); } ] + [ { modifiers.add(ShowTablesStatement.Modifiers.FULL); } ] + + [ + ( + {selectionMode = ShowTablesStatement.SelectionMode.FROM; } + | { selectionMode = ShowTablesStatement.SelectionMode.IN; } + ) + dbName = RelObjectNameExt() + ] + [ ( likeExpression = SimpleExpression() | whereCondition = Expression()) ] + { + showTablesStatement = new ShowTablesStatement(); + showTablesStatement.setModifiers(modifiers); + showTablesStatement.setSelectionMode(selectionMode); + showTablesStatement.setDbName(dbName); + showTablesStatement.setLikeExpression(likeExpression); + showTablesStatement.setWhereCondition(whereCondition); + return showTablesStatement; + } +} + +Values Values(): { + ExpressionList expressions; +} { + ( | ) + expressions = ExpressionList() + + { + return new Values(expressions); + } +} + +ReturningClause ReturningClause(): +{ + Token keyword; + List> selectItems; + Object dataItem; + List dataItems = null; +} +{ + ( keyword= | keyword= ) + selectItems = SelectItemsList() + + [ + + ( dataItem = Table() | dataItem = UserVariable() ) + { dataItems = new ArrayList(); dataItems.add(dataItem); } + + ( + "," + ( dataItem = Table() | dataItem = UserVariable() ) { dataItems.add(dataItem); } + )* + ] + + { + return new ReturningClause(keyword.image, selectItems, dataItems); + } +} + +Update Update( List with ): +{ + Update update = new Update(); + Table table = null; + List startJoins = null; + + List updateSets; + Expression where = null; + FromItem fromItem = null; + List joins = null; + Limit limit = null; + List orderByElements; + boolean useColumnsBrackets = false; + ReturningClause returningClause; + Token tk = null; + UpdateModifierPriority modifierPriority = null; + boolean modifierIgnore = false; + + OutputClause outputClause = null; +} +{ + { update.setOracleHint(getOracleHint()); } + [ LOOKAHEAD(2) { modifierPriority = UpdateModifierPriority.LOW_PRIORITY; }] + [ LOOKAHEAD(2) { modifierIgnore = true; }] + table=TableWithAliasAndMysqlIndexHint() [ startJoins=JoinsList() ] + updateSets = UpdateSets() { update.setUpdateSets(updateSets); } + + [ outputClause = OutputClause() {update.setOutputClause(outputClause); } ] + + [ + fromItem=FromItem() + [ LOOKAHEAD(2) joins=JoinsList() ] ] + + [ where=WhereClause() { update.setWhere(where); } ] + + [ orderByElements = OrderByElements() { update.setOrderByElements(orderByElements); } ] + [ limit = PlainLimit() { update.setLimit(limit); } ] + [ returningClause = ReturningClause() { update.setReturningClause(returningClause); } ] + + { + return update.withWithItemsList(with) + .withTable(table) + .withStartJoins(startJoins) + .withFromItem(fromItem) + .withJoins(joins) + .withModifierPriority(modifierPriority) + .withModifierIgnore(modifierIgnore); + } +} + +List UpdateSets(): +{ + ArrayList updateSets = new ArrayList(); + UpdateSet updateSet; + Column tableColumn; + Expression valueExpression; + + ExpressionList columns; + ExpressionListvalues; +} +{ + ( + ( + tableColumn=Column() "=" valueExpression=Expression() + { updateSets.add( new UpdateSet (tableColumn, valueExpression)); } + ) + | + ( + { updateSet = new UpdateSet(); updateSets.add(updateSet); } + columns = ParenthesedExpressionList() { updateSet.setColumns(columns); } + "=" + ( + LOOKAHEAD(3) valueExpression = ParenthesedSelect() { updateSet.setValues( new ExpressionList(valueExpression)); } + | + values = ParenthesedExpressionList() { updateSet.setValues(values); } + ) + ) + ) + + ( + "," + ( + tableColumn=Column() "=" valueExpression=Expression() + { updateSets.add( new UpdateSet (tableColumn, valueExpression)); } + ) + | + ( + { updateSet = new UpdateSet(); updateSets.add(updateSet); } + columns = ParenthesedExpressionList() { updateSet.setColumns(columns); } + "=" + ( + LOOKAHEAD(3) valueExpression = ParenthesedSelect() { updateSet.setValues( new ExpressionList(valueExpression)); } + | + values = ParenthesedExpressionList() { updateSet.setValues(values); } + ) + ) + )* + + { + return updateSets; + } +} + +Insert Insert( List with ): +{ + Insert insert = new Insert(); + Table table = null; + Column tableColumn = null; + ExpressionList columns = new ExpressionList(); + Expression exp = null; + ReturningClause returningClause; + Select select = null; + boolean useDuplicate = false; + Token tk = null; + InsertModifierPriority modifierPriority = null; + boolean modifierIgnore = false; + + List updateSets; + List duplicateUpdateSets; + + String name = null; + boolean useAs = false; + OutputClause outputClause = null; + + InsertConflictTarget conflictTarget = null; + InsertConflictAction conflictAction = null; +} +{ + { insert.setOracleHint(getOracleHint()); } + + [ + LOOKAHEAD(2) (tk = | tk = | tk = ) + { + if (tk!=null) + modifierPriority = InsertModifierPriority.from(tk.image); + } + ] + [ LOOKAHEAD(2) { modifierIgnore = true; }] + [ LOOKAHEAD(2) ] table=Table() + + [ LOOKAHEAD(2) [ { useAs = true; } ] name=RelObjectNameWithoutValue() { table.setAlias(new Alias(name,useAs)); }] + + [LOOKAHEAD(2) "(" columns=ColumnList() ")" ] + + [ outputClause = OutputClause() { insert.setOutputClause(outputClause); } ] + + ( + ( + updateSets = UpdateSets() { insert.withSetUpdateSets(updateSets); } + ) + | + select = Select() + ) + + [ LOOKAHEAD(2) + duplicateUpdateSets = UpdateSets() { insert.withDuplicateUpdateSets(duplicateUpdateSets); } + ] + + [ + + [ conflictTarget = InsertConflictTarget() ] + conflictAction = InsertConflictAction() { insert.withConflictTarget(conflictTarget).setConflictAction(conflictAction); } + ] + + [ returningClause = ReturningClause() { insert.setReturningClause(returningClause); } ] + + { + if (!columns.isEmpty()) { + insert.setColumns(columns); + } + return insert.withWithItemsList(with) + .withSelect(select) + .withTable(table) + .withModifierPriority(modifierPriority) + .withModifierIgnore(modifierIgnore); + } +} + +InsertConflictTarget InsertConflictTarget(): +{ + String indexColumnName; + ArrayList indexColumnNames = new ArrayList(); + Expression indexExpression = null; + Expression whereExpression = null; + String constraintName = null ; +} +{ + ( + ( + "(" + indexColumnName = RelObjectNameExt2() { indexColumnNames.add(indexColumnName); } + ( "," indexColumnName = RelObjectNameExt2() { indexColumnNames.add(indexColumnName); } )* +// | +// ( +// "(" indexExpression = Expression() ")" +// ) + + ")" + [ whereExpression = WhereClause() ] + ) + | + ( + constraintName = RelObjectNameExt2() + ) + ) + + { return new InsertConflictTarget(indexColumnNames, indexExpression, whereExpression, constraintName); } +} + +InsertConflictAction InsertConflictAction(): +{ + InsertConflictAction conflictAction; + Expression whereExpression = null; + List updateSets; +} +{ + ( + LOOKAHEAD(2) ( + { conflictAction = new InsertConflictAction( ConflictActionType.DO_NOTHING ); } + ) + | + ( + { conflictAction = new InsertConflictAction( ConflictActionType.DO_UPDATE ); } + updateSets = UpdateSets() { conflictAction.setUpdateSets(updateSets); } + [ whereExpression = WhereClause() ] + ) + ) + + { return conflictAction + .withWhereExpression(whereExpression); } +} + +OutputClause OutputClause(): +{ + List> selectItemList = null; + UserVariable tableVariable = null; + Table outputTable = null; + List columnList = null; +} +{ + + selectItemList = SelectItemsList() + [ + ( + tableVariable = UserVariable() + | + outputTable = Table() + ) + [ + LOOKAHEAD(2) columnList = ColumnsNamesList() + ] + ] + + { + return new OutputClause(selectItemList, tableVariable, outputTable, columnList); + } +} + +Upsert Upsert(): +{ + Upsert upsert = new Upsert(); + Table table = null; + ExpressionList columns; + List updateSets; + + Select select = null; + List duplicateUpdateSets; + Token tk = null; +} +{ + ( + { upsert.setUpsertType(UpsertType.UPSERT); } + | + { upsert.setUpsertType(UpsertType.REPLACE); } + | + ( + { upsert.setUpsertType(UpsertType.INSERT_OR_REPLACE); } + ) + ) + [ LOOKAHEAD(2) { upsert.setUsingInto(true); } ] + + table=Table() { upsert.setTable(table); } + + [ LOOKAHEAD(2) columns = ParenthesedColumnList() { upsert.setColumns(columns); } ] + ( + ( + + updateSets = UpdateSets() { upsert.setUpdateSets(updateSets); } + ) + | + ( + select = Select() { upsert.setSelect(select); } + ) + ) + + [ + + duplicateUpdateSets = UpdateSets() { upsert.setDuplicateUpdateSets(duplicateUpdateSets); } + ] + + { + return upsert; + } +} + +Delete Delete( List with ): +{ + Delete delete = new Delete(); + Table table = null; + List
tables = new ArrayList
(); + Table usingTable = null; + List
usingList = new ArrayList
(); + List joins = null; + Expression where = null; + Limit limit = null; + List orderByElements; + boolean hasFrom = false; + Token tk = null; + DeleteModifierPriority modifierPriority = null; + boolean modifierIgnore = false; + boolean modifierQuick = false; + + ReturningClause returningClause; + OutputClause outputClause; +} +{ + { delete.setOracleHint(getOracleHint()); } + [ LOOKAHEAD(2) { modifierPriority = DeleteModifierPriority.LOW_PRIORITY; }] + [ LOOKAHEAD(2) { modifierQuick = true; }] + [ LOOKAHEAD(2) { modifierIgnore = true; }] + [LOOKAHEAD(4) (table=TableWithAlias() { tables.add(table); } + ("," table=TableWithAlias() { tables.add(table); } )* + [ outputClause = OutputClause() {delete.setOutputClause(outputClause); } ] + | ) { hasFrom = true; }] + + [ LOOKAHEAD(3) table=TableWithAlias() [ LOOKAHEAD(2) joins=JoinsList() ] ] + [ usingTable=TableWithAlias() { usingList.add(usingTable); } + ("," usingTable=TableWithAlias() { usingList.add(usingTable); } )*] + [where=WhereClause() { delete.setWhere(where); } ] + [orderByElements = OrderByElements() { delete.setOrderByElements(orderByElements); } ] + [limit=PlainLimit() {delete.setLimit(limit); } ] + + [ returningClause = ReturningClause() { delete.setReturningClause(returningClause); } ] + { + if (joins != null && joins.size() > 0) { + delete.setJoins(joins); + } + return delete.withWithItemsList(with) + .withTables(tables) + .withTable(table) + .withHasFrom(hasFrom) + .withUsingList(usingList) + .withModifierPriority(modifierPriority) + .withModifierIgnore(modifierIgnore) + .withModifierQuick(modifierQuick); + } +} + +Statement Merge( List with ) : { + Merge merge = new Merge(); + Table table; + FromItem fromItem; + Expression condition; + List operations; + OutputClause outputClause; +} +{ + { merge.setOracleHint(getOracleHint()); } table=TableWithAlias() { merge.setTable(table); } + fromItem = FromItem() { merge.setFromItem(fromItem); } + condition = Expression() { merge.setOnCondition(condition); } + + operations = MergeOperations() { merge.setOperations(operations); } + + [ outputClause = OutputClause() { merge.setOutputClause(outputClause); } ] + + { return merge.withWithItemsList(with); } +} + +List MergeOperations() : { + List operationsList = new ArrayList(); + MergeOperation operation; +} +{ + ( + LOOKAHEAD(2) operation = MergeWhenMatched() { operationsList.add(operation); } + | + operation = MergeWhenNotMatched() { operationsList.add(operation); } + )* + { return operationsList; } +} + +MergeOperation MergeWhenMatched() : { + Expression predicate = null; + MergeOperation operation; +} +{ + + [ predicate = Expression() ] + + ( + operation = MergeDeleteClause(predicate) | operation = MergeUpdateClause(predicate) + ) + { return operation; } +} + +MergeOperation MergeDeleteClause(Expression predicate) : { + MergeDelete md = new MergeDelete().withAndPredicate(predicate); +} +{ + { return md; } +} + +MergeOperation MergeUpdateClause(Expression predicate) : { + MergeUpdate mu = new MergeUpdate().withAndPredicate(predicate); + List updateSets; + Expression condition; +} +{ + updateSets = UpdateSets() { mu.setUpdateSets(updateSets); } + [ condition = Expression() { mu.setWhereCondition(condition); }] + [ LOOKAHEAD(2) condition = Expression() { mu.setDeleteWhereCondition(condition); } ] + { return mu; } +} + +MergeOperation MergeWhenNotMatched() : { + MergeInsert mi = new MergeInsert(); + Expression predicate; + ExpressionList columns; + ExpressionList expList; + Expression condition; +} +{ + + [ predicate = Expression() { mi.setAndPredicate(predicate); } ] + + + [ "(" columns = ColumnList() ")" + { + mi.setColumns( new ParenthesedExpressionList(columns) ); + } + ] + "(" expList = SimpleExpressionList() ")" + { + mi.setValues( new ParenthesedExpressionList(expList) ); + } + + [ condition = Expression() { mi.setWhereCondition(condition); }] + + { return mi; } +} + +ObjectNames RelObjectNames() : { + String token = null; + Token delimiter = null; + List data = new ArrayList(); + List delimiters = new ArrayList(); +} { + token = RelObjectNameExt() { data.add(token); } + ( + LOOKAHEAD (2) ( delimiter = "." | delimiter = ":" ) { delimiters.add(delimiter.image); } (( delimiter = "." | delimiter = ":" ) { data.add(null); delimiters.add(delimiter.image); })* + token = RelObjectNameExt2() { data.add(token); } + ) * + + { return new ObjectNames(data, delimiters); } +} + +// See: http://technet.microsoft.com/en-us/library/ms187879%28v=sql.105%29.aspx + +Column Column() #Column : +{ + ObjectNames data = null; + ArrayConstructor arrayConstructor = null; + Token tk = null; +} +{ + data = RelObjectNames() + [ LOOKAHEAD(2) tk= ] + // @todo: we better should return a SEQUENCE instead of a COLUMN + [ "." { data.getNames().add("nextval"); } ] + + [ LOOKAHEAD(2) arrayConstructor = ArrayConstructor(false) ] + { + Column col = new Column(data.getNames(), data.getDelimiters()); + if (tk != null) { col.withCommentText(tk.image); } + if (arrayConstructor!=null) { + col.setArrayConstructor(arrayConstructor); + } + linkAST(col,jjtThis); + return col; + } +} + +/* +The following tokens are allowed as Names for Schema, Table, Column and Aliases +*/ + +// Generated Code! Please do not edit manually. +// Instead: +// 1) define the ALL_RESERVED_KEYWORDS in the PARSER DECLARATION above (line 157 ff) +// 2) run the Gradle Task :JSQLParser:updateKeywords, which would update/replace the content of this method +String RelObjectNameWithoutValue() : +{ Token tk = null; } +{ + ( tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= + | tk="ACTION" | tk="ACTIVE" | tk="ADD" | tk="ADVANCE" | tk="ADVISE" | tk="AGAINST" | tk="ALGORITHM" | tk="ALTER" | tk="ANALYZE" | tk="APPLY" | tk="APPROXIMATE" | tk="ARCHIVE" | tk="ARRAY" | tk="ASC" | tk="AT" | tk="AUTHORIZATION" | tk="AUTO" | tk="BASE64" | tk="BEGIN" | tk="BERNOULLI" | tk="BINARY" | tk="BIT" | tk="BLOCK" | tk="BROWSE" | tk="BUFFERS" | tk="BY" | tk="BYTE" | tk="BYTES" | tk="CACHE" | tk="CALL" | tk="CASCADE" | tk="CASE" | tk="CAST" | tk="CHANGE" | tk="CHANGES" | tk="CHAR" | tk="CHARACTER" | tk="CHECKPOINT" | tk="CLOSE" | tk="COLLATE" | tk="COLUMN" | tk="COLUMNS" | tk="COMMENT" | tk="COMMIT" | tk="CONCURRENTLY" | tk="CONFLICT" | tk="CONSTRAINTS" | tk="CONVERT" | tk="COSTS" | tk="CS" | tk="CYCLE" | tk="DATA" | tk="DATABASE" | tk="DATETIME" | tk="DDL" | tk="DECLARE" | tk="DEFAULT" | tk="DEFERRABLE" | tk="DELAYED" | tk="DELETE" | tk="DESC" | tk="DESCRIBE" | tk="DISABLE" | tk="DISCONNECT" | tk="DIV" | tk="DML" | tk="DO" | tk="DOMAIN" | tk="DROP" | tk="DUMP" | tk="DUPLICATE" | tk="ELEMENTS" | tk="EMIT" | tk="ENABLE" | tk="END" | tk="ESCAPE" | tk="EXCLUDE" | tk="EXEC" | tk="EXECUTE" | tk="EXPLAIN" | tk="EXPLICIT" | tk="EXTENDED" | tk="EXTRACT" | tk="FALSE" | tk="FILTER" | tk="FIRST" | tk="FLUSH" | tk="FN" | tk="FOLLOWING" | tk="FORMAT" | tk="FULLTEXT" | tk="FUNCTION" | tk="GRANT" | tk="GUARD" | tk="HASH" | tk="HISTORY" | tk="HOPPING" | tk="INCLUDE" | tk="INCREMENT" | tk="INDEX" | tk="INSERT" | tk="INTERLEAVE" | tk="INTERPRET" | tk="INVALIDATE" | tk="ISNULL" | tk="JSON" | tk="KEEP" | tk="KEY" | tk="KEYS" | tk="LAST" | tk="LEADING" | tk="LINK" | tk="LOCAL" | tk="LOCKED" | tk="LOG" | tk="LOOP" | tk="MATCH" | tk="MATCHED" | tk="MATERIALIZED" | tk="MAX" | tk="MAXVALUE" | tk="MEMBER" | tk="MERGE" | tk="MIN" | tk="MINVALUE" | tk="MODIFY" | tk="MOVEMENT" | tk="NEXT" | tk="NO" | tk="NOCACHE" | tk="NOKEEP" | tk="NOLOCK" | tk="NOMAXVALUE" | tk="NOMINVALUE" | tk="NOORDER" | tk="NOTHING" | tk="NOTNULL" | tk="NOVALIDATE" | tk="NOWAIT" | tk="NULLS" | tk="OF" | tk="OFF" | tk="OPEN" | tk="OVER" | tk="OVERLAPS" | tk="PARALLEL" | tk="PARENT" | tk="PARTITION" | tk="PATH" | tk="PERCENT" | tk="PLACING" | tk="PRECEDING" | tk="PRECISION" | tk="PRIMARY" | tk="PRIOR" | tk="PURGE" | tk="QUERY" | tk="QUICK" | tk="QUIESCE" | tk="RANGE" | tk="RAW" | tk="READ" | tk="RECURSIVE" | tk="RECYCLEBIN" | tk="REFERENCES" | tk="REFRESH" | tk="REGEXP" | tk="REGISTER" | tk="REMOTE" | tk="RENAME" | tk="REPEATABLE" | tk="REPLACE" | tk="RESET" | tk="RESPECT" | tk="RESTART" | tk="RESTRICT" | tk="RESTRICTED" | tk="RESUMABLE" | tk="RESUME" | tk="RETURN" | tk="RLIKE" | tk="ROLLBACK" | tk="ROLLUP" | tk="ROOT" | tk="ROW" | tk="ROWS" | tk="RR" | tk="RS" | tk="SAVEPOINT" | tk="SCHEMA" | tk="SECURE" | tk="SEED" | tk="SEPARATOR" | tk="SEQUENCE" | tk="SESSION" | tk="SETS" | tk="SHARE" | tk="SHOW" | tk="SHUTDOWN" | tk="SIBLINGS" | tk="SIGNED" | tk="SIMILAR" | tk="SIZE" | tk="SKIP" | tk="STORED" | tk="STRING" | tk="STRUCT" | tk="SUSPEND" | tk="SWITCH" | tk="SYNONYM" | tk="SYSTEM" | tk="TABLE" | tk="TABLESPACE" | tk="TEMP" | tk="TEMPORARY" | tk="THEN" | tk="TIMEOUT" | tk="TIMESTAMPTZ" | tk="TIMEZONE" | tk="TO" | tk="TRIGGER" | tk="TRUE" | tk="TRUNCATE" | tk="TUMBLING" | tk="TYPE" | tk="UNLOGGED" | tk="UNQIESCE" | tk="UNSIGNED" | tk="UPDATE" | tk="UPSERT" | tk="UR" | tk="USER" | tk="VALIDATE" | tk="VERBOSE" | tk="VIEW" | tk="VOLATILE" | tk="WAIT" | tk="WITHIN" | tk="WITHOUT" | tk="WORK" | tk="XML" | tk="XMLAGG" | tk="XMLDATA" | tk="XMLSCHEMA" | tk="XMLTEXT" | tk="XSINIL" | tk="YAML" | tk="YES" | tk="ZONE" ) + { return tk.image; } +} + +/* +These tokens can be used as names for Schema and Tables and Columns +BUT NOT for Aliases (without quoting) +*/ +String RelObjectName() : +{ Token tk = null; String result = null; } +{ + (result = RelObjectNameWithoutValue() + | tk= | tk= | tk= | tk= | tk= | tk= + | tk= | tk= | tk= | tk= | tk= + | tk= + ) + + { return tk!=null ? tk.image : result; } +} + +String RelObjectNameWithoutStart() : +{ Token tk = null; String result = null; } +{ + (result = RelObjectNameWithoutValue() | tk= | tk= | tk= + | tk= ) + + { return tk!=null ? tk.image : result; } +} + +/* +Extended version of object names. + +These tokens can be used as names for Schema and Tables and Columns +BUT NOT for Aliases (without quoting) + +*/ +String RelObjectNameExt(): +{ Token tk = null; + String result=null; +} +{ + ( result=RelObjectName() | tk= | tk= | tk= | tk= | tk= | tk= + | tk= | tk= | tk= | tk= + | tk= | tk= | tk= + | tk= | tk= | tk= | tk= + | tk= + | tk= + ) + { return tk!=null ? tk.image : result; } +} + +/* +Extended usage of object names - part 2. Using within multipart names as following parts. + +These tokens can be used as names for Tables and Columns +BUT NOT for Schema or Aliases (without quoting) + +*/ +String RelObjectNameExt2(): +{ Token tk = null; + String result=null; +} +{ + ( result=RelObjectNameExt() | tk= | tk= | tk= ) + { return tk!=null ? tk.image : result; } +} + +Table Table() #TableName : +{ + //String serverName = null, databaseName = null, schemaName = null, tableName = null; + ObjectNames data = null; +} +{ + data = RelObjectNames() + + { + Table table = new Table(data.getNames()); + linkAST(table,jjtThis); + return table; + } +} + +Table TableWithAlias(): +{ + Table table = null; + Alias alias = null; +} +{ + table=Table() + [ LOOKAHEAD(2) alias=Alias() { table.setAlias(alias); }] + { return table; } +} + +Table TableWithAliasAndMysqlIndexHint(): +{ + Table table = null; + Alias alias = null; + MySQLIndexHint indexHint = null; +} +{ + table=Table() + [ LOOKAHEAD(2) alias=Alias() { table.setAlias(alias); } ] + [ LOOKAHEAD(2) indexHint=MySQLIndexHint() { table.setHint(indexHint); } ] + { return table; } +} + +Number Number(): +{ + Token token; + Number number; +} +{ + ( + token = { number = Double.valueOf(token.image); } + | + token = { number = Long.valueOf(token.image); } + ) + + { + return number; + } +} + +SampleClause SampleClause(): +{ + Token token; + SampleClause sampleClause; + String keyword; + String method=null; + Number percentageArgument; + Number repeatArgument=null; + Number seedArgument=null; +} +{ + ( + ( + // Oracle + token= { keyword = token.image; } + [ token= { method = token.image; } ] + ) + | + ( + // SQL:2016 compliant + token = { keyword = token.image; } + ( token = | token = ) { method = token.image; } + ) + ) + + "(" percentageArgument = Number() ")" + + [ LOOKAHEAD(2) "(" repeatArgument = Number() ")" ] + + [ LOOKAHEAD(2) "(" seedArgument = Number() ")" ] + + { + return new SampleClause(keyword, method, percentageArgument, repeatArgument, seedArgument); + } +} + +Select SelectWithWithItems( List withItems): +{ + Select select; +} +{ + select = Select() { select.setWithItemsList( withItems ); + return select; +} +} + +Select Select() #Select: +{ + Select select = null; + List with = null; + List orderByElements = null; + Limit limit = null; + Offset offset = null; + Fetch fetch = null; + WithIsolation withIsolation = null; +} +{ + [ with=WithList() ] + ( + LOOKAHEAD(3) select = PlainSelect() + | + LOOKAHEAD(3) select = Values() + | + LOOKAHEAD(3) select = ParenthesedSelect() + ) + [ LOOKAHEAD(2) select = SetOperationList(select) ] + + [ LOOKAHEAD( ) orderByElements = OrderByElements() { select.setOrderByElements(orderByElements); } ] + + [ LOOKAHEAD() limit=LimitWithOffset() {select.setLimit(limit);} ] + [ LOOKAHEAD() offset = Offset() { select.setOffset(offset);} ] + [ LOOKAHEAD() fetch = Fetch() { select.setFetch(fetch);} ] + [ LOOKAHEAD( ) withIsolation = WithIsolation() { select.setIsolation(withIsolation);} ] + + { + linkAST(select, jjtThis); + return select.withWithItemsList(with); + } +} + +TableStatement TableStatement(): +{ + Table table = null; + List orderByElements = null; + Limit limit = null; + Offset offset = null; + TableStatement tableStatement = new TableStatement(); +}{ + + table = Table() + { tableStatement.setTable(table); } + [ LOOKAHEAD( ) orderByElements = OrderByElements() { tableStatement.setOrderByElements(orderByElements); } ] + [ LOOKAHEAD() limit=LimitWithOffset() { tableStatement.setLimit(limit);} ] + [ LOOKAHEAD() offset = Offset() { tableStatement.setOffset(offset);} ] + { return tableStatement; } + /* Support operationList */ +} + +ParenthesedSelect ParenthesedSelect() #ParenthesedSelect: +{ + ParenthesedSelect parenthesedSelect = new ParenthesedSelect(); + Select select; +} +{ + "(" + select = Select() + ")" + { + linkAST(parenthesedSelect,jjtThis); + return parenthesedSelect.withSelect(select); + } +} + +LateralView LateralView() #LateralView: +{ + boolean useOuter = false; + Function generatorFunction = null; + String tableName = null; + String columnName = null; + Alias tableAlias = null; + Alias columnAlias = null; +} +{ + [ { useOuter=true; } ] + generatorFunction = Function() + [ LOOKAHEAD(2) + tableName=RelObjectNameWithoutStart() + { + tableAlias = new Alias(tableName, false); + } + ] + columnName = RelObjectNameWithoutStart() + { + columnAlias = new Alias(columnName, true); + return new LateralView( + useOuter + , generatorFunction + , tableAlias + , columnAlias + ); + } +} + +ForClause ForClause() #ForClause: +{ + Token token = null; + ForClause forClause = new ForClause(); +} +{ + + ( + token = + | + token = + ( + ( + ( [ LOOKAHEAD(2) "(" ")" ] | ) + ( + LOOKAHEAD(2) "," + ( + + | + | [ LOOKAHEAD(2) "(" ")" ] + | + | [ LOOKAHEAD(2) "(" ")" ] + | [ LOOKAHEAD(2) ( | ) ] + ) + )* + ) + | + ( + + ( + LOOKAHEAD(2) "," + ( + + | + | [ LOOKAHEAD(2) "(" ")" ] + | + ) + )* + ) + | + ( + [ LOOKAHEAD(2) "(" ")" ] + ( + LOOKAHEAD(2) "," + ( + + | + | [ LOOKAHEAD(2) "(" ")" ] + | [ LOOKAHEAD(2) ( | ) ] + ) + )* + ) + ) + | + ( + token = ( | ) + ( + LOOKAHEAD(2) "," + ( + [ LOOKAHEAD(2) "(" ")" ] + | + | + ) + )* + ) + ) + { + forClause.setForOption(token.image); + linkAST(forClause,jjtThis); + return forClause; + } +} + + +List LateralViews(): +{ + ArrayList lateralViews = new ArrayList(); + LateralView lateralView = null; +} +{ + lateralView = LateralView() { lateralViews.add(lateralView); } + ( lateralView = LateralView() { lateralViews.add(lateralView); } )* + + { + return lateralViews; + } +} + +LateralSubSelect LateralSubSelect() #LateralSubSelect: +{ + LateralSubSelect lateralSubSelect = new LateralSubSelect();; + Select select; +} +{ + "(" select = Select() ")" { lateralSubSelect.withSelect(select).setPrefix("LATERAL"); } + { + linkAST(lateralSubSelect,jjtThis); + return lateralSubSelect; + } +} + +PlainSelect PlainSelect() #PlainSelect: +{ + PlainSelect plainSelect = new PlainSelect(); + List> selectItems = null; + FromItem fromItem = null; + List lateralViews = null; + List joins = null; + List> distinctOn = null; + Expression where = null; + ForClause forClause = null; + List orderByElements; + GroupByElement groupBy = null; + Expression having = null; + Expression qualify; + Limit limitBy = null; + Limit limit = null; + Offset offset = null; + Fetch fetch = null; + WithIsolation withIsolation = null; + OptimizeFor optimize = null; + Top top = null; + Skip skip = null; + First first = null; + OracleHierarchicalExpression oracleHierarchicalQueryClause = null; + List
intoTables = null; + Table updateTable = null; + Wait wait = null; + boolean mySqlSqlCalcFoundRows = false; + Token token; + KSQLWindow ksqlWindow = null; + boolean noWait = false; + String windowName = null; + WindowDefinition winDef; + Table intoTempTable = null; +} +{ + + + [ { plainSelect.setMySqlHintStraightJoin(true); } ] + + { plainSelect.setOracleHint(getOracleHint()); } + + [ LOOKAHEAD(2) skip = Skip() { plainSelect.setSkip(skip); } ] + + [ LOOKAHEAD(2) first = First() { plainSelect.setFirst(first); } ] + + // Redshift allows TOP before DISTINCT + // https://docs.aws.amazon.com/redshift/latest/dg/r_SELECT_list.html + // @Todo: reflect the order when de-parsing + [ LOOKAHEAD(2) top = Top() { plainSelect.setTop(top); } ] + + [ LOOKAHEAD(2) + ( + + | + ( + { Distinct distinct = new Distinct(); plainSelect.setDistinct(distinct); } + [ LOOKAHEAD(2) "ON" "(" distinctOn=SelectItemsList() { plainSelect.getDistinct().setOnSelectItems(distinctOn); } ")" ] + ) + | + { Distinct distinct = new Distinct(true); plainSelect.setDistinct(distinct); } + | + { plainSelect.setMySqlSqlCalcFoundRows(true); } + | + { plainSelect.setMySqlSqlCacheFlag(MySqlSqlCacheFlags.SQL_NO_CACHE); } + | + { plainSelect.setMySqlSqlCacheFlag(MySqlSqlCacheFlags.SQL_CACHE); } + ) + ] + + [ + + ( + { plainSelect.setBigQuerySelectQualifier( PlainSelect.BigQuerySelectQualifier.AS_STRUCT ); } + | + { plainSelect.setBigQuerySelectQualifier( PlainSelect.BigQuerySelectQualifier.AS_VALUE ); } + ) + ] + + [ LOOKAHEAD(2) top = Top() { plainSelect.setTop(top); } ] + + selectItems=SelectItemsList() + + [ LOOKAHEAD(2) intoTables = IntoClause() { plainSelect.setIntoTables(intoTables); } ] + [ LOOKAHEAD(2) fromItem=FromItem() + [ lateralViews=LateralViews() ] + [ LOOKAHEAD(2) joins=JoinsList() ] + ] + [ LOOKAHEAD(3) { plainSelect.setUsingOnly(true); } fromItem=FromItem() + [ lateralViews=LateralViews() ] + [ LOOKAHEAD(2) joins=JoinsList() ] + ] + + // Clickhouse FINAL as shown at https://clickhouse.com/docs/en/operations/settings/settings#final + [ LOOKAHEAD(2) { plainSelect.setUsingFinal(true); } ] + + [ LOOKAHEAD(2) ksqlWindow=KSQLWindowClause() { plainSelect.setKsqlWindow(ksqlWindow); } ] + [ LOOKAHEAD(2) where=WhereClause() { plainSelect.setWhere(where); }] + [ LOOKAHEAD(2) oracleHierarchicalQueryClause=OracleHierarchicalQueryClause() { plainSelect.setOracleHierarchical(oracleHierarchicalQueryClause); } ] + // Oracle supports "HAVING" before "GROUP BY", we will simply parse that but won't pay special attention to the order + [ LOOKAHEAD(2) having=Having() { plainSelect.setHaving(having); }] + [ LOOKAHEAD(2) groupBy=GroupByColumnReferences() { plainSelect.setGroupByElement(groupBy); }] + [ LOOKAHEAD(2) having=Having() { plainSelect.setHaving(having); }] + [ LOOKAHEAD(2) qualify=Qualify() {plainSelect.setQualify(qualify); }] + [ LOOKAHEAD(2) forClause = ForClause() {plainSelect.setForClause(forClause);} ] + [ LOOKAHEAD( ) orderByElements = OrderByElements() { plainSelect.setOracleSiblings(true); plainSelect.setOrderByElements(orderByElements); } ] + [ LOOKAHEAD(2) + windowName = RelObjectName() winDef = windowDefinition() { List winDefs = new ArrayList(); winDefs.add(winDef.withWindowName(windowName)); } + ( LOOKAHEAD(2) "," windowName = RelObjectName() winDef = windowDefinition() { winDefs.add(winDef.withWindowName(windowName)); } )* + { plainSelect.setWindowDefinitions(winDefs); } + ] + [ LOOKAHEAD( ) orderByElements = OrderByElements() { plainSelect.setOrderByElements(orderByElements); } ] + [ LOOKAHEAD(2) { plainSelect.setEmitChanges(true); } ] + [ LOOKAHEAD(LimitBy()) limit = LimitBy() { plainSelect.setLimitBy(limit); } ] + [ LOOKAHEAD() limit = LimitWithOffset() { plainSelect.setLimit(limit); } ] + [ LOOKAHEAD() offset = Offset() { plainSelect.setOffset(offset); } ] + [ LOOKAHEAD(, { limit==null }) limit = LimitWithOffset() { plainSelect.setLimit(limit); } ] + [ LOOKAHEAD() fetch = Fetch() { plainSelect.setFetch(fetch); } ] + [ LOOKAHEAD( ) withIsolation = WithIsolation() { plainSelect.setIsolation(withIsolation); } ] + [ LOOKAHEAD(2) + + ( + { plainSelect.setForMode(ForMode.UPDATE); } + | { plainSelect.setForMode(ForMode.SHARE); } + | ( { plainSelect.setForMode(ForMode.NO_KEY_UPDATE); }) + | ( { plainSelect.setForMode(ForMode.KEY_SHARE); }) + ) + [ LOOKAHEAD(2) updateTable = Table() { plainSelect.setForUpdateTable(updateTable); } ] + [ LOOKAHEAD() wait = Wait() { plainSelect.setWait(wait); } ] + [ LOOKAHEAD(2) ( { plainSelect.setNoWait(true); } + | { plainSelect.setSkipLocked(true); }) ] + ] + [ LOOKAHEAD() optimize = OptimizeFor() { plainSelect.setOptimizeFor(optimize); } ] + [ LOOKAHEAD(3) intoTempTable = Table() { plainSelect.setIntoTempTable(intoTempTable);} ] + [ LOOKAHEAD(3) { plainSelect.setUseWithNoLog(true); } ] + { + plainSelect.setSelectItems(selectItems); + plainSelect.setFromItem(fromItem); + if ( lateralViews!=null && lateralViews.size()>0 ) { + plainSelect.setLateralViews( lateralViews ); + } + if ( joins!=null && joins.size()>0 ) { + plainSelect.setJoins( joins ); + } + linkAST(plainSelect,jjtThis); + return plainSelect; + } +} + +Select SetOperationList(Select select) #SetOperationList: { + SetOperationList list = new SetOperationList(); + List orderByElements = null; + Limit limit = null; + Offset offset = null; + Fetch fetch = null; + WithIsolation withIsolation = null; + List(); + List operations = new ArrayList(); +} +{ + + { + selects.add(select); + } + + ( LOOKAHEAD(2) ( + ( + { UnionOp union = new UnionOp(); linkAST(union,jjtThis); operations.add(union); } + [ { union.setAll(true); } | { union.setDistinct(true); } ] + ) + | + { operations.add(new IntersectOp()); } + | + { operations.add(new MinusOp()); } + | + { operations.add(new ExceptOp()); } + ) + + ( + select = PlainSelect() + | + select = Values() + | + select = ParenthesedSelect() + ) + { + selects.add(select); + } + )+ + + [ LOOKAHEAD(2) orderByElements=OrderByElements() {list.setOrderByElements(orderByElements);} ] + [ LOOKAHEAD() limit = LimitWithOffset() { list.setLimit(limit); } ] + [ LOOKAHEAD() offset = Offset() { list.setOffset(offset); } ] + [ LOOKAHEAD(, { limit==null }) limit = LimitWithOffset() { list.setLimit(limit); } ] + [ LOOKAHEAD() fetch = Fetch() { list.setFetch(fetch); } ] + [ LOOKAHEAD( ) withIsolation = WithIsolation() { list.setIsolation(withIsolation); } ] + + { + if ( selects.get(selects.size()-1) instanceof PlainSelect ) { + PlainSelect ps = (PlainSelect)selects.get(selects.size()-1); + if (ps.getOrderByElements() != null) { + list.setOrderByElements(ps.getOrderByElements()); + list.setLimit(ps.getLimit()); + list.setOffset(ps.getOffset()); + ps.setOrderByElements(null); + ps.setLimit(null); + ps.setOffset(null); + } + if (ps.getFetch() != null) { + list.setFetch(ps.getFetch()); + ps.setFetch(null); + } + if (ps.getIsolation() != null) { + list.setIsolation(ps.getIsolation()); + ps.setIsolation(null); + } + } + list.setBracketsOpsAndSelects(selects,operations); + return list; + } +} + +List WithList(): +{ + List withItemsList = new ArrayList(); + WithItem with = null; +} +{ + with=WithItem() { withItemsList.add(with); } ("," with=WithItem() { withItemsList.add(with); } )* + + { return withItemsList; } +} + +WithItem WithItem() #WithItem: +{ + WithItem withItem = new WithItem(); + String name; + List> selectItems; + Select select; +} +{ + [ LOOKAHEAD(2) { withItem.setRecursive(true); } ] + name=RelObjectName() { withItem.setAlias( new Alias( name, false)); } + [ "(" selectItems=SelectItemsList() ")" { withItem.setWithItemList(selectItems); } ] + select = ParenthesedSelect() { withItem.setSelect(select); } + { + return withItem; + } +} + +List> ColumnSelectItemsList(): +{ + List> selectItemsList = null; + SelectItem selectItem = null; +} +{ + selectItem=SelectItem() { selectItemsList = new ArrayList>(); selectItemsList.add(selectItem); } + ( + LOOKAHEAD(2) "," selectItem=SelectItem() + { + selectItemsList.add(selectItem); + } + )* + + { return selectItemsList; } +} + +List> SelectItemsList(): +{ + List> selectItemsList = null; + SelectItem selectItem = null; +} +{ + selectItem=SelectItem() { selectItemsList = new ArrayList>(); selectItemsList.add(selectItem); } + ( + LOOKAHEAD(2) "," selectItem=SelectItem() + { + selectItemsList.add(selectItem); + } + )* + + { return selectItemsList; } +} + + +SelectItem SelectItem() #SelectItem: +{ + Expression expression; + Alias alias = null; +} +{ + // @fixme: Oracle's SEQUENCE.nextval is parsed as COLUMN with a name part nextval + // @todo: parse a proper SEQUENCE instead of a COLUMN + ( + expression = AllColumns() + | + LOOKAHEAD(AllTableColumns()) expression = AllTableColumns() + | + LOOKAHEAD( 3 ) expression = XorExpression() + | + LOOKAHEAD( 3 ) expression = ConcatExpression() + | + expression=Expression() + ) + [ LOOKAHEAD(2) alias=Alias() ] + { + SelectItem selectItem = new SelectItem(expression, alias); + linkAST(selectItem,jjtThis); + return selectItem; + } +} + +AllColumns AllColumns(): +{ + ParenthesedExpressionList exceptColumns = null; + List> replaceExpressions = null; + String exceptKeyword=null; + Token tk; +} +{ + "*" + [ LOOKAHEAD(2) ( tk= | tk= ) exceptColumns = ParenthesedColumnList() { exceptKeyword=tk.image; } ] + [ LOOKAHEAD(2) "(" replaceExpressions = ColumnSelectItemsList() ")" ] + + { + return new AllColumns(exceptColumns, replaceExpressions, exceptKeyword); + } +} + +AllTableColumns AllTableColumns(): +{ + Table table = null; + AllColumns allColumns; +} +{ + table=Table() "." allColumns=AllColumns() + { + return new AllTableColumns(table, allColumns); + } + +} + +Alias Alias(): +{ String name = ""; + Token token = null; + boolean useAs = false; + Alias alias; + String colname; + ColDataType colDataType = null; +} +{ + ( + LOOKAHEAD(3) ( + // Aliases with AS and Columns, but optional identifier: + // SELECT fun(x) AS (a,b,c) + // SELECT fun(x) AS T(a,b,c) + + [ LOOKAHEAD(2) name=RelObjectNameWithoutStart() ] + { alias = new Alias(name, true ); } + + "(" { List list = new ArrayList(); } + colname = RelObjectName() [ colDataType = ColDataType() ] { list.add(new Alias.AliasColumn(colname, colDataType)); } + ( + "," { colDataType=null; } colname = RelObjectName() [ colDataType = ColDataType()] { list.add(new Alias.AliasColumn(colname, colDataType)); } + )* + ")" { alias.setAliasColumns(list); } + + ) + | + ( + // Aliases with identifier but optional AS and Columns: + // SELECT fun(x) AS T + // SELECT fun(x) T + // SELECT fun(x) T(a,b,c) + + [ { useAs = true; } ] + ( name=RelObjectNameWithoutStart() | token= { name=token.image; } ) + { alias = new Alias(name,useAs); } + + [ LOOKAHEAD(2) "(" { List list = new ArrayList(); } + colname = RelObjectName() [ colDataType = ColDataType() ] { list.add(new Alias.AliasColumn(colname, colDataType)); } + ("," { colDataType=null; } colname = RelObjectName() [ colDataType = ColDataType()] { list.add(new Alias.AliasColumn(colname, colDataType)); } )* + ")" { alias.setAliasColumns(list); } ] + + ) + ) + { return alias; } +} + +void SQLServerHint(SQLServerHints hints) : { + String str; +} +{ + "(" str = RelObjectName() ")" { hints.setIndexName(str); } + | + { hints.withNoLock(); } +} + +SQLServerHints SQLServerHints() : { + SQLServerHints hints = new SQLServerHints(); +} +{ + "(" + SQLServerHint(hints) ("," SQLServerHint(hints) )* + ")" + { return hints; } +} + +MySQLIndexHint MySQLIndexHint(): +{ + Token actionToken = null; + Token indexToken = null; + String indexName = null; + List indexNameList = new ArrayList(); +} +{ + ( + actionToken = + | actionToken = + | actionToken = + | actionToken = + ) + + ( + indexToken = + | indexToken = + ) + + "(" + indexName = RelObjectNameWithoutValue() { indexNameList.add(indexName); } + ("," indexName= RelObjectNameWithoutValue() { indexNameList.add(indexName); })* + ")" + { + return new MySQLIndexHint(actionToken.image, indexToken.image, indexNameList); + } +} + +SelectItem FunctionItem(): +{ + Alias alias = null; + Function function; +} +{ + function=Function() + [ alias=Alias() ] + { return new SelectItem(function, alias); } +} + +ExpressionList PivotForColumns(): +{ + ExpressionList columns; + Column column; +} +{ + ( + columns = ParenthesedColumnList() + | + column = Column() { columns = new ExpressionList(column); } + ) + { return columns; } +} + +List> PivotFunctionItems(): +{ + List> functionItems = new ArrayList>(); + SelectItem item; +} +{ + item = FunctionItem() {functionItems.add(item);} + ( "," item = FunctionItem() {functionItems.add(item);} )* + { return functionItems; } +} + +SelectItem> ExpressionListItem(): +{ + ExpressionList expressionList; + Alias alias = null; +} +{ + expressionList=ParenthesedExpressionList() + [ alias=Alias() ] + { return new SelectItem>(expressionList, alias); } +} + +List>> PivotMultiInItems(): +{ + List>> retval = new ArrayList>>(); + SelectItem> item; +} +{ + item = ExpressionListItem() {retval.add(item);} + ("," item = ExpressionListItem() {retval.add(item);} )* + { return retval; } +} + +Pivot Pivot(): +{ + Pivot retval = new Pivot(); + List> functionItems; + ExpressionList forColumns; + List> singleInItems = null; + List>> multiInItems = null; + Alias alias = null; +} +{ + "(" functionItems = PivotFunctionItems() + forColumns = PivotForColumns() + "(" + (LOOKAHEAD(3) singleInItems = SelectItemsList() + | multiInItems = PivotMultiInItems() ) + ")" + ")" + [ LOOKAHEAD(2) alias = Alias() ] + { + retval.setFunctionItems(functionItems); + retval.setForColumns(forColumns); + retval.setSingleInItems(singleInItems); + retval.setMultiInItems(multiInItems); + retval.setAlias(alias); + return retval; + } +} + +PivotXml PivotXml(): +{ + PivotXml retval = new PivotXml(); + List> functionItems; + ExpressionList forColumns; + List> singleInItems = null; + List>> multiInItems = null; + Select inSelect = null; +} +{ + "(" functionItems = PivotFunctionItems() + forColumns = PivotForColumns() + "(" + ( + LOOKAHEAD(2) { retval.setInAny(true); } | + LOOKAHEAD(1) inSelect = Select() | + LOOKAHEAD(2) singleInItems =SelectItemsList() | + multiInItems = PivotMultiInItems() + ) + ")" + ")" + { + retval.setFunctionItems(functionItems); + retval.setForColumns(forColumns); + retval.setSingleInItems(singleInItems); + retval.setMultiInItems(multiInItems); + retval.setInSelect(inSelect); + return retval; + } +} + +UnPivot UnPivot(): +{ + UnPivot retval = new UnPivot(); + ExpressionList unpivotClause; + ExpressionList unpivotForClause; + List> unpivotInClause; + Alias alias = null; +} +{ + + [ ( { retval.setIncludeNulls(true); } + | { retval.setIncludeNulls(false); } ) ] + "(" unpivotClause = PivotForColumns() + unpivotForClause = PivotForColumns() + "(" + unpivotInClause = SelectItemsList() + ")" + ")" + [ LOOKAHEAD(2) alias = Alias() ] + { + retval.setUnPivotClause(unpivotClause); + retval.setUnPivotForClause(unpivotForClause); + retval.setUnPivotInClause(unpivotInClause); + retval.setAlias(alias); + return retval; + } +} + +List
IntoClause(): +{ + List
tables = new ArrayList
(); + Table table; +} +{ + table=Table() { tables.add(table); } ( LOOKAHEAD(2) "," table=Table() { tables.add(table); } )* + { + return tables; + } +} + +FromItem ParenthesedFromItem(): +{ + ParenthesedFromItem ParenthesedFromItem = new ParenthesedFromItem(); + FromItem fromItem; + List joins = null; +} +{ + "(" + fromItem = FromItem() + [ joins=JoinsList() ] + ")" + + { + return ParenthesedFromItem.withFromItem(fromItem).withJoins(joins); + } +} + +FromItem FromItem() #FromItem: +{ + FromItem fromItem = null; + FromItem fromItem2 = null; + SampleClause sampleClause; + Pivot pivot = null; + UnPivot unpivot = null; + Alias alias = null; + MySQLIndexHint indexHint = null; + SQLServerHints sqlServerHints = null; + Select select; +} +{ + ( + LOOKAHEAD(3) fromItem = Values() + | + LOOKAHEAD( TableFunction() ) fromItem=TableFunction() + | + LOOKAHEAD(3) fromItem=Table() + | + LOOKAHEAD( ParenthesedFromItem() ) fromItem = ParenthesedFromItem() + | + LOOKAHEAD(3) ( + fromItem=ParenthesedSelect() + [ LOOKAHEAD(2) pivot=Pivot() { fromItem.setPivot(pivot); } ] + [ LOOKAHEAD(2) unpivot=UnPivot() { fromItem.setUnPivot(unpivot); } ] + ) + | + fromItem=LateralSubSelect() + ) + + [ LOOKAHEAD(2) alias=Alias() { fromItem.setAlias(alias); } ] + [ LOOKAHEAD(2) sampleClause = SampleClause() { ((Table) fromItem).setSampleClause(sampleClause); } ] + [ LOOKAHEAD(2) unpivot=UnPivot() { fromItem.setUnPivot(unpivot); } ] + [ LOOKAHEAD(2) ( LOOKAHEAD(2) pivot=PivotXml() | pivot=Pivot() ) { fromItem.setPivot(pivot); } ] + [ + LOOKAHEAD(2) + ( + indexHint = MySQLIndexHint() { + if (fromItem instanceof Table) + ((Table) fromItem).setHint(indexHint); + } + | + sqlServerHints = SQLServerHints() { + if (fromItem instanceof Table) + ((Table) fromItem).setSqlServerHints(sqlServerHints); + } + ) + ] + { + linkAST(fromItem,jjtThis); + return fromItem; + } +} + +List JoinsList(): +{ + List joinsList = new ArrayList(); + Join join = null; +} +{ + ( LOOKAHEAD(2) join=JoinerExpression() { joinsList.add(join); } )+ + { + return joinsList; + } +} + +JoinHint JoinHint(): +{ + Token token; +} +{ + ( + token = + | + token = + | + token = + | + token = + ) + { + return new JoinHint(token.image); + } +} + +Join JoinerExpression() #JoinerExpression: +{ + Join join = new Join(); + FromItem right = null; + Expression onExpression = null; + Column tableColumn; + List columns = null; + KSQLJoinWindow joinWindow = null; + JoinHint joinHint = null; +} +{ + [ { join.setGlobal(true); } ] + [ { join.setNatural(true); } ] + + [ + ( + { join.setLeft(true); } [ { join.setSemi(true); } | { join.setOuter(true); } ] + | + ( + { join.setRight(true); } + | + { join.setFull(true); } + ) [ { join.setOuter(true); } ] + | + { join.setInner(true); } + ) + | + { join.setCross(true); } + | + { join.setOuter(true); } + ] + + ( + ( [ joinHint=JoinHint() {join.setJoinHint(joinHint); } ] ) + | + "," { join.setSimple(true); } ( { join.setOuter(true); } )? + | + { join.setStraight(true); } + | + {join.setApply(true); } + ) + + right=FromItem() + + [ + LOOKAHEAD(2) ( + [ "(" joinWindow = JoinWindow() ")" {join.setJoinWindow(joinWindow);} ] + ( onExpression=Expression() { join.addOnExpression(onExpression); } + ( LOOKAHEAD(2) onExpression=Expression() { join.addOnExpression(onExpression); } )* + ) + | + ( + "(" tableColumn=Column() { columns = new ArrayList(); columns.add(tableColumn); } + ( "," tableColumn=Column() { columns.add(tableColumn); } ) * + ")" { join.setUsingColumns(columns); } + ) + ) + ] + { + linkAST(join,jjtThis); + join.setFromItem(right); + return join; + } + +} + +KSQLJoinWindow JoinWindow(): +{ + KSQLJoinWindow retval = new KSQLJoinWindow(); + boolean beforeAfter; + Token beforeDurationToken = null; + Token beforeTimeUnitToken = null; + Token afterDurationToken = null; + Token afterTimeUnitToken = null; +} +{ + beforeDurationToken= (beforeTimeUnitToken= | beforeTimeUnitToken=) + [ "," afterDurationToken= (afterTimeUnitToken= | afterTimeUnitToken=) ] + { + if (afterDurationToken == null) { + retval.setDuration(Long.parseLong(beforeDurationToken.image)); + retval.setTimeUnit(KSQLWindow.TimeUnit.from(beforeTimeUnitToken.image)); + retval.setBeforeAfterWindow(false); + return retval; + } + retval.setBeforeDuration(Long.parseLong(beforeDurationToken.image)); + retval.setBeforeTimeUnit(KSQLWindow.TimeUnit.from(beforeTimeUnitToken.image)); + retval.setAfterDuration(Long.parseLong(afterDurationToken.image)); + retval.setAfterTimeUnit(KSQLWindow.TimeUnit.from(afterTimeUnitToken.image)); + retval.setBeforeAfterWindow(true); + return retval; + } +} + +KSQLWindow KSQLWindowClause(): +{ + KSQLWindow retval = null; + Token sizeDurationToken = null; + Token sizeTimeUnitToken = null; + Token advanceDurationToken = null; + Token advanceTimeUnitToken = null; +} +{ + + { + retval=new KSQLWindow(); + retval.setHoppingWindow(false); + retval.setSessionWindow(false); + retval.setTumblingWindow(false); + } + ( + "(" + sizeDurationToken= sizeTimeUnitToken= "," + advanceDurationToken= advanceTimeUnitToken= ")" + { + retval.setHoppingWindow(true); + } | + "(" sizeDurationToken= sizeTimeUnitToken= ")" + { + retval.setSessionWindow(true); + } | + "(" sizeDurationToken= sizeTimeUnitToken= ")" + { + retval.setTumblingWindow(true); + } + ) + { + retval.setSizeDuration(Long.parseLong(sizeDurationToken.image)); + retval.setSizeTimeUnit(KSQLWindow.TimeUnit.from(sizeTimeUnitToken.image)); + if (advanceDurationToken != null) { + retval.setAdvanceDuration(Long.parseLong(advanceDurationToken.image)); + retval.setAdvanceTimeUnit(KSQLWindow.TimeUnit.from(advanceTimeUnitToken.image)); + } + return retval; + } +} + +Expression WhereClause(): +{ + Expression retval = null; +} +{ + retval=Expression() + { return retval; } +} + +OracleHierarchicalExpression OracleHierarchicalQueryClause(): +{ + OracleHierarchicalExpression result = new OracleHierarchicalExpression(); + Expression expr; +} +{ + ( + ( + expr=AndExpression() {result.setStartExpression(expr);} + [ { result.setNoCycle(true); } ] expr=AndExpression() + { + result.setConnectExpression(expr); + } + ) + | + ( + [ { result.setNoCycle(true); } ] expr=AndExpression() + { + result.setConnectExpression(expr); + result.setConnectFirst(true); + } + [ LOOKAHEAD(2) expr=AndExpression() {result.setStartExpression(expr);} ] + ) + ) + { + return result; + } +} + +GroupByElement GroupByColumnReferences(): +{ + Expression columnReference; + GroupByElement groupBy = new GroupByElement(); + Expression expr; + ExpressionList list; + Token token; +} +{ + + ( + LOOKAHEAD(2) ( + + "(" + list = GroupingSet() { groupBy.addGroupingSet(list); } + ( LOOKAHEAD(2) "," list = GroupingSet() { groupBy.addGroupingSet(list); })* + ")" + ) + | + ( + list = ExpressionList() { groupBy.setGroupByExpressions(list); } + ( + LOOKAHEAD(2) + "(" + list = GroupingSet() { groupBy.addGroupingSet(list); } + ( LOOKAHEAD(2) "," list = GroupingSet() { groupBy.addGroupingSet(list); })* + ")" + )? + [ LOOKAHEAD(2) { groupBy.setMysqlWithRollup(true); } ] + ) + ) + { + return groupBy; + } +} + +ExpressionList GroupingSet(): +{ + ExpressionList list; + Expression expression; +} +{ + ( + LOOKAHEAD(2) list = ParenthesedExpressionList() + | + expression = SimpleExpression() { list = new ExpressionList(expression); } + ) + { + return list; + } +} + +Expression Having(): +{ + Expression having = null; +} +{ + having=Expression() + { + return having; + } +} + +Expression Qualify(): +{ + Expression qualify = null; +} +{ + qualify=Expression() + { + return qualify; + } +} + +List OrderByElements(): +{ + List orderByList = new ArrayList(); + OrderByElement orderByElement = null; +} +{ + [ ] orderByElement=OrderByElement() { orderByList.add(orderByElement); } + ( LOOKAHEAD(2) "," orderByElement=OrderByElement() { orderByList.add(orderByElement); } )* + { + return orderByList; + } +} + +OrderByElement OrderByElement(): +{ + OrderByElement orderByElement = new OrderByElement(); + Expression columnReference = null; +} +{ + columnReference = Expression() + [ LOOKAHEAD(2) ( | ( { orderByElement.setAsc(false); } )) { orderByElement.setAscDescPresent(true); } ] + [ LOOKAHEAD(2) + [ LOOKAHEAD(2) ( + { orderByElement.setNullOrdering(OrderByElement.NullOrdering.NULLS_FIRST); } + | + { orderByElement.setNullOrdering(OrderByElement.NullOrdering.NULLS_LAST); } + ) + ] + ] + [ LOOKAHEAD(2) { orderByElement.setMysqlWithRollup(true); } ] + { + orderByElement.setExpression(columnReference); + return orderByElement; + } +} + +JdbcParameter JdbcParameter() : { + Token tk; + JdbcParameter retval; +} +{ + ( tk="?" | tk= ) + { retval = new JdbcParameter(++jdbcParameterIndex, false, tk.image); } + + [ LOOKAHEAD(2) token = { retval.setUseFixedIndex(true); retval.setIndex(Integer.valueOf(token.image)); } ] + + { return retval; } +} + + +Limit LimitWithOffset() #LimitWithOffset: +{ + Limit limit = new Limit(); + Expression rowCountExpression; + Expression offsetExpression; +} +{ + ( + LOOKAHEAD( Expression() "," Expression()) ( + // mysql-> LIMIT offset,row_count + + offsetExpression=Expression() { limit.setOffset( offsetExpression ); } + "," + rowCountExpression=Expression() { limit.setRowCount( rowCountExpression ); } + ) + | + limit = PlainLimit() + ) + { + linkAST(limit,jjtThis); + return limit; + } +} + +Limit PlainLimit() #PlainLimit: +{ + Limit limit = new Limit(); + Expression rowCountExpression; +} +{ + // mysql-postgresql-> LIMIT (row_count | ALL | NULL) + + ( + LOOKAHEAD(3) rowCountExpression = ParenthesedSelect() + | + rowCountExpression = Expression() + ) + { + limit.setRowCount(rowCountExpression); + linkAST(limit,jjtThis); + return limit; + } +} + +/** + * Clickhouse LIMIT BY + * @see SELECT Query + */ +Limit LimitBy(): +{ + Limit limit; + ExpressionList byExpressions; +} +{ + limit = LimitWithOffset() + byExpressions = ExpressionList() + { + limit.setByExpressions(byExpressions); + return limit; + } +} + +Offset Offset(): +{ + Offset offset = new Offset(); + Expression offsetExpression; +} +{ + ( + // postgresql-> OFFSET offset + // sqlserver-oracle-> OFFSET offset (ROW | ROWS) + + offsetExpression=Expression() { offset.setOffset( offsetExpression ); } + + [ LOOKAHEAD(2) ( { offset.setOffsetParam("ROWS"); } | { offset.setOffsetParam("ROW"); })] + + ) + { + return offset; + } +} + +Fetch Fetch(): +{ + Fetch fetch = new Fetch(); + Token token = null; + Expression expression; + List fetchParameters = new ArrayList(); +} +{ + ( + LOOKAHEAD(3) ( + ( { fetch.setFetchParamFirst(true); } | ) + ( + { fetch.addFetchParameter("ROWS"); } + | + { fetch.addFetchParameter("ROW"); } + ) + ( + { fetch.addFetchParameter("ONLY"); } + | + { fetch.addFetchParameter("WITH TIES"); } + ) + ) + | + ( + ( { fetch.setFetchParamFirst(true); } | ) + + // Expression is optional according to https://www.h2database.com/html/commands.html#select + expression = Expression() { fetch.setExpression(expression); } + [ { fetch.addFetchParameter("PERCENT"); } ] + ( + ( + { fetch.addFetchParameter("ROWS"); } + | + { fetch.addFetchParameter("ROW"); } + ) + + ( + { fetch.addFetchParameter("ONLY"); } + | + { fetch.addFetchParameter("WITH TIES"); } + ) + ) + ) + ) + { + return fetch; + } +} + +WithIsolation WithIsolation(): +{ + WithIsolation withIsolation = new WithIsolation(); + Token token = null; + JdbcParameter jdbc; +} +{ + ( + //with (ur | cs | rs | rr) + + token= { withIsolation.setIsolation(token.image); } + + ) + { + return withIsolation; + } +} + +OptimizeFor OptimizeFor(): +{ + Token token; + LongValue value; +} +{ + token= + { + value = new LongValue(token.image); + return new OptimizeFor(value.getValue()); + } +} + +// according to http://technet.microsoft.com/en-us/library/ms189463.aspx +Top Top() #Top: +{ + Top top = new Top(); + Token token = null; + Expression expr = null; + JdbcParameter jdbc = null; +} +{ + + ( + token= { top.setExpression(new LongValue(token.image)); } + | + jdbc = JdbcParameter() { top.setExpression(jdbc); } + | + ":" { top.setExpression(new JdbcNamedParameter()); } + [ LOOKAHEAD(2) token = { ((JdbcNamedParameter)top.getExpression()).setName(token.image); } ] + | + "(" + expr=AdditiveExpression() + { + top.setExpression(expr); + top.setParenthesis(true); + } + ")" + ) + [ LOOKAHEAD(2) { top.setPercentage(true); } ] + [ LOOKAHEAD(2) { top.setWithTies(true); } ] + { + linkAST(top,jjtThis); + return top; + } +} + +// according to http://www-01.ibm.com/support/knowledgecenter/SSGU8G_12.1.0/com.ibm.sqls.doc/ids_sqs_0156.htm +Skip Skip(): +{ + Skip skip = new Skip(); + Token token = null; + JdbcParameter jdbc; +} +{ + + ( + token= { skip.setRowCount(Long.parseLong(token.image)); } + | token= { skip.setVariable(token.image); } + | jdbc = JdbcParameter() { skip.setJdbcParameter(jdbc); } + /* "?" { skip.setJdbcParameter(new JdbcParameter(++jdbcParameterIndex, false)); } [ LOOKAHEAD(2) token = { skip.getJdbcParameter().setUseFixedIndex(true); skip.getJdbcParameter().setIndex(Integer.valueOf(token.image)); } ] */ + ) + { + return skip; + } +} + +JAVACODE +OracleHint getOracleHint() { + OracleHint hint = null; + Token tok = getToken(1); + // Retrieve first comment (if any) prior next token + if (tok.specialToken != null) { + tok = tok.specialToken; + while (tok.specialToken != null) tok = tok.specialToken; + // Check if it matches Hint pattern? + if (OracleHint.isHintMatch(tok.image)) { + hint = new OracleHint(); + hint.setComment(tok.image); + } + } + return hint; +} + +First First(): +{ + First first = new First(); + Token token = null; + JdbcParameter jdbc; +} +{ + ( + { first.setKeyword(First.Keyword.FIRST); } + | + { first.setKeyword(First.Keyword.LIMIT); } + ) + ( + token= { first.setRowCount(Long.parseLong(token.image)); } + | + token= { first.setVariable(token.image); } + | + jdbc = JdbcParameter() { first.setJdbcParameter(jdbc); } + ) + { + return first; + } +} + + +Expression Expression() #Expression : +{ + Expression retval = null; +} +{ + retval=XorExpression() + + { return retval; } +} + +Expression XorExpression(): +{ + Expression left, right, result; +} +{ + left=OrExpression() { result = left; } + ( LOOKAHEAD(2) + + right=OrExpression() + { + result = new XorExpression(left, right); + left = result; + } + )* + { + return result; + } +} + +Expression OrExpression(): +{ + Expression left, right, result; +} +{ + left=AndExpression() { result = left; } + ( LOOKAHEAD(2) + + right=AndExpression() + { + result = new OrExpression(left, right); + left = result; + } + )* + { + return result; + } + +} + +Expression AndExpression() : +{ + Expression left, right, result; + boolean not = false; + boolean exclamationMarkNot=false; +} +{ + ( + LOOKAHEAD(Condition(), {!interrupted}) + left=Condition() + | + [ { not=true; } | "!" { not=true; exclamationMarkNot=true; } ] + "(" left=XorExpression() ")" {left = new ParenthesedExpressionList(left); if (not) { left = new NotExpression(left, exclamationMarkNot); not = false; } } + ) + { result = left; } + + ( LOOKAHEAD(2) + { boolean useOperator = false; } + ( | {useOperator=true;} ) + ( + LOOKAHEAD(Condition(), {!interrupted}) + right=Condition() + | + [ { not=true; } | "!" { not=true; exclamationMarkNot=true; } ] + "(" right=XorExpression() ")" {right = new ParenthesedExpressionList(right); if (not) { right = new NotExpression(right, exclamationMarkNot); not = false; } } + ) + { + result = new AndExpression(left, right); + ((AndExpression)result).setUseOperator(useOperator); + left = result; + } + )* + { + return result; + } +} + +Expression Condition(): +{ + Expression result; + Token token; + boolean not = false; + boolean exclamationMarkNot = false; +} +{ + [ LOOKAHEAD(2) ( { not=true; } | "!" { not=true; exclamationMarkNot=true; })] + ( + LOOKAHEAD(RegularCondition()) result=RegularCondition() + | result=SQLCondition() + ) + + { return not?new NotExpression(result, exclamationMarkNot):result; } +} + +Expression OverlapsCondition():{ + ExpressionList left = new ExpressionList(); + ExpressionList right = new ExpressionList(); +} +{ + //As per the sql2003 standard, we need at least two items in the list if there is not explicit ROW prefix + //More than two expression are allowed per the sql2003 grammar. + left = ParenthesedExpressionList() + + right = ParenthesedExpressionList() + + {return new OverlapsCondition(left, right);} +} + +Expression RegularCondition() #RegularCondition: +{ + Expression result = null; + Expression leftExpression; + Expression rightExpression; + int oracleJoin=EqualsTo.NO_ORACLE_JOIN; + int oraclePrior=EqualsTo.NO_ORACLE_PRIOR; + boolean binary = false; + boolean not = false; +} +{ + [ LOOKAHEAD(2) { oraclePrior = EqualsTo.ORACLE_PRIOR_START; }] + leftExpression=ComparisonItem() { result = leftExpression; } + + [ "(" "+" ")" { oracleJoin=EqualsTo.ORACLE_JOIN_RIGHT; } ] + + ( + LOOKAHEAD(2) + ">" { result = new GreaterThan(); } + | "<" { result = new MinorThan(); } + | "=" { result = new EqualsTo(); } + | token= { result = new GreaterThanEquals(token.image); } + | token= { result = new MinorThanEquals(token.image); } + | token= { result = new NotEqualsTo(token.image); } + | token= { result = new NotEqualsTo(token.image); } + | "*=" { result = new TSQLLeftJoin(); } + | "=*" { result = new TSQLRightJoin(); } + | token= { result = new DoubleAnd(); } + | token= { result = new Contains(); } + | token= { result = new ContainedBy(); } + | "@@" { result = new Matches(); } + | "~" { result = new RegExpMatchOperator(RegExpMatchOperatorType.MATCH_CASESENSITIVE); } + | "~*" { result = new RegExpMatchOperator(RegExpMatchOperatorType.MATCH_CASEINSENSITIVE); } + | "!~" { result = new RegExpMatchOperator(RegExpMatchOperatorType.NOT_MATCH_CASESENSITIVE); } + | "!~*" { result = new RegExpMatchOperator(RegExpMatchOperatorType.NOT_MATCH_CASEINSENSITIVE); } + + | "@>" { result = new JsonOperator("@>"); } + | "<@" { result = new JsonOperator("<@"); } + | "?" { result = new JsonOperator("?"); } + | "?|" { result = new JsonOperator("?|"); } + | "?&" { result = new JsonOperator("?&"); } + | { result = new JsonOperator("||"); } + | "-" { result = new JsonOperator("-"); } + | "-#" { result = new JsonOperator("-#"); } + | "<->" { result = new GeometryDistance("<->"); } + | "<#>" { result = new GeometryDistance("<#>"); } + ) + + ( LOOKAHEAD(2) rightExpression=ComparisonItem() { oraclePrior = EqualsTo.ORACLE_PRIOR_END; } + | rightExpression=ComparisonItem() ) + + [ LOOKAHEAD(2) "(" "+" ")" { oracleJoin=EqualsTo.ORACLE_JOIN_LEFT; } ] + + { + BinaryExpression regCond = (BinaryExpression) result; + regCond.setLeftExpression(leftExpression); + regCond.setRightExpression(rightExpression); + + if (oracleJoin>0) + ((SupportsOldOracleJoinSyntax)result).setOldOracleJoinSyntax(oracleJoin); + + if (oraclePrior!=EqualsTo.NO_ORACLE_PRIOR) + ((SupportsOldOracleJoinSyntax)result).setOraclePriorPosition(oraclePrior); + } + + { + linkAST(result,jjtThis); + return result; + } +} + +Expression SQLCondition(): +{ + Expression result; + Expression left; +} +{ + ( + result=ExistsExpression() + | LOOKAHEAD(InExpression() , {!interrupted}) result=InExpression() + | LOOKAHEAD(OverlapsCondition(), {!interrupted}) result=OverlapsCondition() + | left = SimpleExpression() { result = left; } + [ + LOOKAHEAD(2) ( + ( + LOOKAHEAD(ExcludesExpression()) result=ExcludesExpression(left) + | + LOOKAHEAD(IncludesExpression()) result=IncludesExpression(left) + | + LOOKAHEAD(2) result=Between(left) + | + result = MemberOfExpression(left) + | + LOOKAHEAD(IsNullExpression()) result=IsNullExpression(left) + | + LOOKAHEAD(IsBooleanExpression()) result=IsBooleanExpression(left) + | + LOOKAHEAD(2) result=LikeExpression(left) + | + LOOKAHEAD(IsDistinctExpression()) result=IsDistinctExpression(left) + | + result=SimilarToExpression(left) + ) + ) + ] + ) + { return result; } +} + +Expression InExpression() #InExpression : +{ + Token token; + int oldOracleJoin = 0; + boolean usingNot = false; + boolean usingGlobal = false; + Expression leftExpression; + Expression rightExpression; +} +{ + leftExpression=SimpleExpression() + [ "(" "+" ")" { oldOracleJoin=EqualsTo.ORACLE_JOIN_RIGHT; } ] + + [ { usingGlobal=true; } ] + [ { usingNot=true; } ] + + ( + LOOKAHEAD(2) token= { rightExpression = new StringValue(token.image); } + | LOOKAHEAD(3) rightExpression = Function() + | LOOKAHEAD(ParenthesedSelect(), {!interrupted}) rightExpression = ParenthesedSelect() + | LOOKAHEAD(3) rightExpression = ParenthesedExpressionList() + | rightExpression = SimpleExpression() + ) + { + InExpression inExpression = new InExpression(leftExpression, rightExpression) + .withOldOracleJoinSyntax(oldOracleJoin) + .withNot(usingNot) + .setGlobal(usingGlobal); + linkAST(inExpression,jjtThis); + return inExpression; + } +} + +Expression IncludesExpression(Expression leftExpression) #IncludesExpression : +{ + Token token; + Expression rightExpression; +} +{ + (rightExpression = ParenthesedExpressionList()) + { + IncludesExpression includesExpression = new IncludesExpression(leftExpression, rightExpression); + + linkAST(includesExpression,jjtThis); + return includesExpression; + } +} + +Expression ExcludesExpression(Expression leftExpression) #ExcludesExpression : +{ + Token token; + Expression rightExpression; +} +{ + (rightExpression = ParenthesedExpressionList()) + { + ExcludesExpression excludesExpression = new ExcludesExpression(leftExpression, rightExpression); + + linkAST(excludesExpression,jjtThis); + return excludesExpression; + } +} + +Expression Between(Expression leftExpression) : +{ + Between result = new Between(); + Expression betweenExpressionStart = null; + Expression betweenExpressionEnd = null; +} +{ + [ { result.setNot(true); }] + + ( + LOOKAHEAD( 3 ) betweenExpressionStart = ParenthesedSelect() + | + LOOKAHEAD( RegularCondition() ) betweenExpressionStart = RegularCondition() + | + betweenExpressionStart = SimpleExpression() + ) + + + ( + LOOKAHEAD( 3 ) betweenExpressionEnd = ParenthesedSelect() + | + LOOKAHEAD( RegularCondition() ) betweenExpressionEnd = RegularCondition() + | + betweenExpressionEnd = SimpleExpression() + ) + + { + result.setLeftExpression(leftExpression); + result.setBetweenExpressionStart(betweenExpressionStart); + result.setBetweenExpressionEnd(betweenExpressionEnd); + return result; + } +} + +Expression LikeExpression(Expression leftExpression) #LikeExpression: +{ + LikeExpression result = new LikeExpression(); + Expression rightExpression = null; + Expression escape; + Token token; +} +{ + [ { result.setNot(true); } ] + ( + token = + | token = + | token = + | token = + | token = + ) { result.setLikeKeyWord( LikeExpression.KeyWord.from(token.image)); } + [ LOOKAHEAD(2) {result.setUseBinary(true); } ] + rightExpression=SimpleExpression() + [ LOOKAHEAD(2) + ( + LOOKAHEAD(2) token = { result.setEscape( new StringValue( token.image ) ); } + | + escape=Expression() { result.setEscape(escape); } + ) + ] + { + result.setLeftExpression(leftExpression); + result.setRightExpression(rightExpression); + linkAST(result,jjtThis); + return result; + } +} + +Expression SimilarToExpression(Expression leftExpression) #SimilarToExpression: +{ + SimilarToExpression result = new SimilarToExpression(); + Expression rightExpression = null; +} +{ + [ { result.setNot(true); } ] + + rightExpression=SimpleExpression() + [ LOOKAHEAD(2) token= { result.setEscape((new StringValue(token.image)).getValue()); }] + { + result.setLeftExpression(leftExpression); + result.setRightExpression(rightExpression); + linkAST(result,jjtThis); + return result; + } +} + +Expression IsDistinctExpression(Expression leftExpression) #IsDistinctExpression: +{ + IsDistinctExpression result = new IsDistinctExpression(); + Expression rightExpression = null; +} +{ + [ { result.setNot(true); } ] + rightExpression=SimpleExpression() + { + result.setLeftExpression(leftExpression); + result.setRightExpression(rightExpression); + linkAST(result,jjtThis); + return result; + } +} + +Expression IsNullExpression(Expression leftExpression): +{ + IsNullExpression result = new IsNullExpression(); +} +{ + ( + [ { result.setNot(true); } ] { result.setUseIsNull(true); } + | { result.setUseIsNull(true); result.setUseNotNull(true); } + | [ { result.setNot(true); } ] + ) + { + result.setLeftExpression(leftExpression); + return result; + } +} + +Expression IsBooleanExpression(Expression leftExpression): +{ + IsBooleanExpression result = new IsBooleanExpression(); +} +{ + ( + [ { result.setNot(true); } ] ( { result.setIsTrue(true); } | { result.setIsTrue(false); }) + ) + + { + result.setLeftExpression(leftExpression); + return result; + } +} + +Expression ExistsExpression(): +{ + ExistsExpression result = new ExistsExpression(); + Expression rightExpression = null; +} +{ + rightExpression=SimpleExpression() + { + result.setRightExpression(rightExpression); + return result; + } +} + +Expression MemberOfExpression(Expression leftExpression): +{ + MemberOfExpression result; + Expression rightExpression = null; +} +{ + rightExpression=Expression() + { + return new MemberOfExpression(leftExpression, rightExpression ); + } +} + +ExpressionList ExpressionList() #ExpressionList: +{ + ExpressionList expressionList; +} +{ + ( + LOOKAHEAD(3, { getAsBoolean(Feature.allowComplexParsing) && !interrupted}) expressionList = ComplexExpressionList() + | + LOOKAHEAD(3) expressionList = SimpleExpressionList() + | + LOOKAHEAD(3) expressionList = ParenthesedExpressionList() + ) + { + // Avoid redundant ExpressionLists containing only one ParenthesedExpressionList + // return the Parenthesed Sub ExpressionList instead + // Same for ParenthesedExpressionList containing only 1 ExpressionList + + if ( expressionList.size() == 1 && expressionList.get(0) instanceof ExpressionList ) { + ExpressionList subList = (ExpressionList) expressionList.get(0); + if (expressionList instanceof ParenthesedExpressionList) { + if (subList instanceof ParenthesedExpressionList) { + return expressionList; + } else { + return new ParenthesedExpressionList(subList); + } + } else { + if (subList instanceof ParenthesedExpressionList) { + return new ParenthesedExpressionList(subList); + } else { + return subList; + } + } + } + return expressionList; + } +} + +ParenthesedExpressionList ParenthesedExpressionList(): +{ + ExpressionList expressions=new ExpressionList(); +} +{ + "(" + ( + LOOKAHEAD({ getAsBoolean(Feature.allowComplexParsing) && !interrupted}) expressions = ComplexExpressionList() + | + expressions = SimpleExpressionList() + )? + ")" + { + return new ParenthesedExpressionList(expressions); + } +} + +ExpressionList SimpleExpressionList(): +{ + ExpressionList expressions = new ExpressionList(); + Expression expr; +} +{ + expr=SimpleExpression() { expressions.add(expr); } + ( LOOKAHEAD(2, {!interrupted} ) "," ( LOOKAHEAD(2) expr=LambdaExpression() | expr=SimpleExpression()) { expressions.add(expr); } )* + { + return expressions; + } +} + + +ExpressionList ColumnList(): +{ + ExpressionList expressions = new ExpressionList(); + Column expr; +} +{ + expr=Column() { expressions.add(expr); } + ( LOOKAHEAD(2) "," expr=Column() { expressions.add(expr); } )* + { + return expressions; + } +} + +ParenthesedExpressionList ParenthesedColumnList(): +{ + ExpressionList expressions; +} +{ + "(" expressions = ColumnList() ")" + { + return new ParenthesedExpressionList(expressions); + } +} + +ExpressionList ComplexExpressionList(): +{ + ExpressionList expressions = new ExpressionList(); + Expression expr; +} +{ + ( + LOOKAHEAD(2) expr=OracleNamedFunctionParameter() + | expr=Expression() + ) + { + expressions.add(expr); + } + + ( + LOOKAHEAD(2, {!interrupted}) "," + ( + LOOKAHEAD(2) expr=OracleNamedFunctionParameter() + | LOOKAHEAD(2) expr=LambdaExpression() + | expr=Expression() + ) { expressions.add(expr); } + )* + + { + return expressions; + } +} + +// @Todo: Refactor this with proper SQL:2016 functions according to https://manticore-projects.com/SQL2016Parser/syntax.html#character-value-function +// substring(expr1 from expr2) +// substring(expr1 from expr2 for expr3) +// trim(expr1 from expr2) <-- Superceded by TrimFunction() below +// position(expr1 in expr2) +// overlay(expr1 placing expr2 from expr3) +// overlay(expr1 placing expr2 from expr3 for expr4) +// expr1 has already been consumed +NamedExpressionList NamedExpressionListExprFirst(): +{ + NamedExpressionList retval = new NamedExpressionList(); + List expressions = new ArrayList(); + List names = new ArrayList(); + Expression expr1 = null; + Expression expr2 = null; + Expression expr3 = null; + Expression expr4 = null; + Token tk2 = null; + Token tk3 = null; + Token tk4 = null; +} +{ + expr1=SimpleExpression() + (tk2=|tk2=|tk2=) + { + names.add(""); + expressions.add(expr1); + names.add(tk2.image); + } + ( + expr2=SimpleExpression() { expressions.add(expr2);} + ( + (tk3=|tk3=) + expr3=SimpleExpression() {names.add(tk3.image); expressions.add(expr3);} + ( + (tk4=) + expr4=SimpleExpression() {names.add(tk4.image); expressions.add(expr4);} + )? + )? + ) + + { + retval.setNames(names); + retval.setExpressions(expressions); + return retval; + } +} + +Expression ComparisonItem() : +{ + Expression retval = null; +} +{ + ( + LOOKAHEAD( AnyComparisonExpression() ) retval=AnyComparisonExpression() + | LOOKAHEAD(3) retval=SimpleExpression() + | LOOKAHEAD(3) retval=ParenthesedExpressionList() + | LOOKAHEAD(3) retval=RowConstructor() + | retval=PrimaryExpression() + ) + + { + return retval; + } +} + +Expression AnyComparisonExpression() : +{ + AnyType anyType; + Select select; +} +{ + ( + ( + { anyType = AnyType.ANY; } + | { anyType = AnyType.SOME; } + | { anyType = AnyType.ALL; } + ) + + select = ParenthesedSelect() + ) + { + return new AnyComparisonExpression(anyType, select); + } +} + +Expression SimpleExpression(): +{ + Expression retval = null; + UserVariable user = null; + Token operation = null; +} +{ + + ( + [ LOOKAHEAD(UserVariable() ("=" | ":=") ) + user = UserVariable() + ( operation = "=" | operation = ":=" ) + ] + + ( + retval=ConcatExpression() + ) + ) + { + if (user != null) { + VariableAssignment assignment = new VariableAssignment(); + assignment.setVariable(user); + assignment.setOperation(operation.image); + assignment.setExpression(retval); + return assignment; + } else + return retval; + } +} + +Expression ConcatExpression(): +{ + Expression result = null; + Expression leftExpression = null; + Expression rightExpression = null; +} +{ + leftExpression=BitwiseAndOr() { result = leftExpression; } + (LOOKAHEAD(3) + /* Oracle allows space between the bars. */ + rightExpression=BitwiseAndOr() + { + Concat binExp = new Concat(); + binExp.setLeftExpression(leftExpression); + binExp.setRightExpression(rightExpression); + result = binExp; + leftExpression = result; + } + )* + + { return result; } +} + +Expression BitwiseAndOr(): +{ + Expression result = null; + Expression leftExpression = null; + Expression rightExpression = null; +} +{ + leftExpression=AdditiveExpression() { result = leftExpression; } + ( + LOOKAHEAD(2) ( + "|" { result = new BitwiseOr(); } + | + "&" { result = new BitwiseAnd(); } + | + "<<" { result = new BitwiseLeftShift(); } + | + ">>" { result = new BitwiseRightShift(); } + ) + + rightExpression=AdditiveExpression() + + { + BinaryExpression binExp = (BinaryExpression) result; + binExp.setLeftExpression(leftExpression); + binExp.setRightExpression(rightExpression); + leftExpression = result; + } + )* + + { return result; } +} + +Expression AdditiveExpression(): +{ + Expression result = null; + Expression leftExpression = null; + Expression rightExpression = null; +} +{ + leftExpression=MultiplicativeExpression() { result = leftExpression; } + ( LOOKAHEAD(2) + ("+" { result = new Addition(); } + | "-" { result = new Subtraction(); } ) + + rightExpression=MultiplicativeExpression() + { + BinaryExpression binExp = (BinaryExpression) result; + binExp.setLeftExpression(leftExpression); + binExp.setRightExpression(rightExpression); + leftExpression = result; + } + )* + + { return result; } +} + +Expression MultiplicativeExpression(): +{ + Expression result = null; + Expression leftExpression = null; + Expression rightExpression = null; +} +{ + ( + leftExpression=BitwiseXor() + ) + { result = leftExpression; } + ( + LOOKAHEAD(2) ("*" { result = new Multiplication(); } + | "/" { result = new Division(); } + | { result = new IntegerDivision(); } + | "%" { result = new Modulo(); } + ) + + rightExpression=BitwiseXor() + + { + BinaryExpression binExp = (BinaryExpression) result; + binExp.setLeftExpression(leftExpression); + binExp.setRightExpression(rightExpression); + leftExpression = result; + } + )* + { return result; } +} + +Expression BitwiseXor(): +{ + Expression result = null; + Expression leftExpression = null; + Expression rightExpression = null; +} +{ + leftExpression=PrimaryExpression() { result = leftExpression; } + ( + "^" + rightExpression=PrimaryExpression() + { + BitwiseXor binExp = new BitwiseXor(); + binExp.setLeftExpression(leftExpression); + binExp.setRightExpression(rightExpression); + result = binExp; + leftExpression = result; + } + )* + + { return result; } +} + +Expression ArrayExpression(Expression obj): { + Expression expr; + Expression idxExpr = null; + Expression startExpr = null; + Expression stopExpr = null; +} { + "[" + [LOOKAHEAD(3) idxExpr = SimpleExpression()] + [ + (":" { startExpr=idxExpr; idxExpr=null; }) + [stopExpr = SimpleExpression()] + ] + "]" + { expr = new ArrayExpression(obj, idxExpr, startExpr, stopExpr); } + ( + LOOKAHEAD(2) "[" + [LOOKAHEAD(3) idxExpr = SimpleExpression()] + [ + (":" { startExpr=idxExpr; idxExpr=null; }) + [stopExpr = SimpleExpression()] + ] + "]" + { expr = new ArrayExpression(expr, idxExpr, startExpr, stopExpr); } + )* + { return expr; } +} + +Expression PrimaryExpression() #PrimaryExpression: +{ + Expression retval = null; + CastExpression castExpr = null; + TimezoneExpression timezoneExpr = null; + Expression timezoneRightExpr = null; + Token token = null; + Token sign = null; + String tmp = ""; + ColDataType type = null; + boolean not = false; + boolean exclamationMarkNot = false; + boolean dateExpressionAllowed = true; + ExpressionList list; + + final List> jsonIdents = new ArrayList>(); +} +{ + [ { not=true; } | "!" { not=true; exclamationMarkNot=true; } ] + [sign="+" | sign="-" | sign="~"] + ( + { retval = new NullValue(); } + + | LOOKAHEAD(3, {!interrupted}) retval=CaseWhenExpression() + + | LOOKAHEAD(2, {!interrupted}) retval=CharacterPrimary() + + | LOOKAHEAD( ImplicitCast(), {!interrupted}) retval=ImplicitCast() + + | retval = JdbcParameter() + + | LOOKAHEAD(2) retval =JdbcNamedParameter() + + | LOOKAHEAD(3) retval=UserVariable() + + | LOOKAHEAD(2, {!interrupted}) retval=NumericBind() + + | LOOKAHEAD( ExtractExpression() , {!interrupted}) retval=ExtractExpression() + + | LOOKAHEAD(3) retval=MySQLGroupConcat() + + | retval=XMLSerializeExpr() + + | LOOKAHEAD(3, { !interrupted}) retval = JsonFunction() + + | LOOKAHEAD(3, { !interrupted}) retval = JsonAggregateFunction() + + | LOOKAHEAD(3, { !interrupted}) retval = FullTextSearch() + + | LOOKAHEAD( Function(), { !interrupted}) retval=Function() [ LOOKAHEAD(2) retval = AnalyticExpression( (Function) retval ) ] + + | LOOKAHEAD(2, {!interrupted}) retval = IntervalExpression() { dateExpressionAllowed = false; } + + | token= { retval = new DoubleValue(token.image); } + + | token= { retval = new LongValue(token.image); } + + | token= { retval = new HexValue(token.image); } + + | LOOKAHEAD(2, {!interrupted}) retval=CastExpression() + + + // support timestamp expressions + | LOOKAHEAD(2, {!interrupted}) (token= | token=) { retval = new TimeKeyExpression(token.image); } + + | LOOKAHEAD(2, {!interrupted}) retval=DateTimeLiteralExpression() + + | LOOKAHEAD( 3 , {!interrupted}) retval=StructType() + + | LOOKAHEAD(3, {!interrupted}) retval=ArrayConstructor(true) + + | LOOKAHEAD(3, {!interrupted}) retval=ArrayConstructor(false) + + | LOOKAHEAD(2, {!interrupted}) retval = NextValExpression() + + | retval=ConnectByRootOperator() + + | LOOKAHEAD(2, {!interrupted}) { retval = new AllValue(); } + + | LOOKAHEAD(2, {!interrupted}) retval=Column() + + | token= { retval = new StringValue(token.image); linkAST(retval,jjtThis); } + + | "{d" token= "}" { retval = new DateValue(token.image); } + + | "{t" token= "}" { retval = new TimeValue(token.image); } + + | "{ts" token= "}" { retval = new TimestampValue(token.image); } + + | LOOKAHEAD( ParenthesedSelect() , {!interrupted} ) retval=ParenthesedSelect() + + | + ( + list=ParenthesedExpressionList() + { + if (list.size() == 1) { + retval = new ParenthesedExpressionList( (Expression) list.getExpressions().get(0)); + } else { + retval = list; + } + } + ["." tmp=RelObjectNameExt() { retval = new RowGetExpression(retval, tmp); }] + ) + ) + + [ + LOOKAHEAD(2) token= { retval = new CollateExpression(retval, token.image); } + ] + + [ + LOOKAHEAD(2, { dateExpressionAllowed } ) retval = IntervalExpressionWithoutInterval(retval) + ] + + [ LOOKAHEAD(2) retval = ArrayExpression(retval) ] + + ( "::" type=ColDataType() { + castExpr = new CastExpression(); + castExpr.setUseCastKeyword(false); + castExpr.setLeftExpression(retval); + castExpr.setColDataType(type); + retval=castExpr; + } + )* + + // Check for JSON operands + [ + LOOKAHEAD(2) ( + "->" (token= | token=) { jsonIdents.add(new AbstractMap.SimpleEntry(token.image,"->")); } + | + "->>" (token= | token=) { jsonIdents.add(new AbstractMap.SimpleEntry(token.image,"->>")); } + | + "#>" token= { jsonIdents.add(new AbstractMap.SimpleEntry(token.image,"#>")); } + | + "#>>" token= { jsonIdents.add(new AbstractMap.SimpleEntry(token.image,"#>>")); } + )+ + retval = JsonExpression(retval, jsonIdents) + ] + + + ( LOOKAHEAD(2) timezoneRightExpr=PrimaryExpression() { + if (timezoneExpr == null) + timezoneExpr = new TimezoneExpression(); + + timezoneExpr.addTimezoneExpression(timezoneRightExpr); + } + )* + + { + if (timezoneExpr != null && !timezoneExpr.getTimezoneExpressions().isEmpty()) { + timezoneExpr.setLeftExpression(retval); + retval=timezoneExpr; + } + if (sign != null) { + retval = new SignedExpression(sign.image.charAt(0), retval); + } + if (not) { + retval = new NotExpression(retval, exclamationMarkNot); + } + linkAST(retval, jjtThis); + return retval; + } +} + +ConnectByRootOperator ConnectByRootOperator() #ConnectByRootOperator: { + Column column; +} +{ + column = Column() + { + return new ConnectByRootOperator(column); + } +} + +NextValExpression NextValExpression() : { + ObjectNames data = null; + Token token; +} +{ + token= data = RelObjectNames() + { + return new NextValExpression(data.getNames(), token.image); + } +} + +JdbcNamedParameter JdbcNamedParameter() : { + JdbcNamedParameter parameter = new JdbcNamedParameter(); + String name; + String namePart; +} +{ + (":" | "&" { parameter.setParameterCharacter("&"); } ) + name=IdentifierChain() + { + parameter.setName(name); + return parameter; + } +} + +OracleNamedFunctionParameter OracleNamedFunctionParameter() : { + Token token = null; + String name = null; + Expression expression; +} +{ + ( name=RelObjectNameExt2() | token= ) + + expression=Expression() + { + return new OracleNamedFunctionParameter(name != null ? name : token.image, expression); + } +} + +UserVariable UserVariable() : { + UserVariable var = new UserVariable(); + String varName; + String var2; +} +{ + ("@" | "@@" { var.setDoubleAdd(true);} ) + varName=IdentifierChain() + { + var.setName(varName); + return var; + } +} + +NumericBind NumericBind() : { + NumericBind var = new NumericBind(); + Token token; +} +{ + ":" token= + { + var.setBindId(Integer.valueOf(token.image)); + return var; + } +} + +DateTimeLiteralExpression DateTimeLiteralExpression() : { + DateTimeLiteralExpression expr = new DateTimeLiteralExpression(); + Token t; +} { + t= { expr.setType(DateTimeLiteralExpression.DateTime.from(t.image)); } + + ( t= | t= ) { expr.setValue(t.image); return expr; } +} + +RangeExpression RangeExpression(Expression startExpression): +{ + Expression endExpression; +} +{ + ":" endExpression = Expression() + { + return new RangeExpression(startExpression, endExpression); + } +} + +ArrayConstructor ArrayConstructor(boolean arrayKeyword) : { + ExpressionList expList = new ExpressionList(); + ArrayConstructor array = new ArrayConstructor(expList, arrayKeyword); + Expression exp; +} { + "[" + [ + ( + LOOKAHEAD(3) exp = Expression() [ exp=RangeExpression(exp) ] + | + exp = ArrayConstructor(false) + ) { expList.add(exp); } + + ( + "," + ( + LOOKAHEAD(3) exp = Expression() [ exp=RangeExpression(exp) ] + | + exp = ArrayConstructor(false) + ){ expList.add(exp); } + )* + ] + "]" + { return array; } +} + +List> StructParameters(): +{ + String parameterName = ""; + ColDataType parameterType = null; + AbstractMap.SimpleEntry parameter = null; + List> parameters = new ArrayList>(); +} +{ + [ LOOKAHEAD(2) parameterName = RelObjectName() ] + parameterType = ColDataType() + { + parameters.add( new AbstractMap.SimpleEntry(parameterName, parameterType) ); + } + + ( + "," { parameterName=""; } + + [ LOOKAHEAD(2) parameterName = RelObjectName() ] + parameterType = ColDataType() + { + parameters.add( new AbstractMap.SimpleEntry(parameterName, parameterType) ); + } + )* + + { + return parameters; + } +} + +StructType StructType() #StruckType: +{ + StructType.Dialect dialect = StructType.Dialect.BIG_QUERY; + Token tk1; + String keyword = ""; + List> parameters = null; + List> arguments = null; + String id = null; + Expression expression = null; + StructType type; +} +{ + ( + LOOKAHEAD(4) ( + tk1= { keyword = tk1.image; } + "<" parameters = StructParameters() ">" + "(" { System.out.println("found arguments!"); } arguments = SelectItemsList() ")" + ) + | + ( + tk1= { keyword = tk1.image; } + "(" arguments = SelectItemsList() ")" + ) + | + ( + { arguments= new ArrayList>(); dialect = StructType.Dialect.DUCKDB;} + + id = RelObjectName() expression = Expression() { arguments.add( new SelectItem( expression, id) ); } + + ( + "," + id = RelObjectName() expression = Expression() { arguments.add( new SelectItem( expression, id) ); } + )* + + + ( + LOOKAHEAD(2) "::" "(" parameters = StructParameters() ")" + )* + ) + + // don't parse this as an Struct, but rather use an Expressionlist + // | + // arguments = StructArguments() + ) + { + type = new StructType(dialect, keyword, parameters, arguments); + linkAST(type,jjtThis); + return type; + } +} + +Expression ParenthesedExpression(): +{ + Expression expression; +} +{ + "(" expression = PrimaryExpression() ")" + { + return new ParenthesedExpressionList(expression); + } +} + +JsonExpression JsonExpression(Expression expr, List> idents) : { + JsonExpression result = new JsonExpression(expr, idents); + Token token; + ColDataType type = null; + CastExpression castExpr = null; +} +{ + + // chaining JSON Expressions, e.g. + // '{"obj":{"field": "value"}}'::JSON -> 'obj'::TEXT ->> 'field'::TEXT + ( + LOOKAHEAD(2, {!interrupted} ) ( + LOOKAHEAD(2) ( + "::" type=ColDataType() + { + castExpr = new CastExpression(); + castExpr.setUseCastKeyword(false); + castExpr.setLeftExpression(result); + castExpr.setColDataType(type); + expr=castExpr; + } + ) + )+ + { + result = new JsonExpression(expr); + } + + ( + LOOKAHEAD(2) ( + "->" (token= | token=) {result.addIdent(token.image,"->");} + | + "->>" (token= | token=) {result.addIdent(token.image,"->>");} + | + "#>" token= {result.addIdent(token.image,"#>");} + | + "#>>" token= {result.addIdent(token.image,"#>>");} + ) + )* + )* + + { + result.setExpression(expr); + return result; + } +} + +JsonFunction JsonFunction() : { + JsonFunction result = new JsonFunction(); + boolean usingKeyKeyword = false; + boolean usingValueKeyword = false; + boolean usingFormatJason = false; + Token keyToken; + Token valueToken = null; + Column column = null; + JsonKeyValuePair keyValuePair; + + Expression expression = null; + JsonFunctionExpression functionExpression; + +} +{ + ( + ( + ( + "(" { result.setType( JsonFunctionType.OBJECT ); } + ( + ( + // SQL2016 compliant Syntax + ( + [ "KEY" { usingKeyKeyword = true; } ] + keyToken = + + ( LOOKAHEAD(2) + ( ":" | "," { result.setType( JsonFunctionType.POSTGRES_OBJECT ); } | "VALUE" { usingValueKeyword = true; } ) + ( + expression = Expression() + ) + [ { usingFormatJason = true; } ] + )? { + if (expression !=null) { + keyValuePair = new JsonKeyValuePair( keyToken.image, expression, usingKeyKeyword, usingValueKeyword ); + keyValuePair.setUsingFormatJson( usingFormatJason ); + result.add(keyValuePair); + } else { + result.setType( JsonFunctionType.POSTGRES_OBJECT ); + keyValuePair = new JsonKeyValuePair( keyToken.image, null, false, false ); + result.add(keyValuePair); + } + } + + // --- Next Elements + ( "," { usingKeyKeyword = false; usingValueKeyword = false; } + [ "KEY" { usingKeyKeyword = true; } ] + keyToken = + ( ":" | "," { result.setType( JsonFunctionType.MYSQL_OBJECT ); } | "VALUE" { usingValueKeyword = true; } ) + ( + expression = Expression() { keyValuePair = new JsonKeyValuePair( keyToken.image, expression, usingKeyKeyword, usingValueKeyword ); result.add(keyValuePair); } + ) + [ { keyValuePair.setUsingFormatJson( true ); } ] + )* + ) + )? + + [ + ( + { result.setOnNullType( JsonAggregateOnNullType.NULL ); } + ) + | + ( + { result.setOnNullType( JsonAggregateOnNullType.ABSENT ); } + ) + ] + + [ + ( + { result.setUniqueKeysType( JsonAggregateUniqueKeysType.WITH ); } + ) + | + ( + { result.setUniqueKeysType( JsonAggregateUniqueKeysType.WITHOUT ); } + ) + ] + ) + ")" + ) + | + ( + { result.setType( JsonFunctionType.ARRAY ); } + "(" + ( + LOOKAHEAD(2) ( + { result.setOnNullType( JsonAggregateOnNullType.NULL ); } + ) + | + expression=Expression() { functionExpression = new JsonFunctionExpression( expression ); result.add( functionExpression ); } + + [ LOOKAHEAD(2) { functionExpression.setUsingFormatJson( true ); } ] + ( + "," + expression=Expression() { functionExpression = new JsonFunctionExpression( expression ); result.add( functionExpression ); } + [ LOOKAHEAD(2) { functionExpression.setUsingFormatJson( true ); } ] + )* + )* + + [ + { result.setOnNullType( JsonAggregateOnNullType.ABSENT ); } + ] + + ")" + ) + ) + ) + + { + return result; + } +} + +JsonAggregateFunction JsonAggregateFunction() : { + JsonAggregateFunction result = new JsonAggregateFunction(); + Token token; + Expression expression; + List expressionOrderByList = null; + + Expression filter; + ExpressionList expressionList = null; + List olist = null; + WindowElement windowElement = null; + boolean partitionByBrackets = false; +} +{ + ( + ( + ( + "(" { result.setType( JsonFunctionType.OBJECT ); } + [ "KEY" { result.setUsingKeyKeyword( true ); } ] + ( token = | token = | token = | token = | token = | token = | token = ) { result.setKey( token.image ); } + ( ":" | "VALUE" {result.setUsingValueKeyword( true ); } ) + ( token = | token = ) { result.setValue( token.image ); } + + [ { result.setUsingFormatJson( true ); } ] + + [ + LOOKAHEAD(2) ( + { result.setOnNullType( JsonAggregateOnNullType.NULL ); } + ) + | + ( + { result.setOnNullType( JsonAggregateOnNullType.ABSENT ); } + ) + ] + + [ + ( + { result.setUniqueKeysType( JsonAggregateUniqueKeysType.WITH ); } + ) + | + ( + { result.setUniqueKeysType( JsonAggregateUniqueKeysType.WITHOUT ); } + ) + ] + ")" + ) + | + ( + + "(" { result.setType( JsonFunctionType.ARRAY ); } + expression=Expression() { result.setExpression( expression ); } + [ { result.setUsingFormatJson( true ); } ] + [ expressionOrderByList = OrderByElements() { result.setExpressionOrderByElements( expressionOrderByList ); } ] + + [ + LOOKAHEAD(2) ( + { result.setOnNullType( JsonAggregateOnNullType.NULL ); } + ) + | + ( + { result.setOnNullType( JsonAggregateOnNullType.ABSENT ); } + ) + ] + + ")" + ) + ) + + // -- Filter + [ LOOKAHEAD(2) "(" { result.setAnalyticType(AnalyticType.FILTER_ONLY); } filter = Expression() { result.setFilterExpression( filter ); } ")" ] + + // -- OVER + [ LOOKAHEAD(2) + {result.setAnalyticType(AnalyticType.OVER);} + "(" + [ + (LOOKAHEAD(ComplexExpressionList()) expressionList=ComplexExpressionList() + | "(" {partitionByBrackets = true;} expressionList=ComplexExpressionList() ")" ) + ] + [olist=OrderByElements() ] + [windowElement = WindowElement() ] + { + result.setPartitionExpressionList(expressionList, partitionByBrackets); + result.setOrderByElements(olist); + result.setWindowElement(windowElement); + } + ")" + ] + ) + + { + return result; + } +} + +IntervalExpression IntervalExpression() : { + IntervalExpression interval; + Token token = null; + Expression expr = null; + boolean signed = false; +} +{ + +{ interval = new IntervalExpression(); } + ["-" {signed=true;}] (token= | token= | token= | LOOKAHEAD(JdbcParameter()) expr = JdbcParameter() | expr = JdbcNamedParameter() | LOOKAHEAD(Function()) expr = Function() | expr = Column()) + { + if (expr != null) { + if (signed) expr = new SignedExpression('-', expr); + interval.setExpression(expr); + } else { + interval.setParameter((signed?"-":"") + token.image); + } + } + [ LOOKAHEAD(2) (token = | token = ) { interval.setIntervalType(token.image); } ] + { + return interval; + } +} + +IntervalExpression IntervalExpressionWithoutInterval(Expression expr) : { + IntervalExpression interval; + Token token; + boolean signed = false; +} +{ + { + interval = new IntervalExpression(false); + interval.setExpression(expr); + } + token = + { + interval.setIntervalType(token.image); + return interval; + } +} + +KeepExpression KeepExpression() : { + KeepExpression keep = new KeepExpression(); + Token token; + List list; +} +{ + "(" token = ( { keep.setFirst(true); } | { keep.setFirst(false); }) + list = OrderByElements() ")" + + { + keep.setName(token.image); + keep.setOrderByElements(list); + return keep; + } +} + + +void windowFun(AnalyticExpression retval):{ + ExpressionList expressionList = null; + boolean partitionByBrackets = false; + String windowName = null; + WindowDefinition winDef; +} { + ( + {retval.setType(AnalyticType.OVER);} + | + {retval.setType(AnalyticType.WITHIN_GROUP);} + ) + + ( + windowName = RelObjectName() { retval.setWindowName(windowName); } + | + winDef = windowDefinition() { retval.setWindowDefinition(winDef); } + + [ + LOOKAHEAD(2) "(" + [ + (LOOKAHEAD(ComplexExpressionList()) expressionList=ComplexExpressionList() + | "(" {partitionByBrackets = true;} expressionList=ComplexExpressionList() ")" ) + ] + ")" { winDef.setPartitionExpressionList(expressionList, partitionByBrackets); retval.setType(AnalyticType.WITHIN_GROUP_OVER); } + ] + ) +} + +WindowDefinition windowDefinition() : { + ExpressionList expressionList = null; + List olist = null; + WindowElement windowElement = null; + boolean partitionByBrackets = false; + WindowDefinition winDef = new WindowDefinition(); +} { + "(" + [ + (LOOKAHEAD(ComplexExpressionList()) expressionList=ComplexExpressionList() + | "(" {partitionByBrackets = true;} expressionList=ComplexExpressionList() ")" ) + ] + [olist=OrderByElements() ] + [windowElement = WindowElement() ] + { + winDef.setPartitionExpressionList(expressionList, partitionByBrackets); + winDef.setOrderByElements(olist); + winDef.setWindowElement(windowElement); + } + ")" + { return winDef; } +} + +AnalyticExpression AnalyticExpression(Function function) : +{ + AnalyticExpression retval = new AnalyticExpression(function); + Expression filter = null; +} +{ + ( + ( + "(" {retval.setType(AnalyticType.FILTER_ONLY);} filter = Expression() ")" + [ LOOKAHEAD(2) windowFun(retval) ] + ) + | windowFun(retval) + ) + { + retval.setFilterExpression(filter); + return retval; + } +} + +WindowElement WindowElement(): +{ + WindowElement windowElement = new WindowElement(); + WindowRange range = new WindowRange(); + WindowOffset offset = null; +} +{ + ( { windowElement.setType(WindowElement.Type.ROWS); } | { windowElement.setType(WindowElement.Type.RANGE); } ) + ( ( + { windowElement.setRange(range); } + offset = WindowOffset() { range.setStart(offset); } + offset = WindowOffset() { range.setEnd(offset); } + ) + | + offset = WindowOffset() { windowElement.setOffset(offset); } + ) + + { + return windowElement; + } +} + +WindowOffset WindowOffset(): +{ + WindowOffset offset = new WindowOffset(); + Expression expr = null; +} +{ + ( + ( + ( { offset.setType(WindowOffset.Type.PRECEDING); } | + { offset.setType(WindowOffset.Type.FOLLOWING); } ) + ) + | + LOOKAHEAD(2) ( { offset.setType(WindowOffset.Type.CURRENT); } ) + | + ( expr = SimpleExpression() { + offset.setType(WindowOffset.Type.EXPR); + offset.setExpression(expr); + } + ( { offset.setType(WindowOffset.Type.PRECEDING); } | { offset.setType(WindowOffset.Type.FOLLOWING); } ) + ) + ) + + { + return offset; + } +} + +ExtractExpression ExtractExpression() : +{ + ExtractExpression retval = new ExtractExpression(); + String fieldName = null; + Token token = null; + Expression expr = null; +} +{ + + "(" + ( fieldName=RelObjectName() { retval.setName(fieldName); } | token= { retval.setName(token.image); } ) + + expr=SimpleExpression() { retval.setExpression(expr); } + ")" + { + return retval; + } +} + +CastExpression ImplicitCast() #ImplicitCast: +{ + ColDataType colDataType; + Token tk1; + Token tk2; + + int precision = -1; + int scale = -1; +} +{ + colDataType = DataType() + ( + tk2 = + | + tk2 = + | + tk2 = + ) + { + CastExpression castExpression = new CastExpression(colDataType, tk2.image); + return castExpression; + } +} + +CastExpression CastExpression(): +{ + Token keyword; + CastExpression retval; + ColumnDefinition columnDefinition; + ColDataType type; + Expression expression = null; + boolean useCastKeyword; + Token formatCharLiteral; +} +{ + ( + keyword= + | + keyword= + | + keyword= + | + keyword= + ) + { + retval = new CastExpression(keyword.image); + } + "(" + expression=SimpleExpression() + { retval.setUseCastKeyword(true); } + ( + LOOKAHEAD(2) ( + + "(" + columnDefinition=ColumnDefinition() { retval.addColumnDefinition(columnDefinition); } + ( "," columnDefinition=ColumnDefinition() { retval.addColumnDefinition(columnDefinition); } )* + ")" + ) + | + type=ColDataType() { retval.setColDataType(type); } + ) + + // BigQuery FORMAT clause + // https://cloud.google.com/bigquery/docs/reference/standard-sql/conversion_functions#cast_as_date + [ formatCharLiteral= { retval.setFormat(formatCharLiteral.image); } ] + + ")" + + { + retval.setLeftExpression(expression); + return retval; + } +} + +Expression CaseWhenExpression() #CaseWhenExpression: +{ + CaseExpression caseExp = new CaseExpression(); + Expression switchExp = null; + WhenClause clause; + List whenClauses = new ArrayList(); + Expression elseExp = null; +} +{ + { caseCounter++; } + [ LOOKAHEAD(2) switchExp=Expression() ] + ( clause=WhenThenSearchCondition() { whenClauses.add(clause); } )+ + [ + + ( + LOOKAHEAD({getAsBoolean(Feature.allowComplexParsing) && !interrupted}) elseExp=Expression() + | elseExp=SimpleExpression() + ) + ] + { caseCounter--; } + { + caseExp.setSwitchExpression(switchExp); + caseExp.setWhenClauses(whenClauses); + caseExp.setElseExpression(elseExp); + return caseExp; + } +} + +WhenClause WhenThenSearchCondition(): +{ + WhenClause whenThen = new WhenClause(); + Expression whenExp; + Expression thenExp; +} +{ + whenExp=Expression() + + ( + LOOKAHEAD({getAsBoolean(Feature.allowComplexParsing) && !interrupted}) thenExp=Expression() + | + thenExp=SimpleExpression() + ) + { + whenThen.setWhenExpression(whenExp); + whenThen.setThenExpression(thenExp); + return whenThen; + } +} + +RowConstructor RowConstructor(): { + Token token; + ParenthesedExpressionList expressions; +} { + token= + expressions = ParenthesedExpressionList() + { + return new RowConstructor(token.image, expressions); + } +} + +/** +TODO: VariableExpression should be a standalone class with more operations available. +*/ +EqualsTo VariableExpression(): { + Expression left; + Expression right; +} { + left = UserVariable() "=" right = SimpleExpression() + { + EqualsTo equals = new EqualsTo(); + equals.setLeftExpression(left); + equals.setRightExpression(right); + return equals; + } +} + +Execute Execute(): { + Token token; + ObjectNames funcName; + ExpressionList expressionList = null; + Execute execute = new Execute(); + List namedExprList; + Expression expr; +} +{ + ( { execute.setExecType(Execute.ExecType.EXEC); } + | { execute.setExecType(Execute.ExecType.EXECUTE); } + | { execute.setExecType(Execute.ExecType.CALL); } ) + + funcName=RelObjectNames() { execute.setName(funcName.getNames()); } + + ( + LOOKAHEAD(2) expressionList=ExpressionList() { execute.setExprList(expressionList); } + )? + + { + return execute; + } +} + +FullTextSearch FullTextSearch() : { + Token searchModifier; + Token againstValue; + JdbcParameter jdbcParameter; + JdbcNamedParameter jdbcNamedParameter; + FullTextSearch fs = new FullTextSearch(); + ExpressionList matchedColumns; +} +{ + "(" matchedColumns=ColumnList() ")" + "(" + ( + againstValue= { fs.setAgainstValue(new StringValue(againstValue.image)); } + | + jdbcParameter=JdbcParameter() { fs.setAgainstValue( jdbcParameter ); } + | + jdbcNamedParameter=JdbcNamedParameter() { fs.setAgainstValue( jdbcNamedParameter ); } + ) + [ + ( + searchModifier="IN NATURAL LANGUAGE MODE" + | searchModifier="IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION" + | searchModifier="IN BOOLEAN MODE" + | searchModifier="WITH QUERY EXPANSION" + ) + { fs.setSearchModifier(searchModifier.image); } + ] + ")" + { + fs.setMatchColumns(matchedColumns); + return fs; + } +} + +LambdaExpression LambdaExpression() #LambdaExpression: +{ + String s; + ArrayList identifiers = new ArrayList(); + Expression expression; + LambdaExpression lambdaExpression; +} +{ +// wip, right now the Grammar works but collides with Multi Value Lists +// ( +// LOOKAHEAD(3) "(" +// s = RelObjectName() { identifiers.add(s); } +// ( "," s = RelObjectName() { identifiers.add(s); } )* +// ")" +// ) +// | + ( + s = RelObjectName() { identifiers.add(s); } + ) + + "->" + expression = Expression() + { + lambdaExpression = new LambdaExpression(identifiers, expression); + linkAST(lambdaExpression,jjtThis); + return lambdaExpression; + } +} + +Function Function() #Function: +{ + Function function; +} +{ + ( + "{" function = InternalFunction(true) "}" + | LOOKAHEAD( SimpleFunction(), { getAsBoolean(Feature.allowComplexParsing) }) function = SimpleFunction() + | LOOKAHEAD(3) function = SpecialStringFunctionWithNamedParameters() + | function = InternalFunction(false) + ) + { + linkAST(function,jjtThis); + return function; + } +} + +Function SpecialStringFunctionWithNamedParameters() : +{ + Token funcName; + NamedExpressionList namedExpressionList = null; + ExpressionList expressionList = null; + List orderByList; +} +{ + funcName = + + "(" + ( + LOOKAHEAD(NamedExpressionListExprFirst(), { getAsBoolean(Feature.allowComplexParsing) }) namedExpressionList = NamedExpressionListExprFirst() + | + expressionList=ExpressionList() + ) + ")" + + { + return new Function() + .withName(funcName.image) + .withNamedParameters(namedExpressionList) + .withParameters(expressionList); + } +} + +// a simplified function with only one parameter +// useful for parsing nested functions fast +Function SimpleFunction(): +{ + Function function = new Function(); + ObjectNames name; + Expression expr=null; + Expression attributeExpression = null; + Column attributeColumn = null; +} +{ + name = RelObjectNames() + "(" + [ + ( + "*" { expr = new AllColumns(); } + | + LOOKAHEAD( AllTableColumns() ) expr=AllTableColumns() + | + LOOKAHEAD( 3 ) expr = ParenthesedSelect() + | + LOOKAHEAD( SimpleFunction() ) expr = SimpleFunction() + | + LOOKAHEAD( RegularCondition() ) expr = RegularCondition() + | + LOOKAHEAD( SimpleExpressionList() ) expr = SimpleExpressionList() + ) + ] + ")" + { + function.setName(name.getNames()); + if (expr!=null) { + function.setParameters(expr); + } + } + + [ "." ( + // tricky lookahead since we do need to support the following constructs + // schema.f1().f2() - Function with Function Column + // schema.f1().f2.f3 - Function with Attribute Column + LOOKAHEAD( Function() ) attributeExpression=Function() { function.setAttribute(attributeExpression); } + | + attributeColumn=Column() { function.setAttribute(attributeColumn); } + ) + ] + + { + return function; + } +} + +Function InternalFunction(boolean escaped): +{ + Token prefixToken = null; + Function retval = new Function(); + ObjectNames funcName; + ExpressionList expressionList = null; + KeepExpression keep = null; + Expression expr = null; + Expression attributeExpression = null; + Column attributeColumn = null; + List orderByList; + Limit limit; +} +{ + [ LOOKAHEAD(2) prefixToken = ] + funcName = RelObjectNames() { if (prefixToken!=null) funcName.getNames().add(0, prefixToken.image ); } + + "(" + [ + LOOKAHEAD(2) [ + LOOKAHEAD(2) ( + { retval.setDistinct(true); } + | { retval.setAllColumns(true); } + | { retval.setUnique(true); } + ) + ] + ( + LOOKAHEAD(3) expressionList=ExpressionList() [ orderByList = OrderByElements() { retval.setOrderByElements(orderByList); } ] + | + expr = Select() { expressionList = new ExpressionList(expr); } + ) + ] + + [ + ( + expr = Expression() { retval.setHavingClause( "MIN", expr ); } + | + expr = Expression() { retval.setHavingClause( "MAX", expr ); } + ) + ] + + [ + ( + { retval.setNullHandling(Function.NullHandling.IGNORE_NULLS); } + ) + | + ( + { retval.setNullHandling(Function.NullHandling.RESPECT_NULLS); } + ) + ] + + [ + limit = PlainLimit() { retval.setLimit(limit); } + ] + + ")" + + + [ "." ( + // tricky lookahead since we do need to support the following constructs + // schema.f1().f2() - Function with Function Column + // schema.f1().f2.f3 - Function with Attribute Column + LOOKAHEAD( Function() ) attributeExpression=Function() { retval.setAttribute(attributeExpression); } + | + attributeColumn=Column() { retval.setAttribute(attributeColumn); } + ) + ] + + [ + ( + + { + retval.setNullHandling(Function.NullHandling.IGNORE_NULLS); + retval.setIgnoreNullsOutside(true); + } + ) + | + ( + + { + retval.setNullHandling(Function.NullHandling.RESPECT_NULLS); + retval.setIgnoreNullsOutside(true); + } + ) + ] + + [ LOOKAHEAD(2) keep = KeepExpression() ] + + { + retval.setEscaped(escaped); + retval.setParameters(expressionList); + retval.setName(funcName.getNames()); + retval.setKeep(keep); + return retval; + } +} + +XMLSerializeExpr XMLSerializeExpr(): { + XMLSerializeExpr result; + Expression expression; + List orderByElements = null; + ColDataType dataType; +} +{ + + "(" + "(" + "(" expression=SimpleExpression() ")" + [ orderByElements=OrderByElements() ] + ")" + dataType=ColDataType() ")" + { + result = new XMLSerializeExpr(); + result.setExpression(expression); + result.setOrderByElements(orderByElements); + result.setDataType(dataType); + return result; + } +} + + +MySQLGroupConcat MySQLGroupConcat():{ + MySQLGroupConcat retval = new MySQLGroupConcat(); + ExpressionList expressionList = null; + List orderByList = null; + Token t; +} +{ + "(" + [ { retval.setDistinct(true); } ] + expressionList = ExpressionList() + [ orderByList = OrderByElements() { retval.setOrderByElements(orderByList); } ] + [ t= { retval.setSeparator(t.image); } ] + ")" + { + retval.setExpressionList(expressionList); + return retval; + } +} + +TableFunction TableFunction(): +{ + Token prefix = null; + Function function; + TableFunction functionItem; +} +{ + [ prefix = ] + function=Function() + { + return prefix!=null + ? new TableFunction(prefix.image, function) + : new TableFunction(function); + } +} + +List ColumnNamesWithParamsList() : { + List colNames = new ArrayList(); + String columnName; + List parameter = null; +} +{ + "(" + columnName=RelObjectName() + + { parameter = null; } + [ parameter = CreateParameter() ] + { + colNames.add(new Index.ColumnParams(columnName,parameter)); + } + + ( + "," + columnName=RelObjectName() + + { parameter = null; } + [ parameter = CreateParameter() ] + { + colNames.add(new Index.ColumnParams(columnName,parameter)); + } + )* + + ")" + + { return colNames; } +} + +Index Index(): { + ObjectNames name; +} +{ + name= RelObjectNames() { return new Index().withName(name.getNames()).withType(""); } +} + +CreateIndex CreateIndex(): +{ + CreateIndex createIndex = new CreateIndex(); + Table table = null; + List colNames; + Token using; + Index index = null; + List parameter = new ArrayList(); + List tailParameters = new ArrayList(); + List name; +} +{ + [ parameter=CreateParameter() ] + + + [ LOOKAHEAD(2) { createIndex.setUsingIfNotExists(true);} ] + index = Index() { index.setType(parameter.isEmpty() ? null : parameter.get(0)); } + ( + LOOKAHEAD(3)( + table=Table() + [ using= { index.setUsing(using.image); } ] + ) + | + ( + [ using= { + index.setUsing(using.image); + createIndex.setIndexTypeBeforeOn(true); + } + ] + table=Table() + ) + ) + colNames = ColumnNamesWithParamsList() + ( LOOKAHEAD(2) parameter=CreateParameter() { tailParameters.addAll(parameter); } )* + { + index.setColumns(colNames); + createIndex.setIndex(index); + createIndex.setTable(table); + createIndex.setTailParameters(tailParameters); + return createIndex; + } +} + +ColumnDefinition ColumnDefinition(): { + ColumnDefinition coldef; + String columnName; + ColDataType colDataType; + List columnSpecs = new ArrayList(); + List parameter; +} { + columnName=RelObjectName() + + colDataType = ColDataType() + + ( LOOKAHEAD(2) parameter=CreateParameter() { columnSpecs.addAll(parameter); } )* + + { + coldef = new ColumnDefinition(); + coldef.setColumnName(columnName); + coldef.setColDataType(colDataType); + if (columnSpecs.size() > 0) + coldef.setColumnSpecs(columnSpecs); + return coldef; + } +} + +CreateSchema CreateSchema(): +{ + Token tk = null; + CreateTable table = null; + CreateView view = null; + CreateSchema schema = new CreateSchema(); + //schema.setSchemaName(System.getProperty("user.name")); + //schema.setAuthorization(System.getProperty("user.name")); + List schemaPath = null; + List statements = new ArrayList(); +} +{ + + [ ( tk= | tk=) { schema.setSchemaName(tk.image); } ] + [ + (tk= | tk=) { schema.setAuthorization(tk.image); } + ] + + [schemaPath=PathSpecification() { schema.setSchemaPath(schemaPath); }] + + ( + + + table = CreateTable(false) + { + table.getTable().setSchemaName(schema.getSchemaName()); + schema.addStatement(table); + } + | view = CreateView(false) + { + view.getView().setSchemaName(schema.getSchemaName()); + schema.addStatement(view); + } + + )* + { + return schema; + } +} + +List PathSpecification(): +{ + Token tk; + List pathList = new ArrayList(); +} +{ + (tk=|tk=) { pathList.add(tk.image); } + ("," (tk=|tk=) { pathList.add(tk.image); })* + { + return pathList; + } +} + +CreateTable CreateTable(boolean isUsingOrReplace): +{ + CreateTable createTable = new CreateTable(); + Table table = null; + List columnDefinitions = new ArrayList(); + List columnSpecs = null; + List tableOptions = new ArrayList(); + List createOptions = new ArrayList(); + String columnName; + Token tk = null; + Token tk2 = null; + Token tk3 = null; + String sk3 = null; + ColDataType colDataType = null; + String stringList = null; + ColumnDefinition coldef = null; + List indexes = new ArrayList(); + List colNames = null; + List colNames2 = null; + Index index = null; + ForeignKeyIndex fkIndex = null; + List parameter = new ArrayList(); + List idxSpec = new ArrayList(); + Table fkTable = null; + SpannerInterleaveIn interleaveIn = null; + Select select = null; + Table likeTable = null; + CheckConstraint checkCs = null; + ExcludeConstraint excludeC = null; + RowMovement rowMovement = null; + ReferentialAction.Action action = null; + String tableColumn = null; + List columns = new ArrayList(); +} +{ + { createTable.setOrReplace(isUsingOrReplace);} + [ { createTable.setUnlogged(true); } ] + + // table options, not required but 1 or none + [ tk= { createOptions.add(tk.image);} ] + /* [ [ (tk= | tk=) {createOptions.add(tk.image);} ] + ( tk= | tk= ) {createOptions.add(tk.image);}] */ + + (parameter = CreateParameter() { createOptions.addAll(parameter); })* + + + [ LOOKAHEAD(2) { createTable.setIfNotExists(true); }] + table=Table() + [ LOOKAHEAD(2) ( + LOOKAHEAD(3) + ("(" tableColumn=RelObjectName() { columns.add(tableColumn); } ("," tableColumn=RelObjectName() { columns.add(tableColumn); } )* ")") + | + ("(" + coldef = ColumnDefinition() + + { columnDefinitions.add(coldef); } + + ( + "," + + ( + LOOKAHEAD(3) ( + tk= + sk3=RelObjectName() + /* colNames=ColumnsNamesList() */ + colNames = ColumnNamesWithParamsList() + { idxSpec.clear(); } + ( parameter=CreateParameter() { idxSpec.addAll(parameter); } )* + { + index = new Index().withType(tk.image).withName(sk3).withColumns(colNames).withIndexSpec(new ArrayList(idxSpec)); + indexes.add(index); + } + ) + | + LOOKAHEAD(3) ( + { + index = new NamedConstraint(); + } + [ sk3=RelObjectName() {index.setName(sk3);} ] + + (tk= tk2= {index.setType(tk.image + " " + tk2.image);} + | tk= [ tk2= ] {index.setType(tk.image + (tk2!=null?" " + tk2.image:""));} + ) + /* colNames=ColumnsNamesList() */ + colNames = ColumnNamesWithParamsList() + { idxSpec.clear(); } + ( parameter=CreateParameter() { idxSpec.addAll(parameter); } )* + { + index.withColumns(colNames).withIndexSpec(new ArrayList(idxSpec)); + indexes.add(index); + } + // reset Token to null forcefullly + { + tk2=null; + } + ) + | + LOOKAHEAD(3) ( {tk=null;} + [ tk= ] [ tk3= ] tk2= + sk3=RelObjectName() + /* colNames=ColumnsNamesList() */ + colNames = ColumnNamesWithParamsList() + { idxSpec.clear(); } + ( parameter=CreateParameter() { idxSpec.addAll(parameter); } )* + { + index = new Index() + .withType((tk!=null?tk.image + " ":"") + (tk3!=null?tk3.image + " ":"") + tk2.image) + .withName(sk3) + .withColumns(colNames) + .withIndexSpec(new ArrayList(idxSpec)); + indexes.add(index); + } + ) + | + LOOKAHEAD(3)( + { + fkIndex = new ForeignKeyIndex(); + } + [ sk3=RelObjectName() {fkIndex.setName(sk3);} ] + tk= tk2= + /* colNames=ColumnsNamesList() */ + colNames = ColumnNamesWithParamsList() + { + fkIndex.withType(tk.image + " " + tk2.image).withColumns(colNames); + } + fkTable=Table() colNames2=ColumnsNamesList() + { + fkIndex.setTable(fkTable); + fkIndex.setReferencedColumnNames(colNames2); + indexes.add(fkIndex); + } + [LOOKAHEAD(2) ( (tk= | tk=) action = Action() + { fkIndex.setReferentialAction(ReferentialAction.Type.from(tk.image), action); } + )] + [LOOKAHEAD(2) ( (tk= | tk=) action = Action() + { fkIndex.setReferentialAction(ReferentialAction.Type.from(tk.image), action); } + )] + ) + | + LOOKAHEAD(3)( + [ sk3 = RelObjectName()] + {Expression exp = null;} + ("(" exp = Expression() ")")* { + checkCs = new CheckConstraint().withName(sk3).withExpression(exp); + indexes.add(checkCs); + } + ) + | + LOOKAHEAD(2) tk= {excludeC = new ExcludeConstraint(); Expression exp = null;} + (tk2= + ("(" exp = Expression() ")")* {excludeC.setExpression(exp);}) + { + indexes.add(excludeC); + } + | + ( + + coldef = ColumnDefinition() + + /* + columnName=RelObjectName() + + colDataType = ColDataType() + { + columnSpecs = new ArrayList(); + } + + ( parameter=CreateParameter() { columnSpecs.addAll(parameter); } )* + + { + coldef = new ColumnDefinition(); + coldef.setColumnName(columnName); + coldef.setColDataType(colDataType); + if (columnSpecs.size() > 0) + coldef.setColumnSpecs(columnSpecs); + columnDefinitions.add(coldef); + } */ + { columnDefinitions.add(coldef); } + ) + ) + )* + + ")" + ) + ) + ] + ( LOOKAHEAD(2, { getToken(1).kind != K_AS }) parameter=CreateParameter() { tableOptions.addAll(parameter); } )* + + // see https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_7002.htm#i2126725 + // table properties , all these are optional + [ rowMovement = RowMovement() { createTable.setRowMovement(rowMovement); }] + [ select = Select() { createTable.setSelect(select, false); }] + [ + ( LOOKAHEAD("(" Table() ")") "(" likeTable=Table() { createTable.setLikeTable(likeTable, true); } ")" + | likeTable=Table() { createTable.setLikeTable(likeTable, false); } ) + ] + [ interleaveIn = SpannerInterleaveIn( ) { createTable.setSpannerInterleaveIn(interleaveIn); } ] + { + createTable.setTable(table); + if (indexes.size() > 0) + createTable.setIndexes(indexes); + if (createOptions.size() > 0) + createTable.setCreateOptionsStrings(createOptions); + if (tableOptions.size() > 0) + createTable.setTableOptionsStrings(tableOptions); + if (columnDefinitions.size() > 0) + createTable.setColumnDefinitions(columnDefinitions); + if (columns.size() > 0) + createTable.setColumns(columns); + return createTable; + } +} + +SpannerInterleaveIn SpannerInterleaveIn(): +{ + Table table = null; + SpannerInterleaveIn.OnDelete action = null; +} +{ + table = Table() + [ + + ( + { action = SpannerInterleaveIn.OnDelete.NO_ACTION; } + | { action = SpannerInterleaveIn.OnDelete.CASCADE; } + ) + ] + { + return new SpannerInterleaveIn(table, action); + } +} + + +ColDataType DataType(): +{ + ColDataType colDataType = new ColDataType(); + Token prefix = null; + Token tk = null; + Token tk2 = null; + String schema; + String type=""; + List argumentsStringList = new ArrayList(); + List array = new ArrayList(); + List name; + ColDataType arrayType; + + int precision = -1; + int scale = -1; +} +{ + ( + LOOKAHEAD(2) tk= ( + ("<" arrayType = ColDataType() ">") { + colDataType.setDataType("ARRAY<" + arrayType.getDataType() + ">"); + } + ) + | + ( + ( tk= | tk= | tk = | tk = | tk = + | tk= | tk= | tk= | tk= + | tk= | tk= | tk= ) { type = tk.image; } + [ + // MySQL seems to allow: INT UNSIGNED + LOOKAHEAD(2) ( LOOKAHEAD(2) ( tk = | tk = | tk = + | tk= | tk= | tk= | tk= + | tk= | tk= | tk= ) { type += " " + tk.image; } )+ + ] + [ + LOOKAHEAD(2) "(" ( tk= { precision = Integer.valueOf(tk.image); } | tk= { precision = Integer.MAX_VALUE; } ) + [ "," tk = { scale = Integer.valueOf(tk.image); } ] + ")" + ] + { + colDataType = new ColDataType(type, precision, scale); + } + ) + ) + + { + return colDataType; + } +} + +ColDataType ColDataType(): +{ + ColDataType colDataType = new ColDataType(); + Token prefix = null; + Token tk = null; + Token tk2 = null; + String schema; + String type=""; + List argumentsStringList = new ArrayList(); + List array = new ArrayList(); + List name; + ColDataType arrayType; + + int precision = -1; + int scale = -1; +} +{ + ( + LOOKAHEAD(2) ( + colDataType = DataType() + ) + | + ( + tk= + | tk= + | tk= + | tk= + | tk= + | tk= + | tk= + | tk= + | tk= + | tk= + | tk= + | tk= + | tk= + | tk= + ) { schema = tk.image; } + + [ "." arrayType = ColDataType() { schema += "." + arrayType.toString(); } ] + { colDataType.setDataType(schema); } + ) + + [ + LOOKAHEAD(2) "(" {tk2 =null;} + ( + ( + ( + ( tk= | tk= ) [ LOOKAHEAD(2) (tk2= | tk2=) ] + ) + | + tk= + | + tk= + | + tk= + ) + { + argumentsStringList.add(tk.image + (tk2!=null?" " + tk2.image:"")); + } + + [ "," ] + )* + ")" + ] + [ LOOKAHEAD(2) ( LOOKAHEAD(2) "[" {tk=null;} [ tk= ] { array.add(tk!=null?Integer.valueOf(tk.image):null); } "]" )+ { colDataType.setArrayData(array); } ] + [ LOOKAHEAD(2) (tk= | tk=) { colDataType.setCharacterSet(tk.image); } ] + + { + if (argumentsStringList.size() > 0) + colDataType.setArgumentsStringList(argumentsStringList); + return colDataType; + } +} + +Analyze Analyze(): +{ + Analyze analyze = new Analyze(); + Table table = null; +} +{ + + table=Table() + + { + analyze.setTable(table); + return analyze; + } +} + +ExpressionList ColumnWithCommentList(): +{ + ExpressionList expressions = new ExpressionList(); + Column img = null; +} +{ + "(" + img=Column() { expressions.add(img); } + ( "," img=Column() { expressions.add(img); } )* + ")" + { + return expressions; + } +} + + +CreateView CreateView(boolean isUsingOrReplace): +{ + CreateView createView = new CreateView(); + Table view = null; + Select select = null; + ExpressionList columnNames = null; + Token tk = null; + List commentTokens = null; +} +{ + { createView.setOrReplace(isUsingOrReplace);} + [ + { createView.setForce(ForceOption.NO_FORCE); } + | { createView.setForce(ForceOption.FORCE); } + ] + [ { createView.setSecure(true);} ] + [ + { createView.setTemporary(TemporaryOption.TEMP); } + | { createView.setTemporary(TemporaryOption.TEMPORARY); } + | { createView.setTemporary(TemporaryOption.VOLATILE); } + ] + [ { createView.setMaterialized(true);} ] + view=Table() { createView.setView(view); } + [LOOKAHEAD(3) (tk= | tk=) { createView.setAutoRefresh(AutoRefreshOption.from(tk.image)); } ] + [LOOKAHEAD(3) {createView.setIfNotExists(true);}] + [ columnNames=ColumnWithCommentList( ) { createView.setColumnNames(columnNames); } ] + [ commentTokens=CreateViewTailComment( ) { createView.setViewCommentOptions(commentTokens); } ] + + select=Select( ) { createView.setSelect(select); } + [ LOOKAHEAD(2) { createView.setWithReadOnly(true); } ] + { return createView; } +} + +List CreateViewTailComment(): +{ + Token tk = null; + Token tk2 = null; + String op = null; + List result = new ArrayList(); +} +{ + tk= + [ "=" { op = "="; } ] + tk2 = { + result.add(""); + result.add(tk.image); + if (op != null) { + result.add(op); + } + result.add(tk2.image); + } + { return result;} +} + + +ReferentialAction.Action Action(): +{ + ReferentialAction.Action action = null; +} +{ + ( { action=ReferentialAction.Action.CASCADE;} + | + {action=ReferentialAction.Action.RESTRICT;} + | + {action=ReferentialAction.Action.NO_ACTION;} + | + ( + {action=ReferentialAction.Action.SET_NULL;} + | + {action=ReferentialAction.Action.SET_DEFAULT;} + ) + ) + { return action; } +} + +AlterView AlterView(boolean useReplace): +{ + AlterView alterView = new AlterView(); + Table view = null; + Select select = null; + List columnNames = null; +} +{ + view=Table() { alterView.setView(view); alterView.setUseReplace(useReplace); } + [ columnNames = ColumnsNamesList() { alterView.setColumnNames(columnNames); } ] + + select=Select() + { + alterView.setSelect(select); + return alterView; + } +} + +List CreateParameter(): +{ + String retval = ""; + Token tk = null, tk2 = null; + Expression exp = null; + ColDataType colDataType; + List param = new ArrayList(); +} +{ + ( + // Postgres: nextval('public.actor_actor_id_seq'::regclass) + ( "(" tk= "::" colDataType = ColDataType() ")" ) + { + param.add("NextVal( " + tk.image + "::" + colDataType + ")" ); + } + | + ( + (tk= | tk=) + { retval+=tk.image; } + + [ + "." + (tk2= | tk2=) + { retval+="."+tk2.image; } + ] + { param.add(retval); } + ) + | + LOOKAHEAD(3) [ { param.add("USING INDEX"); }] retval=RelObjectName() + { param.add("TABLESPACE " + retval); } + | + ( + tk= | tk= | tk= | tk= | tk= + | tk= | tk= | tk= | tk= | tk= | tk= + | tk= | tk= | tk= | tk= | tk= + | tk= | tk= | tk= | tk= + | tk= | tk= | tk= | tk= | tk= + | tk= | tk= | tk= | tk= | tk= + | tk= | tk= | tk= | tk= | tk= | tk= + | tk= | tk= | tk= | tk= | tk= | tk = | tk = + | tk="=" + ) + { param.add(tk.image); } + | + ( tk= | tk= | tk= ) [ LOOKAHEAD(2) "(" exp = Expression() ")" ] + { + param.add(tk.image); + if (exp!=null) { + param.add("(" + exp + ")"); + } + } + | + ( + [ ( tk="+" | tk="-" ) { retval = tk.image; } ] + tk= | tk= + ) + { param.add( retval + tk.image ); } + | + retval=AList() { param.add(retval); } + | + (tk= tk2=) { param.add(tk.image); param.add(tk2.image);} + | + ( exp=ArrayConstructor(true)) { param.add(exp.toString()); } + | + tk="::" colDataType = ColDataType() { param.add(tk.image); param.add(colDataType.toString()); } + ) + {return param;} +} + +// row_movement_clause https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_7002.htm#i2204697 +RowMovement RowMovement(): +{ + RowMovement rowMovement = new RowMovement(); +} +{ + ( + ( ) { rowMovement.setMode(RowMovementMode.ENABLE); } + | + ( ) { rowMovement.setMode(RowMovementMode.DISABLE); } + ) + { return rowMovement;} +} + +String AList(): +{ + StringBuilder retval = new StringBuilder("("); + Token tk = null; + String name = null; +} +{ + "(" + + ( + ( (tk= | tk= | tk=) { retval.append(tk.image); } + | (name=RelObjectNameWithoutValue()) { retval.append(name); }) + [("," {retval.append(",");} | "=" {retval.append("=");})] )* + + ")" + { + retval.append(")"); + return retval.toString(); + } +} + +String ColumnsNamesListItem(): +{ + Token tk = null; + String item = null; +} +{ + ( item = RelObjectName() ) + [ LOOKAHEAD(2) "(" tk = ")" { item = item + "(" + tk.image + ")"; } ] + { + return item; + } +} + +List ColumnsNamesList(): +{ + List retval = new ArrayList(); + String img = null; +} +{ + "(" + img=ColumnsNamesListItem() { retval.add(img); } + ( "," img=ColumnsNamesListItem() { retval.add(img); } )* + + ")" + { + return retval; + } +} + +String FuncArgsListItem(): +{ + Token tk = null; + String argName = null; + String argType = null; +} +{ + ( + LOOKAHEAD(2) ( + argName = RelObjectName() + argType = RelObjectName() + [ "(" tk = ")" { argType = argType + "(" + tk.image + ")"; } ] + ) + | + ( + argType = RelObjectName() + [ "(" tk = ")" { argType = argType + "(" + tk.image + ")"; } ] + ) + ) + { + return argName != null ? String.format("%s %s", argName, argType) : argType; + } +} + +List FuncArgsList(): +{ + List retval = null; + String img = null; +} +{ + "(" + { retval = new ArrayList(); } + [ + img=FuncArgsListItem() { retval.add(img); } + ( "," img=FuncArgsListItem() { retval.add(img); } )* + ] + ")" + { + return retval; + } +} + +Drop Drop(): +{ + Drop drop = new Drop(); + Token tk = null; + Table name; + List dropArgs = new ArrayList(); + List funcArgs = null; + boolean useTemporary = false; +} +{ + + [ { drop.setMaterialized(true);} ] + ( + tk= + | + ( + [ {useTemporary=true;} ] tk= + ) + | + tk= + | + tk= + | + tk= + | + tk= + | + tk= + ) + { drop.setType(tk.image); } + + [ LOOKAHEAD(2) {drop.setIfExists(true);} ] + + name = Table() { drop.setName(name); } + [ LOOKAHEAD(2) funcArgs = FuncArgsList() ] + ((tk= | tk= | tk= | tk=) { dropArgs.add(tk.image); })* + + { + if (dropArgs.size() > 0) { + drop.setParameters(dropArgs); + } + if (drop.getType().equals("FUNCTION")) { + drop.getTypeToParameters().put("FUNCTION", funcArgs); + } + + drop.setUsingTemporary(useTemporary); + + return drop; + } +} + +Truncate Truncate(): +{ + Truncate truncate = new Truncate(); + Table table; +} +{ +/** +* TRUNCATE can be followed directly by the table name in Postgresql +* See: https://www.postgresql.org/docs/current/sql-truncate.html +* +* TRUNCATE [ TABLE ] [ ONLY ] name [ * ] [, ... ] +* [ RESTART IDENTITY | CONTINUE IDENTITY ] [ CASCADE | RESTRICT ] +* +*/ + [LOOKAHEAD(2) {truncate.setTableToken(true);}] [ {truncate.setOnly(true);}] + table=Table() { truncate.setTable(table); truncate.setCascade(false); } [ {truncate.setCascade(true);} ] + { + return truncate; + } +} + + +AlterExpression.ColumnDataType AlterExpressionColumnDataType(): +{ + String columnName = null; + boolean withType = false; + ColDataType dataType = null; + List columnSpecs = null; + List parameter = null; +} +{ + columnName = RelObjectName() { columnSpecs = new ArrayList(); } + ( LOOKAHEAD(2) { withType = true; } )? + ( LOOKAHEAD(2) dataType = ColDataType() )? + ( LOOKAHEAD(2) parameter = CreateParameter() { columnSpecs.addAll(parameter); } )* + { + return new AlterExpression.ColumnDataType(columnName, withType, dataType, columnSpecs); + } +} + +AlterExpression.ColumnDropNotNull AlterExpressionColumnDropNotNull(): +{ + String columnName = null; + boolean withNot = false; + ColDataType dataType = null; + List columnSpecs = null; + List parameter = null; +} +{ + columnName = RelObjectName() + + ( { withNot = true; } )? + + { + return new AlterExpression.ColumnDropNotNull(columnName, withNot); + } +} + +AlterExpression.ColumnDropDefault AlterExpressionColumnDropDefault(): +{ + String columnName = null; + boolean withNot = false; + ColDataType dataType = null; + List columnSpecs = null; + List parameter = null; +} +{ + columnName = RelObjectName() + { + return new AlterExpression.ColumnDropDefault(columnName); + } +} + +List AlterExpressionConstraintState(): +{ + List retval = new ArrayList(); +} +{ + ( + ( + {retval.add(new DeferrableConstraint(false));} + ) + | + ( + {retval.add(new DeferrableConstraint(true));} + ) + | + ( + {retval.add(new ValidateConstraint(false));} + ) + | + ( + {retval.add(new ValidateConstraint(true));} + ) + | + ( + {retval.add(new EnableConstraint(false));} + ) + | + ( + {retval.add(new EnableConstraint(true));} + ) + )* + { + return retval; + } +} + +Index IndexWithComment(Index index): +{ + Token tk = null; +} +{ + tk= { + index.setCommentText(tk.image); + } + { + return index; + } +} + +/** +* This production needs refactoring to multiple smaller productions. The target class should +* be splitted as well. +*/ +AlterExpression AlterExpression(): +{ + AlterExpression alterExp = new AlterExpression(); + Token tk; + Token tk2 = null; + String sk3 = null; + String sk4 = null; + ColDataType dataType; + List columnNames = null; + List constraints = null; + ForeignKeyIndex fkIndex = null; + Index index = null; + Table fkTable = null; + AlterExpression.ColumnDataType alterExpressionColumnDataType = null; + AlterExpression.ColumnDropNotNull alterExpressionColumnDropNotNull = null; + AlterExpression.ColumnDropDefault alterExpressionColumnDropDefault = null; + ReferentialAction.Action action = null; + String truncatePartitionName = null; + + // for captureRest() + List tokens = new LinkedList(); +} +{ + + ( + ( + ( + { alterExp.setOperation(AlterOperation.ADD); } + | + { alterExp.setOperation(AlterOperation.ALTER); } + | + { alterExp.setOperation(AlterOperation.MODIFY); } + ) + + ( + LOOKAHEAD(2) ( columnNames=ColumnsNamesList() { alterExp.setPkColumns(columnNames); }) + + constraints=AlterExpressionConstraintState() { alterExp.setConstraints(constraints); } + [ sk4=RelObjectName() { alterExp.addParameters("USING", sk4); }] + | + LOOKAHEAD(2) ( + (tk= { alterExp.setUk(true); } | tk=) + sk3 = RelObjectName() + columnNames = ColumnsNamesList() + { + index = new Index().withType(tk.image).withName(sk3).withColumnsNames(columnNames); + alterExp.setIndex(index); + } + constraints=AlterExpressionConstraintState() { alterExp.setConstraints(constraints); } + [ sk4=RelObjectName() { alterExp.addParameters("USING", sk4); }] + [ LOOKAHEAD(2) index = IndexWithComment(index) { alterExp.setIndex(index); } ] + ) + | + LOOKAHEAD(2) ( + sk3=RelObjectName() tk= { alterExp.withColumnName(sk3).withCommentText(tk.image); } + ) + | + LOOKAHEAD(3) ( + ( LOOKAHEAD(2) { alterExp.hasColumn(true); } )? + [ { alterExp.setUseIfNotExists(true); } ] + ( + LOOKAHEAD(4) ( + "(" + { alterExp.useBrackets(true);} + alterExpressionColumnDataType = AlterExpressionColumnDataType() { + alterExp.addColDataType(alterExpressionColumnDataType); + } + ( + "," + alterExpressionColumnDataType = AlterExpressionColumnDataType() { + alterExp.addColDataType(alterExpressionColumnDataType); + } + )* + ")" + ) + | + LOOKAHEAD(2) alterExpressionColumnDataType = AlterExpressionColumnDataType() + { alterExp.addColDataType(alterExpressionColumnDataType); } + | + LOOKAHEAD(3) alterExpressionColumnDropNotNull = AlterExpressionColumnDropNotNull() + { alterExp.addColDropNotNull( alterExpressionColumnDropNotNull);} + | + alterExpressionColumnDropDefault = AlterExpressionColumnDropDefault() + { alterExp.addColDropDefault( alterExpressionColumnDropDefault); } + ) + ) + | + ( + "(" alterExpressionColumnDataType = AlterExpressionColumnDataType() { alterExp.addColDataType(alterExpressionColumnDataType); } + ("," + alterExpressionColumnDataType = AlterExpressionColumnDataType() { alterExp.addColDataType(alterExpressionColumnDataType); } + )* + ")" + ) + | + ( (( { alterExp.setUk(true); } | ) (tk= | tk=) { alterExp.setUkName(tk.image); } )? + columnNames=ColumnsNamesList() { alterExp.setUkColumns(columnNames); } + [ sk4=RelObjectName() { alterExp.addParameters("USING", sk4); }] + [ LOOKAHEAD(2) index = IndexWithComment(index) { alterExp.setIndex(index); } ] + ) + | + //following two choices regarding foreign keys should be merged + ( columnNames=ColumnsNamesList() { alterExp.setFkColumns(columnNames); columnNames = null; } + /* + tk= [ columnNames=ColumnsNamesList() ] + { alterExp.setFkSourceTable(tk.image); alterExp.setFkSourceColumns(columnNames); } + */ + fkTable=Table() [ LOOKAHEAD(2) columnNames=ColumnsNamesList() ] + { + alterExp.setFkSourceSchema(fkTable.getSchemaName()); + alterExp.setFkSourceTable(fkTable.getName()); + alterExp.setFkSourceColumns(columnNames); + } + + [LOOKAHEAD(2) ( (tk= | tk=) action = Action() + { alterExp.setReferentialAction(ReferentialAction.Type.from(tk.image), action); } + )] + [LOOKAHEAD(2) ( (tk= | tk=) action = Action() + { alterExp.setReferentialAction(ReferentialAction.Type.from(tk.image), action); } + )] + ) + | + ( + sk3=RelObjectName() + ( + ( tk= tk2= + columnNames=ColumnsNamesList() + { + fkIndex = new ForeignKeyIndex() + .withName(sk3) + .withType(tk.image + " " + tk2.image) + .withColumnsNames(columnNames); + columnNames = null; + } + fkTable=Table() [ LOOKAHEAD(2) columnNames=ColumnsNamesList() ] + { + fkIndex.withTable(fkTable).withReferencedColumnNames(columnNames); + alterExp.setIndex(fkIndex); + } + + [LOOKAHEAD(2) ( (tk= | tk=) action = Action() + { fkIndex.setReferentialAction(ReferentialAction.Type.from(tk.image), action); } + )] + [LOOKAHEAD(2) ( (tk= | tk=) action = Action() + { fkIndex.setReferentialAction(ReferentialAction.Type.from(tk.image), action); } + )] + constraints=AlterExpressionConstraintState() { alterExp.setConstraints(constraints); } + ) + | + ( tk= tk2= + columnNames=ColumnsNamesList() + { + index = new NamedConstraint() + .withName(sk3) + .withType(tk.image + " " + tk2.image) + .withColumnsNames(columnNames); + alterExp.setIndex(index); + } + constraints=AlterExpressionConstraintState() { alterExp.setConstraints(constraints); } + [ sk4=RelObjectName() { alterExp.addParameters("USING", sk4); }] + [ LOOKAHEAD(2) index = IndexWithComment(index) { alterExp.setIndex(index); } ] + ) + | + ( + {Expression exp = null;} (LOOKAHEAD(2) "(" exp = Expression() ")")* { + CheckConstraint checkCs = new CheckConstraint().withName(sk3).withExpression(exp); + alterExp.setIndex(checkCs); + } + ) + | + ( + tk= (tk2= { alterExp.setUk(true); } | tk2=)? + columnNames=ColumnsNamesList() + { + index = new NamedConstraint() + .withName(sk3) + .withType(tk.image + (tk2!=null?" " + tk2.image:"")) + .withColumnsNames(columnNames); + alterExp.setIndex(index); + } + constraints=AlterExpressionConstraintState() { alterExp.setConstraints(constraints); } + [ sk4=RelObjectName() { alterExp.addParameters("USING", sk4); }] + [ LOOKAHEAD(2) index = IndexWithComment(index) { alterExp.setIndex(index); } ] + ) + | + ( + tk= + columnNames=ColumnsNamesList() + { + index = new NamedConstraint() + .withName(sk3) + .withType(tk.image) + .withColumnsNames(columnNames); + alterExp.setIndex(index); + } + constraints=AlterExpressionConstraintState() { alterExp.setConstraints(constraints); } + ) + ) + ) + ) + ) + | + ( + { alterExp.setOperation(AlterOperation.CHANGE); } + [ { alterExp.hasColumn(true); alterExp.setOptionalSpecifier("COLUMN"); } ] + ( + (tk= | tk=) + alterExpressionColumnDataType = AlterExpressionColumnDataType() { alterExp.withColumnOldName(tk.image).addColDataType(alterExpressionColumnDataType); } + ) + ) + | + { alterExp.setOperation(AlterOperation.DROP); } + ( + ( + ( + // we use the PK Columns Field instead of the Column Field + // for holding multiple DROP Columns + columnNames=ColumnsNamesList() { alterExp.setPkColumns(columnNames); columnNames = null; } + + [ "INVALIDATE" { alterExp.addParameters("INVALIDATE"); } ] + + [ + "CASCADE" { alterExp.addParameters("CASCADE"); } + [ "CONSTRAINTS" { alterExp.addParameters("CONSTRAINTS"); } ] + ] + ) + | + ( + ( LOOKAHEAD(2) { alterExp.hasColumn(true); } )? + [ { alterExp.setUsingIfExists(true); } ] + (tk= | tk=) { alterExp.setColumnName(tk.image); } + + [ "INVALIDATE" { alterExp.addParameters("INVALIDATE"); } ] + + [ + "CASCADE" { alterExp.addParameters("CASCADE"); } + [ "CONSTRAINTS" { alterExp.addParameters("CONSTRAINTS"); } ] + ] + ) + ) + | + ( + ( tk= | tk= ) + ( tk2= | tk2= ) { + index = new Index().withType(tk.image).withName(tk2.image); + alterExp.setIndex(index); + } + ) + | + ( + tk= { alterExp.setOperation(AlterOperation.DROP_UNIQUE); } + columnNames=ColumnsNamesList() { alterExp.setPkColumns(columnNames); columnNames = null; } + [ ( tk= | tk= ) { alterExp.addParameters(tk.image); } ] + ) + | + ( + tk= tk2= { alterExp.setOperation(AlterOperation.DROP_PRIMARY_KEY); } + [ ( tk= | tk= ) { alterExp.addParameters(tk.image); } ] + ) + | + ( + tk= tk2= { alterExp.setOperation(AlterOperation.DROP_FOREIGN_KEY); } + columnNames=ColumnsNamesList() { alterExp.setPkColumns(columnNames); columnNames = null; } + [ ( tk= | tk= ) { alterExp.addParameters(tk.image); } ] + ) + | + ( + [ { alterExp.setUsingIfExists(true); } ] + ( tk= | tk=) { alterExp.setConstraintName(tk.image); } + [ ( tk= | tk= ) { alterExp.addParameters(tk.image); } ] + ) + ) + | + ( + { + alterExp.setOperation(AlterOperation.ALGORITHM); + } + ["=" { alterExp.setUseEqual(true);} ] + sk3 = RelObjectName() {alterExp.addParameters(sk3); } + ) + | + LOOKAHEAD(2) { alterExp.setOperation(AlterOperation.RENAME); } [ { alterExp.hasColumn(true);} ] + ( tk= | tk= ) { alterExp.setColOldName(tk.image); } + + (tk2= | tk2=) { alterExp.setColumnName(tk2.image); } + | + LOOKAHEAD(2)( + {alterExp.setOperation(AlterOperation.RENAME_TABLE);} + (tk2= | tk2=) { alterExp.setNewTableName(tk2.image);} + ) + | + ( {alterExp.setOperation(AlterOperation.COMMENT);} + ["=" {alterExp.setOperation(AlterOperation.COMMENT_WITH_EQUAL_SIGN);} ] + tk= { alterExp.setCommentText(tk.image); } + ) + | + LOOKAHEAD(2) + ( (( {alterExp.setOperation(AlterOperation.RENAME_INDEX);} + | {alterExp.setOperation(AlterOperation.RENAME_KEY);}) + | { alterExp.setOperation(AlterOperation.RENAME_CONSTRAINT); } + ) + (tk= | tk=){ + alterExp.setOldIndex(new Index().withName(tk.image)); + } + + (tk2= | tk2=){ + index = new Index().withName(tk2.image); + alterExp.setIndex(index); + } + ) + | + LOOKAHEAD(2) { alterExp.setOperation(AlterOperation.TRUNCATE_PARTITION); } truncatePartitionName = RelObjectName() { alterExp.setTruncatePartitionName(truncatePartitionName); } + | + tokens = captureRest() { + alterExp.setOperation(AlterOperation.UNSPECIFIC); + StringBuilder optionalSpecifier = new StringBuilder(); + int i=0; + + for (String s: tokens) + if (! (s.equals(";") || s.equals("\n\n\n")) ) { + if (i>0) + optionalSpecifier.append( " " ); + optionalSpecifier.append( s ); + i++; + } + + alterExp.setOptionalSpecifier( optionalSpecifier.toString() ); + } + ) + + { + return alterExp; + } +} + +Statement Alter(): +{ + Statement statement; + List captureRest; +} +{ + ( + ( + + ( + statement = AlterTable() + | + statement = AlterSession() + | + statement = AlterView(false) + | + statement = AlterSystemStatement() + | + statement = AlterSequence() + | + captureRest = captureRest() + { + statement = new UnsupportedStatement("ALTER", captureRest); + } + ) + ) + | + ( + + ( + statement = AlterView(true) + | + captureRest = captureRest() + { + statement = new UnsupportedStatement("REPLACE", captureRest); + } + ) + ) + ) + { + return statement; + } +} + +Alter AlterTable(): +{ + Alter alter = new Alter(); + Table table; + AlterExpression alterExp; + boolean usingIfExists = false; +} +{ + + [ { alter.setUseOnly(true); } ] + [ LOOKAHEAD(2) { alter.setUseTableIfExists(true); } ] + table=Table() { alter.setTable(table); } + + alterExp=AlterExpression() { alter.addAlterExpression(alterExp); } + + ("," alterExp=AlterExpression() { alter.addAlterExpression(alterExp); } )* + + { + return alter; + } +} + +AlterSession AlterSession(): +{ + AlterSessionOperation operation = null; + List parameters = new ArrayList(); + Token token; +} +{ + ( + ( + ( { operation = AlterSessionOperation.ADVISE_COMMIT; } + | { operation = AlterSessionOperation.ADVISE_ROLLBACK; } + | { operation = AlterSessionOperation.ADVISE_NOTHING; } + ) + ) + | + ( + { operation = AlterSessionOperation.CLOSE_DATABASE_LINK; } + ) + | + ( + ( { operation = AlterSessionOperation.ENABLE_COMMIT_IN_PROCEDURE; } + | { operation = AlterSessionOperation.ENABLE_GUARD; } + | ( { operation = AlterSessionOperation.ENABLE_PARALLEL_DML; } + | { operation = AlterSessionOperation.ENABLE_PARALLEL_DDL; } + | { operation = AlterSessionOperation.ENABLE_PARALLEL_QUERY; } + ) + | { operation = AlterSessionOperation.ENABLE_RESUMABLE; } + ) + ) + | + ( + ( { operation = AlterSessionOperation.DISABLE_COMMIT_IN_PROCEDURE; } + | { operation = AlterSessionOperation.DISABLE_GUARD; } + | ( { operation = AlterSessionOperation.DISABLE_PARALLEL_DML; } + | { operation = AlterSessionOperation.DISABLE_PARALLEL_DDL; } + | { operation = AlterSessionOperation.DISABLE_PARALLEL_QUERY; } + ) + | { operation = AlterSessionOperation.DISABLE_RESUMABLE; } + ) + ) + | + ( + ( { operation = AlterSessionOperation.FORCE_PARALLEL_DML; } + | { operation = AlterSessionOperation.FORCE_PARALLEL_DDL; } + | { operation = AlterSessionOperation.FORCE_PARALLEL_QUERY; } + ) + ) + | + ( + { operation = AlterSessionOperation.SET; } + ) + ) + + ( ( token = + | token = + | token = "=" + | token = + | token = + ) { parameters.add( token.image ); } + )* + + + { + return new AlterSession(operation, parameters); + } +} + +AlterSystemStatement AlterSystemStatement(): +{ + AlterSystemOperation operation = null; + List parameters = new LinkedList(); +} +{ + ( + ( + "ARCHIVE" "LOG" { operation = AlterSystemOperation.ARCHIVE_LOG; } + ) + | + ( + "CHECKPOINT" { operation = AlterSystemOperation.CHECKPOINT; } + ) + | + ( + "DUMP" "ACTIVE" "SESSION" "HISTORY" { operation = AlterSystemOperation.DUMP_ACTIVE_SESSION_HISTORY; } + ) + | + ( + ( + "DISTRIBUTED RECOVERY" { operation = AlterSystemOperation.ENABLE_DISTRIBUTED_RECOVERY; } + | "RESTRICTED SESSION" { operation = AlterSystemOperation.ENABLE_DISTRIBUTED_RECOVERY; } + ) + ) + | + ( + ( + "DISTRIBUTED RECOVERY" { operation = AlterSystemOperation.DISABLE_DISTRIBUTED_RECOVERY; } + | "RESTRICTED SESSION" { operation = AlterSystemOperation.DISABLE_RESTRICTED_SESSION; } + ) + ) + | + ( + "FLUSH" { operation = AlterSystemOperation.FLUSH; } + ) + | + ( + "DISCONNECT" "SESSION" { operation = AlterSystemOperation.DISCONNECT_SESSION; } + ) + | + ( + "KILL SESSION" { operation = AlterSystemOperation.KILL_SESSION; } + ) + | + ( + "SWITCH" { operation = AlterSystemOperation.SWITCH; } + ) + | + ( + "SUSPEND" { operation = AlterSystemOperation.SUSPEND; } + ) + | + ( + "RESUME" { operation = AlterSystemOperation.RESUME; } + ) + | + ( + "QUIESCE" "RESTRICTED" { operation = AlterSystemOperation.QUIESCE; } + ) + | + ( + { operation = AlterSystemOperation.UNQUIESCE; } + ) + | + ( + "SHUTDOWN" { operation = AlterSystemOperation.SHUTDOWN; } + ) + | + ( + "REGISTER" { operation = AlterSystemOperation.REGISTER; } + ) + | + ( + "SET" { operation = AlterSystemOperation.SET; } + ) + | + ( + "RESET" { operation = AlterSystemOperation.RESET; } + ) + ) + parameters = captureRest() + + { + return new AlterSystemStatement(operation, parameters); + } +} + +Wait Wait(): +{ + Wait wait = new Wait(); + Token token = null; +} +{ + // sqlserver-oracle-> WAIT (TIMEOUT) + // https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_10002.htm#i2126016 + token= + { + wait.setTimeout(Long.parseLong(token.image)); + return wait; + } +} + +SavepointStatement SavepointStatement(): +{ + SavepointStatement savepointStatement; +} +{ + token= + { + savepointStatement = new SavepointStatement(token.image); + return savepointStatement; + } +} + +RollbackStatement RollbackStatement(): +{ + RollbackStatement rollbackStatement; + boolean usingWorkKeyword=false; + boolean usingSavepointKeyword=false; + String savepointName=null; + String forceDistributedTransactionIdentifier=null; +} +{ + { rollbackStatement = new RollbackStatement(); } + [ { rollbackStatement.setUsingWorkKeyword(true); } ] + [ ( + [ { rollbackStatement.setUsingSavepointKeyword(true); }] + token= { rollbackStatement.setSavepointName(token.image); } + ) + | + ( + token= { rollbackStatement.setForceDistributedTransactionIdentifier(token.image); } + ) ] + + { + return rollbackStatement; + } +} + +Commit Commit(): +{ + Commit commit=new Commit(); +} +{ + + { + return commit; + } +} + +Comment Comment(): +{ + Comment result = new Comment(); + Table table; + Table view; + Column column; + Token comment; +} +{ + + ( + ( + table = Table() { result.setTable(table); } + ) + | + ( + column = Column() { result.setColumn(column); } + ) + | + ( + view = Table() { result.setView(view); } + ) + ) + comment= + { + result.setComment(new StringValue(comment.image)); + return result; + } +} + +Grant Grant(): +{ + Grant grant = new Grant(); + ArrayList privileges = new ArrayList(); + List users; + Token tk = null; + ObjectNames objName; +} +{ + + ( + ( + [readGrantTypes(privileges) ( readGrantTypes(privileges))*] + + ( + objName=RelObjectNames() { grant.setObjectName(objName.getNames()); } + ) + ) + | + ( + tk= { grant.setRole(tk.image); } + ) + ) + (users = UsersList() {grant.setUsers(users);}) + { + if(privileges.size() > 0) { + grant.setPrivileges(privileges); + } + return grant; + } +} + +List UsersList(): +{ + List users = new ArrayList(); + String user = null; +} +{ + user=RelObjectName() { users.add(user); } + ( "," user=ColumnsNamesListItem() { users.add(user); } )* + { + return users; + } +} + +void readGrantTypes(ArrayList privileges): +{ +} +{ + {privileges.add("SELECT");} | + {privileges.add("INSERT");} | + {privileges.add("UPDATE");} | + {privileges.add("DELETE");} | + {privileges.add("EXECUTE");} | + {privileges.add("ALTER");} | + {privileges.add("DROP");} +} + +Sequence Sequence() #Sequence : +{ + ObjectNames data = null; + String serverName = null, databaseName = null, schemaName = null, sequenceName = null; +} +{ + data = RelObjectNames() + { + Sequence sequence = new Sequence(data.getNames()); + linkAST(sequence,jjtThis); + return sequence; + } +} + +List SequenceParameters(): +{ + List sequenceParameters = new ArrayList(); + Sequence.Parameter parameter = null; + Token token = null; +} +{ +( + ( token= + { + parameter = new Sequence.Parameter(Sequence.ParameterType.INCREMENT_BY); + parameter.setValue(Long.parseLong(token.image)); + sequenceParameters.add(parameter); + } + ) + | + ( token= + { + parameter = new Sequence.Parameter(Sequence.ParameterType.START_WITH); + parameter.setValue(Long.parseLong(token.image)); + sequenceParameters.add(parameter); + } + ) + | + ( [ LOOKAHEAD(2) token=] + { + parameter = new Sequence.Parameter(Sequence.ParameterType.RESTART_WITH); + if(token != null){ + parameter.setValue(Long.parseLong(token.image)); + } + sequenceParameters.add(parameter); + } + ) + | + ( + { + parameter = new Sequence.Parameter(Sequence.ParameterType.NOMAXVALUE); + sequenceParameters.add(parameter); + } + | token= + { + parameter = new Sequence.Parameter(Sequence.ParameterType.MAXVALUE); + parameter.setValue(Long.parseLong(token.image)); + sequenceParameters.add(parameter); + } + ) + | + ( + { + parameter = new Sequence.Parameter(Sequence.ParameterType.NOMINVALUE); + sequenceParameters.add(parameter); + } + | token= + { + parameter = new Sequence.Parameter(Sequence.ParameterType.MINVALUE); + parameter.setValue(Long.parseLong(token.image)); + sequenceParameters.add(parameter); + } + ) + | + ( { sequenceParameters.add(new Sequence.Parameter(Sequence.ParameterType.NOCYCLE)); } + | { sequenceParameters.add(new Sequence.Parameter(Sequence.ParameterType.CYCLE)); } + ) + | + ( + { + parameter = new Sequence.Parameter(Sequence.ParameterType.NOCACHE); + sequenceParameters.add(parameter); + } + | token= + { + parameter = new Sequence.Parameter(Sequence.ParameterType.CACHE); + parameter.setValue(Long.parseLong(token.image)); + sequenceParameters.add(parameter); + } + ) + | + ( { sequenceParameters.add(new Sequence.Parameter(Sequence.ParameterType.ORDER)); } + | { sequenceParameters.add(new Sequence.Parameter(Sequence.ParameterType.NOORDER)); } + ) + | + ( { sequenceParameters.add(new Sequence.Parameter(Sequence.ParameterType.KEEP)); } + | { sequenceParameters.add(new Sequence.Parameter(Sequence.ParameterType.NOKEEP)); } + ) + | + ( { sequenceParameters.add(new Sequence.Parameter(Sequence.ParameterType.SESSION)); } + | { sequenceParameters.add(new Sequence.Parameter(Sequence.ParameterType.GLOBAL)); } + ) + )* //zero or many times those productions + { + return sequenceParameters; + } +} + +CreateSequence CreateSequence(): +{ + CreateSequence createSequence = new CreateSequence(); + Sequence sequence; + List sequenceParameters; +} +{ + sequence=Sequence() { createSequence.setSequence(sequence); } + sequenceParameters = SequenceParameters() + { + sequence.setParameters(sequenceParameters); + return createSequence; + } +} + +AlterSequence AlterSequence(): +{ + AlterSequence alterSequence = new AlterSequence(); + Sequence sequence; + List sequenceParameters; +} +{ + sequence=Sequence() { alterSequence.setSequence(sequence); } + sequenceParameters = SequenceParameters() + { + sequence.setParameters(sequenceParameters); + return alterSequence; + } +} + +Statement Create(): +{ + boolean isUsingOrReplace=false; + Token tk; + Statement statement; + List captureRest; +} +{ + [ { isUsingOrReplace = true; } ] + ( + statement = CreateFunctionStatement(isUsingOrReplace) + | + statement = CreateSchema() + | + statement = CreateSequence() + | + statement = CreateSynonym(isUsingOrReplace) + | + LOOKAHEAD(3) statement = CreateTable(isUsingOrReplace) + | + LOOKAHEAD(2) statement = CreateView(isUsingOrReplace) + | + // @fixme: must appear with TRIGGER before INDEX or it will collide with INDEX's CreateParameter() production + ( tk= | tk= ) captureRest = captureRest() + { + statement = new UnsupportedStatement("CREATE " + tk.image, captureRest); + } + | + /* @fixme + * Create Index uses CreateParameter() which allows all kind of tokens + * it can conflict with other statements and must be at the end right now + */ + statement = CreateIndex() + | + captureRest = captureRest() + { + statement = new UnsupportedStatement("CREATE", captureRest); + } + ) + { + return statement; + } +} + +CreateFunctionalStatement CreateFunctionStatement(boolean isUsingOrReplace): +{ + CreateFunctionalStatement type = null; + List tokens = new LinkedList(); + String statementType = null; +} +{ + ( + { statementType = "FUNCTION"; } + | + { statementType = "PROCEDURE"; } + ) + tokens=captureFunctionBody() + { + if(statementType.equals("FUNCTION")) { + type = new CreateFunction(isUsingOrReplace, tokens); + } + if(statementType.equals("PROCEDURE")) { + type = new CreateProcedure(isUsingOrReplace, tokens); + } + + return type; + } +} + +CreateSynonym CreateSynonym(boolean isUsingOrReplace): +{ + CreateSynonym createSynonym = new CreateSynonym(); + Synonym synonym; + boolean publicSynonym = false; + ObjectNames data = null; +} +{ + [ { publicSynonym = true; } ] + synonym=Synonym() { createSynonym.setSynonym(synonym); } + data = RelObjectNames() + { + createSynonym.setOrReplace(isUsingOrReplace); + createSynonym.setPublicSynonym(publicSynonym); + createSynonym.setForList(data.getNames()); + return createSynonym; + } +} + +Synonym Synonym() #Synonym : +{ + ObjectNames data = null; + String serverName = null, databaseName = null, schemaName = null, sequenceName = null; +} +{ + data = RelObjectNames() + { + Synonym synonym = new Synonym(data.getNames()); + linkAST(synonym,jjtThis); + return synonym; + } +} + +UnsupportedStatement UnsupportedStatement(): +{ + List tokens = new LinkedList(); +} +{ + tokens=captureUnsupportedStatementDeclaration() + { + return new UnsupportedStatement(tokens); + } +} + +JAVACODE +List captureRest() { + List tokens = new LinkedList(); + Token tok; + while(true) { + tok = getToken(1); + int l = tokens.size(); + if( tok.kind == EOF || tok.kind == ST_SEMICOLON ) { + break; + } else if ( l>0 && ( tok.image.equals(".") || tokens.get(l-1).endsWith(".")) ) { + tokens.set(l-1, tokens.get(l-1) + tok.image); + } else { + tokens.add(tok.image); + } + tok = getNextToken(); + } + return tokens; +} + +/** +* Reads the tokens of a function or procedure body. +* A function body can end in 2 ways: +* 1) BEGIN...END; +* 2) Postgres: $$...$$...; +*/ + +JAVACODE +List captureFunctionBody() { + List tokens = new LinkedList(); + Token tok; + boolean foundEnd = false; + while(true) { + tok = getToken(1); + int l = tokens.size(); + if( tok.kind == EOF || ( foundEnd && tok.kind == ST_SEMICOLON) ) { + if (tok.kind == ST_SEMICOLON) { + tokens.add(tok.image); + } + break; + } else if ( l>0 && ( tok.image.equals(".") || tokens.get(l-1).endsWith(".")) ) { + tokens.set(l-1, tokens.get(l-1) + tok.image); + } else { + tokens.add(tok.image); + } + foundEnd |= (tok.kind == K_END) + || ( tok.image.trim().startsWith("$$") && tok.image.trim().endsWith("$$")) ; + + tok = getNextToken(); + } + return tokens; +} + +JAVACODE +List captureUnsupportedStatementDeclaration() { + List tokens = new LinkedList(); + Token tok; + + while(true) { + tok = getToken(1); + if( tok.kind == EOF || tok.kind== ST_SEMICOLON || tok.kind== K_END ) { + break; + } + tokens.add(tok.image); + tok = getNextToken(); + } + return tokens; +} + +String IdentifierChain(): +{ + String identifierChain; + String part; +} +{ + identifierChain=RelObjectNameExt2() + ( "." part=RelObjectNameExt2() { identifierChain += "." + part; } )* + + { + return identifierChain; + } +} + +Expression CharacterPrimary(): +{ + Expression expression; +} +{ + ( + expression = TranscodingFunction() + | + expression = TrimFunction() + ) + // @todo + // @see https://manticore-projects.com/SQL2016Parser/syntax.html#character-value-function + // | character_substring_function + // | regular_expression_substring_function + // | regex_substring_function + // | fold + // | character_transliteration + // | regex_transliteration + // | character_overlay_function + // | normalize_function + // | specific_type_method + + { + return expression; + } +} + +TranscodingFunction TranscodingFunction() #TranscodingFunction : +{ + TranscodingFunction transcodingFunction; + ColDataType colDataType; + Expression expression; + String transcodingName=null; + Token style; +} +{ + "(" + ( + LOOKAHEAD(ColDataType() ",") ( + colDataType = ColDataType() + "," expression = Expression() + [ "," style = { transcodingName = style.image; } ] + + { + transcodingFunction = new TranscodingFunction(colDataType, expression, transcodingName); + } + ) + | + ( + expression = Expression() + transcodingName=IdentifierChain() + + { + transcodingFunction = new TranscodingFunction(expression, transcodingName); + } + ) + ) + ")" + { + return transcodingFunction; + } +} + +TrimFunction TrimFunction(): +{ + TrimFunction.TrimSpecification trimSpecification=null; + Expression expression = null; + boolean usesFrom = false; + Expression fromExpression = null; +} +{ + "(" + [ + LOOKAHEAD(2) ( + { trimSpecification = TrimFunction.TrimSpecification.LEADING; } + | + { trimSpecification = TrimFunction.TrimSpecification.TRAILING; } + | + { trimSpecification = TrimFunction.TrimSpecification.BOTH; } + ) + ] + + // This is not SQL:2016 compliant, but Postgres supports it + [ expression = Expression() ] + + [ + ( + "," + | + { usesFrom = true; } + ) + fromExpression = Expression() + ] + ")" + + { + return new TrimFunction(trimSpecification, expression, fromExpression, usesFrom); + } +} diff --git a/src/main/resources/rr/xhtml2rst.xsl b/src/main/resources/rr/xhtml2rst.xsl new file mode 100644 index 0000000..1f46875 --- /dev/null +++ b/src/main/resources/rr/xhtml2rst.xsl @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + .. raw:: html + + <div id="floating-toc"> + <div class="search-container"> + <input type="button" id="toc-hide-show-btn"></input> + <input type="text" id="toc-search" placeholder="Search" /> + </div> + <ul id="toc-list"></ul> + </div> + + + + + + + + + + + ********************************************************************* + SQL Syntax + + + + |JSQLPARSER_SNAPSHOT_VERSION| + + + |JSQLPARSER_VERSION| + + + + ********************************************************************* + + The EBNF and Railroad Diagrams for + + + + |JSQLPARSER_SNAPSHOT_VERSION| + + + |JSQLPARSER_VERSION| + + + . + + + + + + + + ====================================================================================================================== + + + + ====================================================================================================================== + + + + + .. raw:: html + + + + + + +
+ + + + + + +
+ + + +
+ + + + Referenced by: +
    + +
+
+ + Not referenced by any. + +
+
+
+ + + + + + + + +
  • + + + + + + + + + +
  • +
    + diff --git a/src/site/site.xml b/src/site/site.xml new file mode 100644 index 0000000..6d98119 --- /dev/null +++ b/src/site/site.xml @@ -0,0 +1,35 @@ + + + + + + + org.apache.maven.skins + maven-fluido-skin + 1.7 + + + + JSqlParser + + + + + + + + + + + + diff --git a/src/site/sphinx/_images/JavaAST.png b/src/site/sphinx/_images/JavaAST.png new file mode 100644 index 0000000..30428c5 Binary files /dev/null and b/src/site/sphinx/_images/JavaAST.png differ diff --git a/src/site/sphinx/_images/favicon.svg b/src/site/sphinx/_images/favicon.svg new file mode 100644 index 0000000..5a86c12 --- /dev/null +++ b/src/site/sphinx/_images/favicon.svg @@ -0,0 +1,171 @@ + + + + + + + + + + + + + diff --git a/src/site/sphinx/_images/logo-no-background.svg b/src/site/sphinx/_images/logo-no-background.svg new file mode 100644 index 0000000..3289bc1 --- /dev/null +++ b/src/site/sphinx/_images/logo-no-background.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/site/sphinx/_static/floating_toc.css b/src/site/sphinx/_static/floating_toc.css new file mode 100644 index 0000000..7080e42 --- /dev/null +++ b/src/site/sphinx/_static/floating_toc.css @@ -0,0 +1,83 @@ +/* Styling for the floating TOC */ +#floating-toc { + position: fixed; + top: 50%; + right: 20px; + transform: translateY(-50%); + background-color: rgba(255, 255, 255, 0.72); + border: 1px solid rgba(64, 64, 64, 0.2); /* Set the border style */ + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + z-index: 9999; + height: 66%; + overflow-y: hide; + overflow-x: auto; + width: 240px; +} + +/* Styling for TOC list items */ +#floating-toc ul { + list-style-type: none; + padding-left: 26px; + margin-top: 36px; + line-height: 2px; +} + +/* Styling for heading levels in TOC */ +#floating-toc ul li { + /* no line breaks please */ + width: 250%; +} + +#floating-toc ul li a { + font-size: 14px; + font-weight: normal; +} + +#floating-toc ul li h1 a { + font-weight: bold; + font-size: 16px; +} + +#floating-toc ul li h2 a { + font-weight: bold; +} + +#floating-toc ul li h3 a { + font-style: italic; +} + + +/* Styling for search input */ +#floating-toc .search-container { + position: sticky; + top: 6px; + padding: 0px; +} + +#floating-toc input[type="text"] { + width: 186px; + height: 24px; + box-sizing: border-box; + background-color: rgba(255, 255, 255, 1.00); + color: rgba(128, 128, 128, 1.0); /* Set the text color */ + border: 1px solid rgba(0, 0, 0, 0.2); /* Set the border style */ + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); +} + + +#floating-toc input[type="button"] { + position: float; + width: 24px; + height: 24px; + padding: 0; + margin: 0; + box-sizing: border-box; + background-color: rgba(255, 255, 255, 0.72); + color: rgba(128, 128, 128, 1.0); /* Set the text color */ + border: 0px solid rgba(0, 0, 0, 0.2); /* Set the border style */ +} + +/* Highlighting current caption in TOC */ +#floating-toc ul li a.active { + font-weight: bold; +} diff --git a/src/site/sphinx/_static/floating_toc.js b/src/site/sphinx/_static/floating_toc.js new file mode 100644 index 0000000..eb2aeab --- /dev/null +++ b/src/site/sphinx/_static/floating_toc.js @@ -0,0 +1,98 @@ +// JavaScript code for creating the floating TOC +window.addEventListener('DOMContentLoaded', function() { + var tocContainer = document.getElementById('floating-toc'); + var showBtn = document.getElementById('toc-hide-show-btn'); + var tocList = document.getElementById('toc-list'); + var headings = document.querySelectorAll('h1, h2, h3'); + var tocLevels = [0, 0, 0]; + + // Calculate the initial position of the TOC + const tocContainerRect = tocContainer.getBoundingClientRect(); + const tocContainerRight = tocContainer.style.right; + const buttonText = document.getElementById('buttonText'); + + headings.forEach(function(heading) { + var level = parseInt(heading.tagName.substr(1), 10) - 1; + + tocLevels[level]++; + for (var i = level + 1; i < 3; i++) { + tocLevels[i] = 0; + } + + var listItem = document.createElement('li'); + var link = document.createElement('a'); + + var number = tocLevels.slice(0, level + 1).join('.') + ' '; + link.textContent = number + heading.textContent.trim().replace(/#$/, '').replace(/¶$/, ''); + + var headingId = 'heading-' + Math.random().toString(36).substr(2, 9); + heading.setAttribute('id', headingId); + link.href = '#' + headingId; + + var styledHeading = document.createElement('h' + (level + 1)); + styledHeading.appendChild(link); + listItem.appendChild(styledHeading); + + tocList.appendChild(listItem); + }); + + // Toggle TOC visibility + showBtn.addEventListener('click', function() { + if (tocContainer.style.right != tocContainerRight) { + tocContainer.style.right = tocContainerRight; + // buttonText.innerText="H"; + } else { + tocContainer.style.right = `-${tocContainerRect.width-26}px`; + // buttonText.innerText="S"; + }; + }); + + // JavaScript code for searching the TOC + var searchInput = document.getElementById('toc-search'); + var tocItems = Array.from(tocList.getElementsByTagName('li')); + + searchInput.addEventListener('input', function() { + var searchValue = this.value.toLowerCase(); + + tocItems.forEach(function(item) { + var link = item.querySelector('a'); + var linkText = link.textContent.toLowerCase(); + + if (linkText.includes(searchValue)) { + item.style.display = 'block'; + } else { + item.style.display = 'none'; + } + }); + }); + + // JavaScript code for updating the floating TOC on scroll + window.addEventListener('scroll', function() { + var scrollPosition = window.pageYOffset || document.documentElement.scrollTop; + + var visibleHeading = null; + headings.forEach(function(heading) { + var rect = heading.getBoundingClientRect(); + if (rect.top > 0 && rect.top < window.innerHeight) { + visibleHeading = heading; + return; + } + }); + + if (visibleHeading) { + var activeLink = tocList.querySelector('a[href="#' + visibleHeading.id + '"]'); + if (activeLink) { + activeLink.classList.add('active'); + tocContainer.scrollTop = activeLink.offsetTop - tocContainer.offsetTop; + } + + // Remove 'active' class from other links + var allLinks = tocList.querySelectorAll('a'); + allLinks.forEach(function(link) { + if (link !== activeLink) { + link.classList.remove('active'); + } + }); + } + }); +}); diff --git a/src/site/sphinx/_static/pygments.css b/src/site/sphinx/_static/pygments.css new file mode 100644 index 0000000..8a76dc5 --- /dev/null +++ b/src/site/sphinx/_static/pygments.css @@ -0,0 +1,74 @@ +pre { line-height: 125%; } +td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight .hll { background-color: #ffffcc } +.highlight { background: #fff; } +.highlight .c { color: #408090; font-style: italic } /* Comment */ +.highlight .err { border: 1px solid #FF0000 } /* Error */ +.highlight .k { color: #007020; font-weight: bold } /* Keyword */ +.highlight .o { color: #666666 } /* Operator */ +.highlight .ch { color: #408090; font-style: italic } /* Comment.Hashbang */ +.highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #007020 } /* Comment.Preproc */ +.highlight .cpf { color: #408090; font-style: italic } /* Comment.PreprocFile */ +.highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ +.highlight .gd { color: #A00000 } /* Generic.Deleted */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .gr { color: #FF0000 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #00A000 } /* Generic.Inserted */ +.highlight .go { color: #333333 } /* Generic.Output */ +.highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #0044DD } /* Generic.Traceback */ +.highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #007020 } /* Keyword.Pseudo */ +.highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #902000 } /* Keyword.Type */ +.highlight .m { color: #208050 } /* Literal.Number */ +.highlight .s { color: #4070a0 } /* Literal.String */ +.highlight .na { color: #4070a0 } /* Name.Attribute */ +.highlight .nb { color: #007020 } /* Name.Builtin */ +.highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ +.highlight .no { color: #60add5 } /* Name.Constant */ +.highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ +.highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ +.highlight .ne { color: #007020 } /* Name.Exception */ +.highlight .nf { color: #06287e } /* Name.Function */ +.highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ +.highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ +.highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #bb60d5 } /* Name.Variable */ +.highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mb { color: #208050 } /* Literal.Number.Bin */ +.highlight .mf { color: #208050 } /* Literal.Number.Float */ +.highlight .mh { color: #208050 } /* Literal.Number.Hex */ +.highlight .mi { color: #208050 } /* Literal.Number.Integer */ +.highlight .mo { color: #208050 } /* Literal.Number.Oct */ +.highlight .sa { color: #4070a0 } /* Literal.String.Affix */ +.highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ +.highlight .sc { color: #4070a0 } /* Literal.String.Char */ +.highlight .dl { color: #4070a0 } /* Literal.String.Delimiter */ +.highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #4070a0 } /* Literal.String.Double */ +.highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ +.highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ +.highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ +.highlight .sx { color: #c65d09 } /* Literal.String.Other */ +.highlight .sr { color: #235388 } /* Literal.String.Regex */ +.highlight .s1 { color: #4070a0 } /* Literal.String.Single */ +.highlight .ss { color: #517918 } /* Literal.String.Symbol */ +.highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ +.highlight .fm { color: #06287e } /* Name.Function.Magic */ +.highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ +.highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ +.highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ +.highlight .vm { color: #bb60d5 } /* Name.Variable.Magic */ +.highlight .il { color: #208050 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/src/site/sphinx/_static/svg.css b/src/site/sphinx/_static/svg.css new file mode 100644 index 0000000..bd6b551 --- /dev/null +++ b/src/site/sphinx/_static/svg.css @@ -0,0 +1,34 @@ +div .ebnf +{ + display: block; + padding: 2pt; + margin-bottom: 22pt; + font:10px 'Roboto-Mono',monospace; +} + +@namespace "http://www.w3.org/2000/svg"; + .line {fill: none; stroke: #0063db; stroke-width: 1;} + .bold-line {stroke: #000714; shape-rendering: crispEdges; stroke-width: 2;} + .thin-line {stroke: #000A1F; shape-rendering: crispEdges;} + .filled {fill: #0063db; stroke: none;} + text.terminal {font-family: Roboto, Sans-serif; + font-size: 10px; + fill: #000714; + font-weight: bold; + } + text.nonterminal {font-family: Roboto, Sans-serif; + font-size: 10px; + fill: #00091A; + font-weight: normal; + } + text.regexp {font-family: Roboto, Sans-serif; + font-size: 10px; + fill: #000A1F; + font-weight: normal; + } + rect, circle, polygon {fill: #0063db; stroke: #0063db;} + rect.terminal {fill: #4D88FF; stroke: #0063db; stroke-width: 1;} + rect.nonterminal {fill: #9EBFFF; stroke: #0063db; stroke-width: 1;} + rect.text {fill: none; stroke: none;} + polygon.regexp {fill: #C7DAFF; stroke: #0063db; stroke-width: 1;} + diff --git a/src/site/sphinx/conf.py b/src/site/sphinx/conf.py new file mode 100644 index 0000000..29ab663 --- /dev/null +++ b/src/site/sphinx/conf.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- + +# General options +needs_sphinx = '1.0' +add_function_parentheses = True + +extensions = ['myst_parser', 'sphinx.ext.autodoc', 'sphinx.ext.autosectionlabel', 'sphinx.ext.extlinks', 'sphinx-prompt', 'sphinx_substitution_extensions', 'sphinx_inline_tabs', 'pygments.sphinxext', ] + +issues_github_path = "JSQLParser/JSqlParser" + +source_encoding = 'utf-8-sig' +#pygments_style = 'friendly' +show_sphinx = False +master_doc = 'index' +exclude_patterns = ['_themes', '_static/css'] +logo_only = True + +# HTML options +html_theme = "furo" +html_theme_path = ["_themes"] +html_short_title = "JSQLParser" +htmlhelp_basename = "JSQLParser" + '-doc' +html_use_index = True +html_show_sourcelink = False +html_static_path = ['_static'] +html_logo = '_images/logo-no-background.svg' +html_favicon = '_images/favicon.svg' +html_css_files = ['svg.css', 'floating_toc.css'] +html_js_files = ['floating_toc.js',] + +html_theme_options = { + 'path_to_docs': 'site/sphinx', + 'repository_url': 'https://github.com/JSQLParser/JSqlParser', + 'repository_branch': 'master', + 'use_issues_button': True, + 'use_download_button': True, + 'use_fullscreen_button': True, + 'use_repository_button': True, +} + + diff --git a/src/site/sphinx/contribution.rst b/src/site/sphinx/contribution.rst new file mode 100644 index 0000000..c001f8e --- /dev/null +++ b/src/site/sphinx/contribution.rst @@ -0,0 +1,168 @@ +****************************** +How to contribute +****************************** + +Error Reports +============================== + +Please report any issue to the `GitHub Issue Tracker `_: + + 1) Provide the **Sample SQL** (shortened and simplified, properly formatted) + 2) State the exact **Version of JSQLParser** + 3) State the **RDBMS** in use and point on the applicable Grammar specification + 4) Please write in **English** and post **Plain Text only** (avoiding screen shots or bitmap pictures). + +Before reporting any issues, please verify your statement using `JSQLFormatter Online `_. + +Feature Requests +============================== + +JSQLParser is a demand-driven software library, where many contributors have shared solutions for their own needs. Requests for new features have a good chance to get implemented when + + 1) the request is about a commonly used feature for one of the major RDBMS + 2) or the request is backed by a sponsor or a bounty, which may attract developers to spend their time on it. + +Implementing new Features +============================== + +The team around JSQLParser warmly welcomes Code Contributions and Pull Requests. Please follow the guidance below and do not hesitate to ask us for assistance. + +Create a new Git Branch +------------------------------ + +When starting afresh, clone `JSQLParser` from the `GitHub` repository: + +.. code-block:: Bash + + git clone https://github.com/JSQLParser/JSqlParser.git + cd JSqlParser + git branch + +When having a local repository already, then pull/merge from the `GitHub` repository: + +.. code-block:: Bash + + cd JSqlParser + git pull origin master + git branch + +Amend the Code +------------------------------ + +The JSQLParser is generated by ``JavaCC`` based on the provided Grammar. The Grammar defines how a SQL Text is read and translated into Java Objects. Thus any contribution will depend on the following steps: + + 1) Edit the ``JavaCC`` Grammar at ``src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt`` + 2) Add or edit the Java Classes at ``src/main/java/net/sf/jsqlparser`` to facilitate this Grammar. + 3) When have added new Java Classes, amend the Visitors, the Visitor Adaptors, the De-Parsers and the Validators. + 4) Provide Java Unit Tests at ``src/test/java/net/sf/jsqlparser`` to test the new feature + + * The test should call at least one time the method ``assertSqlCanBeParsedAndDeparsed()`` + * The test should ensure complete coverage of the new Java Classes. + * The complete test suite must succeed. + + 5) Add the description of the new feature to the ``README.md`` file, section `Extensions`. + 6) Build the package with ``Maven`` and ensure, all checks do pass (PMD and CheckStyle and Code Formatting). + +Manage Reserved Keywords +------------------------------ + +Since JSQLParser is built by JavaCC from a Token based Grammar, ``Reserved Keywords`` need a special treatment. All Tokens of the Grammar would become ``Reserved Keywords`` -- unless explicitly allowed and white-listened. + +.. code-block:: sql + :caption: White-list Keyword example + + -- is a Token, recently defined in the Grammar + -- Although it is not restricted by the SQL Standard and could be used for Column, Table and Alias names + -- Explicitly white-listing OVERLAPS by adding it to the RelObjectNameWithoutValue() Production will allow for parsing the following statement + + SELECT Overlaps( overlaps ) AS overlaps + FROM overlaps.overlaps overlaps + WHERE overlaps = 'overlaps' + AND (CURRENT_TIME, INTERVAL '1' HOUR) OVERLAPS (CURRENT_TIME, INTERVAL -'1' HOUR) + ; + +So we will need to define and white-list any Keywords which may be allowed for Object Names (such as `Schema`, `Table`, `Column`, `Function`, `Alias`). This White-List must be updated whenever the Tokens of the Grammar change (e. |_| g. when adding a new Token or Production). + +There is a task ``updateKeywords`` for Gradle and Maven, which will: + + 1) Parse the Grammar in order to find all Token definitions + 2) Read the list of explicitly ``Reserved Keywords`` from ``net/sf/jsqlparser/parser/ParserKeywordsUtils.java`` + 3) Derive the list of ``White-Listed Keywords`` as difference between ``All Tokens`` and ``Reserved Keywords`` + 4) Modifies the Grammar Productions ``RelObjectNameWithoutValue...`` adding all Tokens according to ``White-Listed Keywords`` + 5) Run two special Unit Tests to verify parsing of all ``White-Listed Keywords`` (as `Schema`, `Table`, `Column`, `Function` or `Alias`) + 6) Update the web page about the Reserved Keywords + + +.. tab:: Gradle + + .. code-block:: shell + :caption: Gradle `updateKeywords` Task + + gradle updateKeywords + +.. tab:: Maven + + .. code-block:: shell + :caption: Maven `updateKeywords` Task + + mvn exec:java + + +Without this Gradle Task, any new Token or Production will become a ``Reserved Keyword`` automatically and can't be used for Object Names without quoting. + + +Commit a Pull Request +--------------------------------- + +.. code-block:: Bash + + cd JSqlParser + git add -A + git commit -m -m <description> + git push –set-upstream origin <new-branch> + +Follow the advice on `Meaningful Commit Messages <https://www.freecodecamp.org/news/how-to-write-better-git-commit-messages/>`_ and consider using `Commitizen <https://commitizen-tools.github.io/commitizen/>`_ when describing your commits. + +Please consider using `Conventional Commits` and structure your commit message as follows: + +.. code-block:: text + :caption: Conventional Commit Message Structure + + <type>[optional scope]: <description> + + [optional body] + + [BREAKING CHANGE: <change_description>] + + [optional footer(s)] + +.. list-table:: Commit Message Types + :widths: 15 85 + :header-rows: 1 + + * - Type + - Description + * - **feat** + - introduce a new feature + * - **fix** + - patches a bug in your codebase (bugfix or hotfix) + * - **build** + - changes that affect the build system or external dependencies + * - **chore** + - updates dependencies and does not relate to fix or feat and does not modify src or test files. + * - **ci** + - changes that affect the continuous integration process + * - **docs** + - updates the documentation or introduce documentation + * - **style** + - updates the formatting of code; remove white spaces, add missing spaces, remove unnecessary newlines + * - **refactor** + - reactors code segments to optimize readability without changing behavior + * - **perf** + - improve performance + * - **test** + - add/remove/update tests + * - **revert** + - reverts one or many previous commits + +Please visit `Better Programming <https://betterprogramming.pub/write-better-git-commit-messages-to-increase-your-productivity-89fa773e8375>`_ for more information and guidance. diff --git a/src/site/sphinx/index.rst b/src/site/sphinx/index.rst new file mode 100644 index 0000000..e28a571 --- /dev/null +++ b/src/site/sphinx/index.rst @@ -0,0 +1,115 @@ +.. meta:: + :description: Java Software Library for parsing SQL Statements into Abstract Syntax Trees (AST) and manipulation of SQL Statements + :keywords: java sql statement parser abstract syntax tree + +########################### +Java SQL Parser Library +########################### + +.. toctree:: + :maxdepth: 2 + :hidden: + + usage + contribution + migration47 + migration50 + SQL Grammar Stable <syntax_stable> + SQL Grammar Snapshot <syntax_snapshot> + Unsupported Grammar <unsupported> + Java API Stable <javadoc_stable> + Java API Snapshot <javadoc_snapshot> + keywords + changelog + +.. image:: https://img.shields.io/github/release/JSQLParser/JSqlParser?include_prereleases=&sort=semver&color=blue + :alt: GitGub Release Badge + :target: https://github.com/JSQLParser/JSqlParser/releases + +.. image:: https://img.shields.io/github/issues/JSQLParser/JSqlParser + :alt: GitGub Issues Badge + :target: https://github.com/JSQLParser/JSqlParser/issues + +.. image:: https://badgen.net/maven/v/maven-central/com.github.jsqlparser/jsqlparser + :alt: Maven Badge + :target: https://mvnrepository.com/artifact/com.github.jsqlparser/jsqlparser + +.. image:: https://github.com/JSQLParser/JSqlParser/actions/workflows/maven.yml/badge.svg + :alt: Maven Build Status + :target: https://github.com/JSQLParser/JSqlParser/actions/workflows/maven.yml + +.. image:: https://coveralls.io/repos/JSQLParser/JSqlParser/badge.svg?branch=master + :alt: Coverage Status + :target: https://coveralls.io/github/com.github.jsqlparser/jsqlparser?branch=master + +.. image:: https://app.codacy.com/project/badge/Grade/6f9a2d7eb98f45969749e101322634a1 + :alt: Codacy Status + :target: https://app.codacy.com/gh/com.github.jsqlparser/jsqlparser/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade + +.. image:: https://www.javadoc.io/badge/com.github.jsqlparser/jsqlparser.svg + :alt: Java Docs + :target: https://javadoc.io/doc/com.github.jsqlparser/jsqlparser/latest/index.html + + +**JSQLParser** is a SQL statement parser built from JavaCC. It translates SQLs in a traversable hierarchy of Java classes. +The upcoming 5.0 release will depend on Java 11 and introduces new Visitors. Please see the :ref:`Migration to 5.0` guide. + +Latest stable release: |JSQLPARSER_STABLE_VERSION_LINK| + +Development version: |JSQLPARSER_SNAPSHOT_VERSION_LINK| + +.. sidebar:: Java API Website + + .. image:: _images/JavaAST.png + + +.. code-block:: SQL + :caption: Sample SQL Statement + + SELECT /*+ PARALLEL */ + cfe.id_collateral_ref.nextval + , id_collateral + FROM ( SELECT DISTINCT + a.id_collateral + FROM cfe.collateral a + LEFT JOIN cfe.collateral_ref b + ON a.id_collateral = b.id_collateral + WHERE b.id_collateral_ref IS NULL ) + ; + + +****************************** +SQL Dialects +****************************** + +**JSqlParser** is RDBMS agnostic and provides support for many dialects such as: + + * Oracle Database + * MS SqlServer + * MySQL and MariaDB + * PostgreSQL + * H2 + +******************************* +Features +******************************* + + * Comprehensive support for statements: + - QUERY: ``SELECT ...`` + - DML: ``INSERT ... INTO ...`` ``UPDATE ...`` ``MERGE ... INTO ...`` ``DELETE ... FROM ...`` + - DDL: ``CREATE ...`` ``ALTER ...`` ``DROP ...`` + + * Nested Expressions (e.g. Sub-Selects) + * ``WITH`` clauses + * Old Oracle ``JOIN (+)`` + * PostgreSQL implicit ``CAST ::`` + * SQL Parameters (e.g. ``?`` or ``:parameter``) + * Arrays vs. T-SQL Squared Bracket Quotes + * Fluent API to create SQL Statements from java Code + * Statement De-Parser to write SQL from Java Objects + + + + + + diff --git a/src/site/sphinx/keywords.rst b/src/site/sphinx/keywords.rst new file mode 100644 index 0000000..91437ec --- /dev/null +++ b/src/site/sphinx/keywords.rst @@ -0,0 +1,241 @@ +*********************** +Restricted Keywords +*********************** + +The following Keywords are **restricted** in JSQLParser-|JSQLPARSER_VERSION| and must not be used for **Naming Objects**: + ++----------------------+-------------+-----------+ +| **Keyword** | JSQL Parser | SQL:2016 | ++----------------------+-------------+-----------+ +| ABSENT | Yes | Yes | ++----------------------+-------------+-----------+ +| ALL | Yes | Yes | ++----------------------+-------------+-----------+ +| AND | Yes | Yes | ++----------------------+-------------+-----------+ +| ANY | Yes | Yes | ++----------------------+-------------+-----------+ +| AS | Yes | Yes | ++----------------------+-------------+-----------+ +| BETWEEN | Yes | Yes | ++----------------------+-------------+-----------+ +| BOTH | Yes | Yes | ++----------------------+-------------+-----------+ +| CASEWHEN | Yes | | ++----------------------+-------------+-----------+ +| CHECK | Yes | Yes | ++----------------------+-------------+-----------+ +| CONNECT | Yes | | ++----------------------+-------------+-----------+ +| CONNECT_BY_ROOT | Yes | Yes | ++----------------------+-------------+-----------+ +| CONSTRAINT | Yes | Yes | ++----------------------+-------------+-----------+ +| CREATE | Yes | | ++----------------------+-------------+-----------+ +| CROSS | Yes | Yes | ++----------------------+-------------+-----------+ +| CURRENT | Yes | Yes | ++----------------------+-------------+-----------+ +| DISTINCT | Yes | Yes | ++----------------------+-------------+-----------+ +| DOUBLE | Yes | | ++----------------------+-------------+-----------+ +| ELSE | Yes | Yes | ++----------------------+-------------+-----------+ +| EXCEPT | Yes | Yes | ++----------------------+-------------+-----------+ +| EXCLUDES | Yes | Yes | ++----------------------+-------------+-----------+ +| EXISTS | Yes | Yes | ++----------------------+-------------+-----------+ +| FETCH | Yes | Yes | ++----------------------+-------------+-----------+ +| FINAL | Yes | Yes | ++----------------------+-------------+-----------+ +| FOR | Yes | Yes | ++----------------------+-------------+-----------+ +| FORCE | Yes | Yes | ++----------------------+-------------+-----------+ +| FOREIGN | Yes | Yes | ++----------------------+-------------+-----------+ +| FROM | Yes | Yes | ++----------------------+-------------+-----------+ +| FULL | Yes | Yes | ++----------------------+-------------+-----------+ +| GLOBAL | Yes | | ++----------------------+-------------+-----------+ +| GROUP | Yes | Yes | ++----------------------+-------------+-----------+ +| GROUPING | Yes | | ++----------------------+-------------+-----------+ +| QUALIFY | Yes | | ++----------------------+-------------+-----------+ +| HAVING | Yes | Yes | ++----------------------+-------------+-----------+ +| IF | Yes | Yes | ++----------------------+-------------+-----------+ +| IIF | Yes | | ++----------------------+-------------+-----------+ +| IGNORE | Yes | | ++----------------------+-------------+-----------+ +| ILIKE | Yes | Yes | ++----------------------+-------------+-----------+ +| IN | Yes | Yes | ++----------------------+-------------+-----------+ +| INCLUDES | Yes | Yes | ++----------------------+-------------+-----------+ +| INNER | Yes | Yes | ++----------------------+-------------+-----------+ +| INTERSECT | Yes | Yes | ++----------------------+-------------+-----------+ +| INTERVAL | Yes | Yes | ++----------------------+-------------+-----------+ +| INTO | Yes | Yes | ++----------------------+-------------+-----------+ +| IS | Yes | Yes | ++----------------------+-------------+-----------+ +| JOIN | Yes | Yes | ++----------------------+-------------+-----------+ +| LATERAL | Yes | Yes | ++----------------------+-------------+-----------+ +| LEFT | Yes | Yes | ++----------------------+-------------+-----------+ +| LIKE | Yes | Yes | ++----------------------+-------------+-----------+ +| LIMIT | Yes | Yes | ++----------------------+-------------+-----------+ +| MINUS | Yes | Yes | ++----------------------+-------------+-----------+ +| NATURAL | Yes | Yes | ++----------------------+-------------+-----------+ +| NOCYCLE | Yes | Yes | ++----------------------+-------------+-----------+ +| NOT | Yes | Yes | ++----------------------+-------------+-----------+ +| NULL | Yes | Yes | ++----------------------+-------------+-----------+ +| OFFSET | Yes | Yes | ++----------------------+-------------+-----------+ +| ON | Yes | Yes | ++----------------------+-------------+-----------+ +| ONLY | Yes | Yes | ++----------------------+-------------+-----------+ +| OPTIMIZE | Yes | | ++----------------------+-------------+-----------+ +| OR | Yes | Yes | ++----------------------+-------------+-----------+ +| ORDER | Yes | Yes | ++----------------------+-------------+-----------+ +| OUTER | Yes | Yes | ++----------------------+-------------+-----------+ +| OUTPUT | Yes | Yes | ++----------------------+-------------+-----------+ +| OPTIMIZE | Yes | Yes | ++----------------------+-------------+-----------+ +| PIVOT | Yes | Yes | ++----------------------+-------------+-----------+ +| PROCEDURE | Yes | | ++----------------------+-------------+-----------+ +| PUBLIC | Yes | | ++----------------------+-------------+-----------+ +| RETURNING | Yes | Yes | ++----------------------+-------------+-----------+ +| RIGHT | Yes | Yes | ++----------------------+-------------+-----------+ +| SAMPLE | Yes | | ++----------------------+-------------+-----------+ +| SEL | Yes | | ++----------------------+-------------+-----------+ +| SELECT | Yes | | ++----------------------+-------------+-----------+ +| SEMI | Yes | Yes | ++----------------------+-------------+-----------+ +| SET | Yes | Yes | ++----------------------+-------------+-----------+ +| SOME | Yes | Yes | ++----------------------+-------------+-----------+ +| START | Yes | Yes | ++----------------------+-------------+-----------+ +| TABLES | Yes | | ++----------------------+-------------+-----------+ +| TOP | Yes | Yes | ++----------------------+-------------+-----------+ +| TRAILING | Yes | Yes | ++----------------------+-------------+-----------+ +| UNBOUNDED | Yes | Yes | ++----------------------+-------------+-----------+ +| UNION | Yes | Yes | ++----------------------+-------------+-----------+ +| UNIQUE | Yes | Yes | ++----------------------+-------------+-----------+ +| UNPIVOT | Yes | Yes | ++----------------------+-------------+-----------+ +| USE | Yes | Yes | ++----------------------+-------------+-----------+ +| USING | Yes | Yes | ++----------------------+-------------+-----------+ +| SQL_CACHE | Yes | Yes | ++----------------------+-------------+-----------+ +| SQL_CALC_FOUND_ROWS | Yes | Yes | ++----------------------+-------------+-----------+ +| SQL_NO_CACHE | Yes | Yes | ++----------------------+-------------+-----------+ +| STRAIGHT_JOIN | Yes | Yes | ++----------------------+-------------+-----------+ +| TABLESAMPLE | Yes | | ++----------------------+-------------+-----------+ +| VALUE | Yes | Yes | ++----------------------+-------------+-----------+ +| VALUES | Yes | Yes | ++----------------------+-------------+-----------+ +| VARYING | Yes | Yes | ++----------------------+-------------+-----------+ +| WHEN | Yes | Yes | ++----------------------+-------------+-----------+ +| WHERE | Yes | Yes | ++----------------------+-------------+-----------+ +| WINDOW | Yes | Yes | ++----------------------+-------------+-----------+ +| WITH | Yes | Yes | ++----------------------+-------------+-----------+ +| XOR | Yes | Yes | ++----------------------+-------------+-----------+ +| XMLSERIALIZE | Yes | Yes | ++----------------------+-------------+-----------+ +| SEL | Yes | Yes | ++----------------------+-------------+-----------+ +| SELECT | Yes | Yes | ++----------------------+-------------+-----------+ +| DATE | Yes | Yes | ++----------------------+-------------+-----------+ +| TIME | Yes | Yes | ++----------------------+-------------+-----------+ +| TIMESTAMP | Yes | Yes | ++----------------------+-------------+-----------+ +| YEAR | Yes | Yes | ++----------------------+-------------+-----------+ +| MONTH | Yes | Yes | ++----------------------+-------------+-----------+ +| DAY | Yes | Yes | ++----------------------+-------------+-----------+ +| HOUR | Yes | Yes | ++----------------------+-------------+-----------+ +| MINUTE | Yes | Yes | ++----------------------+-------------+-----------+ +| SECOND | Yes | Yes | ++----------------------+-------------+-----------+ +| SUBSTR | Yes | Yes | ++----------------------+-------------+-----------+ +| SUBSTRING | Yes | Yes | ++----------------------+-------------+-----------+ +| TRIM | Yes | Yes | ++----------------------+-------------+-----------+ +| POSITION | Yes | Yes | ++----------------------+-------------+-----------+ +| OVERLAY | Yes | Yes | ++----------------------+-------------+-----------+ +| NEXTVAL | Yes | | ++----------------------+-------------+-----------+ +| 0x | Yes | Yes | ++----------------------+-------------+-----------+ diff --git a/src/site/sphinx/migration47.rst b/src/site/sphinx/migration47.rst new file mode 100644 index 0000000..7fc5036 --- /dev/null +++ b/src/site/sphinx/migration47.rst @@ -0,0 +1,387 @@ +********************************* +Migration to 4.7 +********************************* + +The new version of JSQLParser 4.7 is a rewrite in order to simplify accessing the SQL's Abstract Syntax Tree (AST). Quite a few redundant classes have been removed or merged. + +As always, such a major improvement comes at a certain cost, which is breaking the previous API. Following the guidance below, the new API can be adopted easily although you are welcome to lodge a support request when any questions or concerns arise. + +`Values` Clause +--------------------------------- +The ``ValueListExpression`` has been replaced by ``Values``, which implements ``Select`` `Statement` and `Expression`. + +The ``ValuesStatement`` has been replaced by ``Values``, which implements ``Select`` `Statement` and `Expression`. + +.. tab:: Statement + + .. code-block:: SQL + + VALUES ( 1, 2, 3 ) + ; + + + .. code-block:: TEXT + + SQL Text + └─Statements: statement.select.Values + └─ParenthesedExpressionList: (1, 2, 3) + + + .. code-block:: JAVA + + Values values = (Values) CCJSqlParserUtil.parse(sqlStr); + assertEquals( 3, values.getExpressions().size() ); + + +.. tab:: Sub-query + + .. code-block:: SQL + + SELECT * + FROM ( VALUES 1, 2, 3 ) + ; + + + .. code-block:: TEXT + + SQL Text + └─Statements: statement.select.PlainSelect + ├─selectItems: statement.select.SelectItem + │ └─AllColumns: * + └─fromItem: statement.select.ParenthesedSelect + └─select: statement.select.Values + └─ExpressionList: 1, 2, 3 + + + .. code-block:: JAVA + + PlainSelect select = (PlainSelect) CCJSqlParserUtil.parse(sqlStr); + ParenthesedSelect subSelect = (ParenthesedSelect) select.getFromItem(); + Values values = (Values) subSelect.getSelect(); + assertEquals( 3, values.getExpressions().size() ); + + +.. tab:: Expression + + .. code-block:: SQL + + UPDATE test + SET ( a + , b + , c ) = ( VALUES 1, 2, 3 ) + ; + + + .. code-block:: TEXT + + SQL Text + └─Statements: statement.update.Update + ├─Table: test + └─updateSets: statement.update.UpdateSet + ├─ParenthesedExpressionList: (a, b, c) + └─ExpressionList: (VALUES 1, 2, 3) + + + .. code-block:: JAVA + + Update update = (Update) CCJSqlParserUtil.parse(sqlStr); + UpdateSet updateSet = update.getUpdateSets().get(0); + ParenthesedSelect subSelect = (ParenthesedSelect) updateSet.getValues().get(0); + Values values = (Values) subSelect.getSelect(); + assertEquals( 3, values.getExpressions().size() ); + + +.. tab:: Clause + + .. code-block:: SQL + + INSERT INTO test + VALUES ( 1, 2, 3 ) + ; + + .. code-block:: TEXT + + SQL Text + └─Statements: statement.insert.Insert + ├─Table: test + └─select: statement.select.Values + └─ParenthesedExpressionList: (1, 2, 3) + + + .. code-block:: JAVA + + Insert insert = (Insert) CCJSqlParserUtil.parse(sqlStr); + Values values = (Values) insert.getSelect(); + Assertions.assertEquals(3, values.getExpressions().size()); + + +`Expression` Lists +--------------------------------- + +The class ``ExpressionList`` extends ``List<Expression>`` directly and so ``ExpressionList.getExpressions()`` is obsolete. + +Any instance of `List<Expression>` is considered an Anti Pattern and the class ``ExpressionList<T extends Expression>`` shall be used instead. + +``ItemsList`` has been removed and ``ExpressionList`` is used instead. + +``MultiExpressionList`` has been removed and ``ExpressionList`` is used instead (with ``ExpressionList`` elements). + +.. tab:: ExpressionList + + .. code-block:: SQL + + SELECT Function( a, b, c ) + FROM dual + GROUP BY a + , b + , c + ; + + + .. code-block:: TEXT + + SQL Text + └─Statements: statement.select.PlainSelect + ├─selectItems: statement.select.SelectItem + │ └─expression: expression.Function + │ └─ExpressionList: a, b, c + ├─Table: dual + └─groupBy: statement.select.GroupByElement + └─ExpressionList: a, b, c + + + .. code-block:: JAVA + + PlainSelect select = (PlainSelect) CCJSqlParserUtil.parse(sqlStr); + Function function = (Function) select.getSelectItem(0).getExpression(); + assertEquals(3, function.getParameters().size()); + + ExpressionList<?> groupByExpressions=select.getGroupBy().getGroupByExpressionList(); + assertEquals(3, groupByExpressions.size()); + + +.. tab:: Wrapped ExpressionList + + .. code-block:: SQL + + SELECT ( ( 1, 2, 3 ), ( 4, 5, 6 ), ( 7, 8, 9 ) ) + ; + + + .. code-block:: TEXT + + SQL Text + └─Statements: statement.select.PlainSelect + └─selectItems: statement.select.SelectItem + └─ParenthesedExpressionList: ((1, 2, 3), (4, 5, 6), (7, 8, 9)) + + + .. code-block:: JAVA + + PlainSelect select = (PlainSelect) CCJSqlParserUtil.parse(sqlStr); + ParenthesedExpressionList<?> expressionList = (ParenthesedExpressionList<?>) select.getSelectItem(0).getExpression(); + + ParenthesedExpressionList<?> expressionList1 = (ParenthesedExpressionList<?>) expressionList.get(0); + assertEquals(3, expressionList1.size()); + + +Generic `SelectItem` +--------------------------------- + +The class ``SelectItem<T extends Expression>`` is now generic and various derivatives (e. |_| g. ``SelectExpressionItem``, ``FunctionItem``, ``ExpressionListItem``) have been removed. + + +`Select` Statement +--------------------------------- + +``SelectBody`` has been removed and ``PlainSelect`` can be used directly + +``SubJoin`` has been replaced by ``ParenthesedFromItem`` (implementing a ``FromItem`` with a regular list of ``Join``) + +``SubSelect`` has been removed and any instance of ``Select`` (``PlainSelect``, ``Values`` or ``SetOperationList``) can be used instead + +.. tab:: Select + + .. code-block:: SQL + + ( + SELECT * + FROM ( SELECT 1 ) + UNION ALL + SELECT * + FROM ( VALUES 1, 2, 3 ) + UNION ALL + VALUES ( 1, 2, 3 ) ) + ; + + .. code-block:: TEXT + + SQL Text + └─Statements: statement.select.ParenthesedSelect + └─select: statement.select.SetOperationList + ├─selects: statement.select.PlainSelect + │ ├─selectItems: statement.select.SelectItem + │ │ └─AllColumns: * + │ └─fromItem: statement.select.ParenthesedSelect + │ └─select: statement.select.PlainSelect + │ └─selectItems: statement.select.SelectItem + │ └─LongValue: 1 + ├─selects: statement.select.PlainSelect + │ ├─selectItems: statement.select.SelectItem + │ │ └─AllColumns: * + │ └─fromItem: statement.select.ParenthesedSelect + │ └─select: statement.select.Values + │ └─ExpressionList: 1, 2, 3 + ├─selects: statement.select.Values + │ └─ParenthesedExpressionList: (1, 2, 3) + ├─UnionOp: UNION ALL + └─UnionOp: UNION ALL + + + .. code-block:: JAVA + + ParenthesedSelect parenthesedSelect = (ParenthesedSelect) CCJSqlParserUtil.parse(sqlStr); + SetOperationList setOperationList = parenthesedSelect.getSetOperationList(); + + PlainSelect select1 = (PlainSelect) setOperationList.getSelect(0); + PlainSelect subSelect1 = ((ParenthesedSelect) select1.getFromItem()).getPlainSelect(); + Assertions.assertEquals( 1L, subSelect1.getSelectItem(0).getExpression(LongValue.class).getValue()); + + Values values = (Values) setOperationList.getSelect(2); + Assertions.assertEquals( 3, values.getExpressions().size()); + + + +.. tab:: Join + + .. code-block:: SQL + + SELECT * + FROM a + INNER JOIN ( b + LEFT JOIN c + ON b.d = c.d ) + ON a.e = b.e + ; + + .. code-block:: TEXT + + SQL Text + └─Statements: statement.select.PlainSelect + ├─selectItems: statement.select.SelectItem + │ └─AllColumns: * + ├─Table: a + └─joins: statement.select.Join + ├─rightItem: statement.select.ParenthesedFromItem + │ ├─Table: b + │ └─joins: statement.select.Join + │ ├─Table: c + │ └─onExpressions: expression.operators.relational.EqualsTo + │ ├─Column: b.d + │ └─Column: c.d + └─onExpressions: expression.operators.relational.EqualsTo + ├─Column: a.e + └─Column: b.e + + + .. code-block:: JAVA + + PlainSelect select = (PlainSelect) CCJSqlParserUtil.parse(sqlStr); + Table aTable = (Table) select.getFromItem(); + + ParenthesedFromItem fromItem = (ParenthesedFromItem) select.getJoin(0).getFromItem(); + Table bTable = (Table) fromItem.getFromItem(); + + Join join = fromItem.getJoin(0); + Table cTable = (Table) join.getFromItem(); + + assertEquals("c", cTable.getName()); + + +Brackets +--------------------------------- + +Any `hasBrackets()`, `isUsingBrackets()` and similar methods have been removed; instead the Parser will return a ``ParenthesedExpressionList`` or ``ParenthesedSelect`` or ``ParenthesedFromItem`` or ``Parenthesis`` wrapping the object within brackets. + +This allows for much better bracket handling. + +.. code-block:: SQL + :caption: `Parenthesis` and Brackets example + + ( SELECT ( 1 ) ) + ; + + +.. code-block:: TEXT + + SQL Text + └─Statements: statement.select.ParenthesedSelect + └─select: statement.select.PlainSelect + └─selectItems: statement.select.SelectItem + └─expression: expression.Parenthesis + └─LongValue: 1 + + +.. code-block:: JAVA + + ParenthesedSelect parenthesedSelect = (ParenthesedSelect) CCJSqlParserUtil.parse(sqlStr); + SetOperationList setOperationList = parenthesedSelect.getSetOperationList(); + + PlainSelect select1 = (PlainSelect) setOperationList.getSelect(0); + PlainSelect subSelect1 = ((ParenthesedSelect) select1.getFromItem()).getPlainSelect(); + Assertions.assertEquals( 1L, subSelect1.getSelectItem(0).getExpression(LongValue.class).getValue()); + + Values values = (Values) setOperationList.getSelect(2); + Assertions.assertEquals( 3, values.getExpressions().size()); + + + +`UpdateSet` clause +--------------------------------- + +A ``List<UpdateSet>`` is used for any `Set` clause within `Insert`, `Update`, `Upsert` or `Merge` statements. + + +.. code-block:: SQL + :caption: `UpdateSet` example + + UPDATE a + SET ( a + , b + , c ) = ( 1 + , 2 + , 3 ) + , d = 4 + ; + + +.. code-block:: TEXT + + SQL Text + └─Statements: statement.update.Update + ├─Table: a + ├─updateSets: statement.update.UpdateSet + │ ├─ParenthesedExpressionList: (a, b, c) + │ └─ParenthesedExpressionList: (1, 2, 3) + └─updateSets: statement.update.UpdateSet + ├─ExpressionList: d + └─ExpressionList: 4 + + +.. code-block:: JAVA + + Update update = (Update) CCJSqlParserUtil.parse(sqlStr); + UpdateSet updateSet1 = update.getUpdateSet(0); + Assertions.assertEquals( 3, updateSet1.getColumns().size()); + Assertions.assertEquals( 3, updateSet1.getValues().size()); + + UpdateSet updateSet2 = update.getUpdateSet(1); + Assertions.assertEquals( "d", updateSet2.getColumn(0).getColumnName()); + Assertions.assertEquals( 4L, ((LongValue) updateSet2.getValue(0)).getValue() ); + + +`Statements` collection +--------------------------------- + +The ``Statements`` class extends `List<Statement>` directly and so `Statements.getStatements()` is obsolete. + diff --git a/src/site/sphinx/migration50.rst b/src/site/sphinx/migration50.rst new file mode 100644 index 0000000..eb0a992 --- /dev/null +++ b/src/site/sphinx/migration50.rst @@ -0,0 +1,96 @@ +********************************* +Migration to 5.0 +********************************* + +The new JSQLParser 5 introduces API-breaking changes: + +1. **Dependency on Java 11 or newer** + +2. **Reworked AST Visitors** + + The AST Visitors have been reworked to pass a Generic Context from the Root Node down to the Leaves. + + .. code-block:: java + :caption: Generic Interface + + public interface SelectVisitor<T> { + + <S> T visit(PlainSelect plainSelect, S context); + + default void visit(PlainSelect plainSelect) { + this.visit(plainSelect, null); + } + + } + + .. code-block:: java + :caption: Sample Implementation + + public class JSQLColumnResolver + implements SelectVisitor<JdbcResultSetMetaData>, FromItemVisitor<JdbcResultSetMetaData> { + + @Override + public <S> JdbcResultSetMetaData visit(PlainSelect select, S context) { + if (context instanceof JdbcMetaData) { + return visit(select, (JdbcMetaData) context); + } + return null; + } + + public JdbcResultSetMetaData visit(PlainSelect select, JdbcMetaData metaData) { + JdbcResultSetMetaData resultSetMetaData = new JdbcResultSetMetaData(); + + // Logic to retrieve the column information + resultSetMetaData = getColumn(metaData, select.getFromItem(), select.getJoins()); + + return resultSetMetaData; + } + } + +3. **Generic Result from Leaves to Root** + + Node objects now return a Generic Result from the Leaves up to the Root. + + .. code-block:: java + :caption: AST Node + + public class PlainSelect extends Select { + @Override + public <T, S> T accept(SelectVisitor<T> selectVisitor, S context) { + return selectVisitor.visit(this, context); + } + } + +How is this useful? Consider resolving the `AllColumns` ``*`` or `AllTableColumns` ``t.*`` expressions to retrieve the actual column names. This process depends on the database's physical metadata and the context of the current scope, including virtual data frames (like sub-selects and with-clauses). + +Therefore, every branch of the AST must receive scoped metadata from its parent node. Each AST node must receive the resolved columns from its child nodes. A global result object (like the `StringBuilder` in the `DepParser` implementations) is inadequate. + +Alternatively, consider substituting `TimeValueKey` (``CURRENT_DATE``, ``CURRENT_TIME``, etc.) with actual date or time values. You can push a simple `Map` of key/value pairs down to the Expression Visitor: + + .. code-block:: java + :caption: Expression Visitor + + @Override + public <S> StringBuilder visit(TimeKeyExpression expression, S context) { + if (context instanceof Map) { + return visit(expression, (Map<String, Object>) substitutions); + } else { + return expression.toString(); + } + } + + public StringBuilder visit(TimeKeyExpression expression, Map<String, Object> substitutions) { + // Remove possible trailing brackets "()" + String value = expression.getStringValue().toUpperCase().replaceAll("[()]", ""); + + if (substitutions.containsKey(value)) { + // @todo: Cast Date/Time types + return castDateTime(substitutions.get(value).toString()).accept(this, null); + } else { + return super.visit(expression, null); + } + } + +Another advantage is parallel processing: Without relying on a global result object, the AST can be traversed in parallel (whereas it currently must be traversed strictly in serial). + +Finally, any child node can now know its parent and identify who called it. diff --git a/src/site/sphinx/unsupported.rst b/src/site/sphinx/unsupported.rst new file mode 100644 index 0000000..b6489a8 --- /dev/null +++ b/src/site/sphinx/unsupported.rst @@ -0,0 +1,57 @@ +*************************************** +Unsupported Grammar of various RDBMS +*************************************** + +*JSQLParser* is a RDBMS agnostic parser with a certain focus on SQL:2016 Standard compliant Queries and the "Big Four" (Oracle, MS SQL Server, Postgres, MySQL/MariaDB). +We would like to recommend writing portable, standard compliant SQL in general. + +- Postgres Implicit cast is not supported. + + .. code-block:: java + + SELECT date '2022-12-31'; + SELECT double precision 1; + + +- Oracle PL/SQL blocks are not support. + + .. code-block:: sql + + DECLARE + num NUMBER; + BEGIN + num := 10; + dbms_output.put_line('The number is ' || num); + END; + + + +- Oracle `INSERT ALL ...` is not supported + + .. code-block:: sql + + INSERT ALL + INTO mytable (column1, column2, column_n) VALUES (expr1, expr2, expr_n) + INTO mytable (column1, column2, column_n) VALUES (expr1, expr2, expr_n) + INTO mytable (column1, column2, column_n) VALUES (expr1, expr2, expr_n) + SELECT * FROM dual; + +- DDL statements + + While *JSQLParser* provides a lot of generic support for DDL statements, it is possible that certain RDBMS specific syntax (especially about indices, encodings, compression) won't be supported. + +- `JSON` or `XML` specific syntax and functions + + While *JSQLParser* provides a lot of generic support for `JSON` or `XML` processing, it is possible that certain RDBMS specific syntax or functions won't be supported. + +- Interval Operators + + Anything like `DAY HOUR MINUTE SECOND [TO HOUR MINUTE SECOND]` is not supported.: + + .. code-block:: sql + + values cast ((time '12:03:34' - time '11:57:23') minute to second as varchar(8)); + + + + diff --git a/src/site/sphinx/usage.rst b/src/site/sphinx/usage.rst new file mode 100644 index 0000000..378166d --- /dev/null +++ b/src/site/sphinx/usage.rst @@ -0,0 +1,320 @@ +****************************** +How to use it +****************************** + +.. hint:: + + 1) **Quoting:** Double Quotes ``".."`` are used for quoting identifiers. Parsing T-SQL on **MS SQL Server** or **Sybase** with Squared Brackets ``[..]`` depends on ``Squared Bracket Quotation`` as shown in section :ref:`Define the Parser Features` below. + + 2) JSQLParser uses a more restrictive list of ``Reserved Keywords`` and such keywords will **need to be quoted**. + + 3) **Escaping:** JSQLParser pre-defines standard compliant **Single Quote** ``'..`` **Escape Character**. Additional Back-slash ``\..`` Escaping needs to be activated by setting the ``BackSlashEscapeCharacter`` parser feature. See section :ref:`Define the Parser Features` below for details. + + 4) Oracle Alternative Quoting is partially supported for common brackets such as ``q'{...}'``, ``q'[...]'``, ``q'(...)'`` and ``q''...''``. + + 5) Supported Statement Separators are Semicolon ``;``, ``GO``, Slash ``/`` or two empty lines ``\n\n\n``. + + +Compile from Source Code +============================== + +You will need to have ``JDK 8`` or ``JDK 11`` installed. Please note that JSQLParser-4.9 is the last ``JDK 8`` compatible release and all development after will depend on ``JDK 11``. + +.. tab:: Maven + + .. code-block:: shell + + git clone https://github.com/JSQLParser/JSqlParser.git + cd jsqlformatter + mvn install + +.. tab:: Gradle + + .. code-block:: shell + + git clone https://github.com/JSQLParser/JSqlParser.git + cd jsqlformatter + gradle publishToMavenLocal + + + +Build Dependencies +============================== + +.. tab:: Maven Release + + .. code-block:: xml + :substitutions: + + <dependency> + <groupId>com.github.jsqlparser</groupId> + <artifactId>jsqlparser</artifactId> + <version>|JSQLPARSER_VERSION|</version> + </dependency> + +.. tab:: Maven Snapshot + + .. code-block:: xml + :substitutions: + + <repositories> + <repository> + <id>jsqlparser-snapshots</id> + <snapshots> + <enabled>true</enabled> + </snapshots> + <url>https://oss.sonatype.org/content/groups/public/</url> + </repository> + </repositories> + <dependency> + <groupId>com.github.jsqlparser</groupId> + <artifactId>jsqlparser</artifactId> + <version>|JSQLPARSER_SNAPSHOT_VERSION|</version> + </dependency> + +.. tab:: Gradle Stable + + .. code-block:: groovy + :substitutions: + + repositories { + mavenCentral() + } + + dependencies { + implementation 'com.github.jsqlparser:jsqlparser:|JSQLPARSER_VERSION|' + } + +.. tab:: Gradle Snapshot + + .. code-block:: groovy + :substitutions: + + repositories { + maven { + url = uri('https://oss.sonatype.org/content/groups/public/') + } + } + + dependencies { + implementation 'com.github.jsqlparser:jsqlparser:|JSQLPARSER_SNAPSHOT_VERSION|' + } + + +Parse a SQL Statement +============================== + +Parse the SQL Text into Java Objects: + +.. code-block:: java + + String sqlStr = "select 1 from dual where a=b"; + + PlainSelect select = (PlainSelect) CCJSqlParserUtil.parse(sqlStr); + + SelectItem selectItem = + select.getSelectItems().get(0); + Assertions.assertEquals( + new LongValue(1) + , selectItem.getExpression()); + + Table table = (Table) select.getFromItem(); + Assertions.assertEquals("dual", table.getName()); + + EqualsTo equalsTo = (EqualsTo) select.getWhere(); + Column a = (Column) equalsTo.getLeftExpression(); + Column b = (Column) equalsTo.getRightExpression(); + Assertions.assertEquals("a", a.getColumnName()); + Assertions.assertEquals("b", b.getColumnName()); + + +For guidance with the API, use `JSQLFormatter <http://jsqlformatter.manticore-projects.com>`_ to visualize the Traversable Tree of Java Objects: + +.. raw:: html + + <div class="highlight"> + <pre> + SQL Text + └─Statements: net.sf.jsqlparser.statement.select.Select + ├─selectItems -> Collection<SelectItem> + │ └─LongValue: 1 + ├─Table: dual + └─where: net.sf.jsqlparser.expression.operators.relational.EqualsTo + ├─Column: a + └─Column: b + </pre> + </div> + +Error Handling +============================== + +There are two features for handling errors + +- ``parser.withErrorRecovery(true)`` will continue to the next statement separator and return an empty statement. +- ``parser.withUnsupportedStatements(true)`` will return an instance of the `UnsupportedStatement` class, although the first statement **must** be a regular statement + +.. code-block:: java + :caption: Error Recovery + + CCJSqlParser parser = new CCJSqlParser( + "select * from mytable; select from; select * from mytable2" ); + Statements statements = parser.withErrorRecovery().Statements(); + + // 3 statements, the failing one set to NULL + assertEquals(3, statements.size()); + assertNull(statements.get(1)); + + // errors are recorded + assertEquals(1, parser.getParseErrors().size()); + +.. code-block:: java + :caption: Unsupported Statement + + Statements statements = CCJSqlParserUtil.parseStatements( + "select * from mytable; select from; select * from mytable2; select 4;" + , parser -> parser.withUnsupportedStatements() ); + + // 4 statements with one Unsupported Statement holding the content + assertEquals(4, statements.size()); + assertInstanceOf(UnsupportedStatement.class, statements.get(1)); + assertEquals("select from", statements.get(1).toString()); + + // no errors records, because a statement has been returned + assertEquals(0, parser.getParseErrors().size()); + + +Use the Visitor Patterns +============================== + +Traverse the Java Object Tree using the Visitor Patterns: + +.. code-block:: java + + // Define an Expression Visitor reacting on any Expression + // Overwrite the visit() methods for each Expression Class + ExpressionVisitorAdapter<Void> expressionVisitorAdapter = new ExpressionVisitorAdapter<>() { + public <S> Void visit(EqualsTo equalsTo, S context) { + equalsTo.getLeftExpression().accept(this, context); + equalsTo.getRightExpression().accept(this, context); + return null; + } + public <S> Void visit(Column column, S context) { + System.out.println("Found a Column " + column.getColumnName()); + return null; + } + }; + + // Define a Select Visitor reacting on a Plain Select invoking the Expression Visitor on the Where Clause + SelectVisitorAdapter<Void> selectVisitorAdapter = new SelectVisitorAdapter<>() { + @Override + public <S> Void visit(PlainSelect plainSelect, S context) { + return plainSelect.getWhere().accept(expressionVisitorAdapter, context); + } + }; + + // Define a Statement Visitor for dispatching the Statements + StatementVisitorAdapter<Void> statementVisitor = new StatementVisitorAdapter<>() { + public <S> Void visit(Select select, S context) { + return select.getSelectBody().accept(selectVisitorAdapter, context); + } + }; + + String sqlStr="select 1 from dual where a=b"; + Statement stmt = CCJSqlParserUtil.parse(sqlStr); + + // Invoke the Statement Visitor without a context + stmt.accept(statementVisitor, null); + +Find Table Names +============================== + +The class ``net.sf.jsqlparser.util.TablesNamesFinder`` can be used to return all Table Names from a Query or an Expression. + +.. code-block:: java + + // find in Statements + String sqlStr = "select * from A left join B on A.id=B.id and A.age = (select age from C)"; + Set<String> tableNames = TablesNamesFinder.findTables(sqlStr); + assertThat( tableNames ).containsExactlyInAnyOrder("A", "B", "C"); + + // find in Expressions + String exprStr = "A.id=B.id and A.age = (select age from C)"; + tableNames = TablesNamesFinder.findTablesInExpression(exprStr); + assertThat( tableNames ).containsExactlyInAnyOrder("A", "B", "C"); + + +Build a SQL Statement +============================== + +Build any SQL Statement from Java Code using a fluent API: + +.. code-block:: java + + String expectedSQLStr = "SELECT 1 FROM dual t WHERE a = b"; + + // Step 1: generate the Java Object Hierarchy for + Table table = new Table().withName("dual").withAlias(new Alias("t", false)); + + Column columnA = new Column().withColumnName("a"); + Column columnB = new Column().withColumnName("b"); + Expression whereExpression = + new EqualsTo().withLeftExpression(columnA).withRightExpression(columnB); + + PlainSelect select = new PlainSelect().addSelectItem(new LongValue(1)) + .withFromItem(table).withWhere(whereExpression); + + // Step 2a: Print into a SQL Statement + Assertions.assertEquals(expectedSQLStr, select.toString()); + + // Step 2b: De-Parse into a SQL Statement + StringBuilder builder = new StringBuilder(); + StatementDeParser deParser = new StatementDeParser(builder); + deParser.visit(select); + + Assertions.assertEquals(expectedSQLStr, builder.toString()); + + +Define the Parser Features +============================== + +JSQLParser interprets Squared Brackets ``[..]`` as Arrays, which does not work with MS SQL Server and T-SQL. Please use the Parser Features to instruct JSQLParser to read Squared Brackets as Quotes instead. + +JSQLParser allows for standard compliant Single Quote ``'..`` Escaping. Additional Back-slash ``\..`` Escaping needs to be activated by setting the ``BackSlashEscapeCharacter`` parser feature. + +Additionally there are Features to control the Parser's effort at the cost of the performance. + +.. code-block:: java + + String sqlStr="select 1 from [sample_table] where [a]=[b]"; + + // T-SQL Square Bracket Quotation + Statement stmt = CCJSqlParserUtil.parse( + sqlStr + , parser -> parser + .withSquareBracketQuotation(true) + ); + + // Set Parser Timeout to 6000 ms + Statement stmt1 = CCJSqlParserUtil.parse( + sqlStr + , parser -> parser + .withSquareBracketQuotation(true) + .withTimeOut(6000) + ); + + // Allow Complex Parsing (which allows nested Expressions, but is much slower) + Statement stmt2 = CCJSqlParserUtil.parse( + sqlStr + , parser -> parser + .withSquareBracketQuotation(true) + .withAllowComplexParsing(true) + .withTimeOut(6000) + ); + + // Allow Back-slash escaping + sqlStr="SELECT ('\\'Clark\\'', 'Kent')"; + Statement stmt2 = CCJSqlParserUtil.parse( + sqlStr + , parser -> parser + .withBackslashEscapeCharacter(true) + );