diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 000000000..a3108f35b
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,104 @@
+name: CI Pipeline
+
+on:
+ push:
+ branches: [ "**" ] # Run on every commit to any branch
+ pull_request:
+ branches: [ "**" ] # Run for PRs from any branch
+ workflow_dispatch:
+
+permissions: write-all
+
+jobs:
+ gradle_check:
+ name: Gradle Check
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [ ubuntu-latest, windows-latest, macos-latest ]
+ steps:
+ - uses: actions/checkout@main
+ with:
+ fetch-depth: 0
+
+ - name: Set up JDK 17
+ uses: actions/setup-java@main
+ with:
+ java-version: '17'
+ distribution: 'temurin'
+
+ - name: Set up Gradle
+ uses: gradle/actions/setup-gradle@main
+
+ - name: Run Gradle Check
+ run: ./gradlew check
+
+ maven_verify:
+ name: Maven Verify
+ needs: gradle_check # ✅ Run only after Gradle check succeeds
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+# os: [ ubuntu-latest, windows-latest, macos-latest ]
+ os: [ ubuntu-latest, macos-latest ]
+ steps:
+ - uses: actions/checkout@main
+ with:
+ fetch-depth: 0
+
+ - name: Set up JDK 17
+ uses: actions/setup-java@main
+ with:
+ java-version: '17'
+ distribution: 'temurin'
+
+ - name: Run Maven Verify
+ run: mvn --batch-mode verify
+
+ gradle_publish:
+ name: Gradle Publish
+ needs: [ gradle_check, maven_verify ] # ✅ Run only after both succeed
+ if: github.ref == 'refs/heads/master' && github.repository == 'JSQLParser/JSqlParser' # ✅ Only for master branch of main repo
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@main
+ with:
+ fetch-depth: 0
+
+ - name: Set up JDK 17
+ uses: actions/setup-java@main
+ with:
+ java-version: '17'
+ distribution: 'temurin'
+
+ - name: Build with Gradle
+ uses: gradle/actions/setup-gradle@main
+
+ - name: Publish with Gradle
+ run: ./gradlew publish
+ env:
+ ossrhUsername: ${{ secrets.OSSRHUSERNAME }}
+ ossrhPassword: ${{ secrets.OSSRHPASSWORD }}
+
+ - uses: actions/setup-python@main
+
+ - name: Install XSLT Processor
+ run: sudo apt-get install -y xsltproc sphinx-common
+
+ - name: Install Python dependencies
+ run: pip install manticore_sphinx_theme sphinx_javadoc_xml myst_parser sphinx_substitution_extensions sphinx_issues sphinx_inline_tabs pygments
+
+ - name: Build Sphinx documentation with Gradle
+ run: FLOATING_TOC=false ./gradlew -DFLOATING_TOC=false gitChangelogTask renderRR xslt xmldoc sphinx
+
+ - name: Configure GitHub Pages
+ uses: actions/configure-pages@main
+
+ - name: Upload artifact
+ uses: actions/upload-pages-artifact@main
+ with:
+ path: 'build/sphinx'
+
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@main
diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml
deleted file mode 100644
index 14411e519..000000000
--- a/.github/workflows/gradle.yml
+++ /dev/null
@@ -1,40 +0,0 @@
-# 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: Java CI with Gradle
-
-on:
- push:
- branches: [ "master" ]
- pull_request:
- branches: [ "master" ]
-
-permissions:
- contents: read
-
-jobs:
- build:
-
- 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.4.2
- with:
- arguments: build check
- # arguments: build check publish
- env:
- ossrhUsername: ${{ secrets.OSSRH_USERNAME }}
- ossrhPassword: ${{ secrets.OSSRH_TOKEN }}
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
deleted file mode 100644
index dad694833..000000000
--- a/.github/workflows/maven.yml
+++ /dev/null
@@ -1,30 +0,0 @@
-# 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: Java CI with Maven
-
-on:
- push:
- branches: [ master ]
- pull_request:
- branches: [ master ]
-
-jobs:
- build:
-
- runs-on: ubuntu-latest
- strategy:
- matrix:
- java: [8, 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
- - name: Build with Maven
- run: mvn -B package --file pom.xml -DdisableXmlReport=true -Djacoco.skip=true -Dpmd.skip=true
diff --git a/.github/workflows/sphinx.yml b/.github/workflows/sphinx.yml
deleted file mode 100644
index ad8bde188..000000000
--- a/.github/workflows/sphinx.yml
+++ /dev/null
@@ -1,34 +0,0 @@
-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
index 20db77d7c..955e7bf2d 100755
--- a/.gitignore
+++ b/.gitignore
@@ -1,16 +1,17 @@
# Generated by maven
/target
/build
+/out
# 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
+/src/site/sphinx/javadoc_stable.xml
+/src/site/sphinx/javadoc_snapshot.xml
# Generated by javacc-maven-plugin
/bin
@@ -31,3 +32,6 @@
/nbproject/
/.gradle
+
+# Mac
+.DS_Store
diff --git a/README.md b/README.md
index f3449956a..42122c891 100644
--- a/README.md
+++ b/README.md
@@ -1,23 +1,22 @@
-# [JSqlParser 4.7 Website](https://jsqlparser.github.io/JSqlParser)
+# [JSqlParser 5.3 Website](https://jsqlparser.github.io/JSqlParser)
-
-
-[](https://travis-ci.com/JSQLParser/JSqlParser) [](https://coveralls.io/r/JSQLParser/JSqlParser?branch=master)
+[](https://github.com/JSQLParser/JSqlParser/actions/workflows/ci.yml)
+[](https://coveralls.io/r/JSQLParser/JSqlParser?branch=master)
[](https://www.codacy.com/gh/JSQLParser/JSqlParser/dashboard?utm_source=github.com&utm_medium=referral&utm_content=JSQLParser/JSqlParser&utm_campaign=Badge_Grade)
-[](http://maven-badges.herokuapp.com/maven-central/com.github.jsqlparser/jsqlparser)
-[](https://www.javadoc.io/doc/com.github.jsqlparser/jsqlparser)
-
+[](https://central.sonatype.com/artifact/com.github.jsqlparser/jsqlparser) [](https://www.javadoc.io/doc/com.github.jsqlparser/jsqlparser)
[](https://gitter.im/JSQLParser/JSqlParser?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
+A huge thank you to our sponsor, [Starlake.ai](https://starlake.ai/) who simplifies data ingestion, transformation, and orchestration, enabling faster delivery of high-quality data. Starlake has been instrumental in providing Piped SQL and numerous test cases for BigQuery, Redshift, DataBricks, and DuckDB. Show your support for ongoing development by visiting Starlake.ai and giving us a star!
+
## 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)):
+Please visit our [WebSite](https://jsqlparser.github.io/JSqlParser) for detailed information. **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
+/* produces the following AST
+
SQL Text
└─Statements: statement.select.PlainSelect
├─selectItems: statement.select.SelectItem
@@ -26,6 +25,7 @@ SQL Text
└─where: expression.operators.relational.EqualsTo
├─Column: a
└─Column: b
+*/
```
```java
@@ -47,7 +47,42 @@ Column a = (Column) equalsTo.getLeftExpression();
Column b = (Column) equalsTo.getRightExpression();
Assertions.assertEquals("a", a.getColumnName());
Assertions.assertEquals("b", b.getColumnName());
-}
+```
+## Support for `Piped SQL`
+
+Work is progressing for parsing `Piped SQL`, a much saner and more logical way to write queries in its semantic order.
+```sql
+FROM Produce
+|> WHERE
+ item != 'bananas'
+ AND category IN ('fruit', 'nut')
+|> AGGREGATE COUNT(*) AS num_items, SUM(sales) AS total_sales
+ GROUP BY item
+|> ORDER BY item DESC;
+```
+
+For details, please see https://storage.googleapis.com/gweb-research2023-media/pubtools/1004848.pdf, https://cloud.google.com/bigquery/docs/reference/standard-sql/pipe-syntax and https://duckdb.org/docs/sql/query_syntax/from.html#from-first-syntax
+
+## Java Version
+
+JSQLParser-4.9 was the last JDK8 compatible version. JSQLParser-5.0 and later depend on JDK11 and introduce API breaking changes to the AST Visitors. Please see the Migration Guide for the details.
+
+Building JSQLParser-5.1 and newer with Gradle will depend on a JDK17 toolchain due to the used plugins.
+
+JSQLParser-5.4 Snapshot and later use JavaCC-8 Snapshots for generating the parser.
+
+## Performance
+
+Unfortunately the released JSQLParser-5.2 shows a performance deterioration caused by commit [30cf5d7](https://github.com/JSQLParser/JSqlParser/commit/30cf5d7b930ae0a076f49deb5cc841d39e62b3dc) related to `FunctionAllColumns()`.
+This has been resolved in JSQLParser 5.3-SNAPSHOT and JMH benchmarks have been added to avoid such regressions in the future. Further all `LOOKAHEAD` have been revised one by one, and we have gained back a very good performance of the Parser.
+
+As per March-2026, the productions `Condition()`, `RegularCondition()` and `AndExpression()` have been refactored successfully. Furthermore, we have overhauled Token definition and handling of Reserved Keywords. This resulted in a massive performance boost and seem to have solved most of the performance issues.
+
+```text
+Benchmark (version) Mode Cnt Score Error Units
+JSQLParserBenchmark.parseSQLStatements latest avgt 15 7.602 ± 0.135 ms/op <-- March/26
+JSQLParserBenchmark.parseSQLStatements 5.3 avgt 15 84.687 ± 3.321 ms/op
+JSQLParserBenchmark.parseSQLStatements 5.1 avgt 15 86.592 ± 5.781 ms/op
```
## [Supported Grammar and Syntax](https://jsqlparser.github.io/JSqlParser/syntax.html)
@@ -56,13 +91,24 @@ Assertions.assertEquals("b", b.getColumnName());
| 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 ...` |
-
+| BigQuery
Snowflake
DuckDB
Redshift
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 ...` |
+| PostgreSQL Row Level Security | `CREATE POLICY`
`ALTER TABLE ... ENABLE/DISABLE/FORCE/NO FORCE ROW LEVEL SECURITY` |
+| Salesforce SOQL | `INCLUDES`, `EXCLUDES` |
+| Piped SQL (also known as FROM SQL) | |
**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)).
+## Sister Projects
+
+If you like JSqlParser then please check out its related projects:
+
+* [JSQLFormatter](https://manticore-projects.com/JSQLFormatter/index.html) for pretty printing and formatting SQL Text
+
+* [JSQLTranspiler](https://manticore-projects.com/JSQLTranspiler/index.html) for dialect specific rewriting, SQL Column resolution and Lineage, provided by [Starlake.ai](https://starlake.ai/)
+
## 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 handwritten 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)
diff --git a/build.gradle b/build.gradle
index 4974164dc..a690cef6b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,4 +1,13 @@
import se.bjurr.gitchangelog.plugin.gradle.GitChangelogTask
+import com.nwalsh.gradle.saxon.SaxonXsltTask
+
+import java.time.Instant
+
+buildscript {
+ dependencies {
+ classpath group: 'net.sf.saxon', name: 'Saxon-HE', version: 'latest.release'
+ }
+}
plugins {
id 'java'
@@ -12,6 +21,7 @@ plugins {
id "com.diffplug.spotless" version "latest.release"
id 'pmd'
id 'checkstyle'
+ id 'eclipse'
// download the RR tools which have no Maven Repository
id "de.undercouch.download" version "latest.release"
@@ -19,7 +29,8 @@ plugins {
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"
+ id "com.nwalsh.gradle.saxon.saxon-gradle" version "latest.release"
+ id 'biz.aQute.bnd.builder' version "latest.release"
}
def getVersion = { boolean considerSnapshot ->
@@ -27,58 +38,106 @@ def getVersion = { boolean considerSnapshot ->
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')
- }
+ String commit = null
+ String snapshot = ""
+
+ def versionStr = providers.exec {
+ commandLine "git", "--no-pager", "-C", project.projectDir, "describe", "--tags", "--always", "--dirty=-SNAPSHOT"
+ }.standardOutput.asText.get().trim()
+
+ def pattern = /jsqlparser-(?\d*)\.(?\d*)(\.(?\d*))?(-(?\d*)-(?[a-zA-Z\d]*))?/
+ def matcher = versionStr =~ pattern
+
+ if (matcher.find()) {
+ major = matcher.group('major') as Integer ?: 0
+ minor = matcher.group('minor') as Integer ?: 0
+ patch = matcher.group('patch') as Integer ?: null
+ build = matcher.group('build') as Integer ?: null
+ commit = matcher.group('commit') ?: null
+ }
- if (considerSnapshot && ( versionStr.endsWith('SNAPSHOT') || build!=null) ) {
- minor++
- if (patch!=null) patch = 0
- snapshot = "-SNAPSHOT"
- }
+ if (considerSnapshot && (versionStr.endsWith('SNAPSHOT') || build != null)) {
+ minor++
+ if (patch != null) patch = 0
+ snapshot = "-SNAPSHOT"
}
- return patch!=null
+
+ return patch != null
? "${major}.${minor}.${patch}${snapshot}"
- : "${major}.${minor}${snapshot}"
+ : "${major}.${minor}${snapshot}"
}
+
// for publishing a release, call Gradle with Environment Variable RELEASE:
-// RELEASE=true gradle JSQLParser:publish
+// RELEASE=true gradle JSQLParser:publish
version = getVersion( !System.getenv("RELEASE") )
group = 'com.github.jsqlparser'
description = 'JSQLParser library'
-archivesBaseName = "JSQLParser"
+
+tasks.register('generateBuildInfo') {
+ outputs.dir layout.buildDirectory.file("resources/main")
+ doLast {
+ def outputDir = new File( layout.buildDirectory.file("generated/sources/buildinfo/java/main").get().asFile, "net/sf/jsqlparser")
+ outputDir.mkdirs()
+
+ def gitVersionStr = providers.exec {
+ commandLine "git", "--no-pager", "-C", project.projectDir, "describe", "--tags", "--always", "--dirty=-SNAPSHOT"
+ }.standardOutput.asText.get().trim()
+
+ def gitCommitStr = providers.exec {
+ commandLine "git", "--no-pager", "-C", project.projectDir, "rev-parse", "--short", "HEAD"
+ }.standardOutput.asText.get().trim()
+
+ def buildTime = Instant.now().toString()
+
+ def content = """\
+ |package net.sf.jsqlparser;
+ |
+ |public final class BuildInfo {
+ | public static final String NAME = "${project.name}";
+ | public static final String VERSION = "${gitVersionStr}";
+ | public static final String GIT_COMMIT = "${gitCommitStr ?: 'unknown'}";
+ | public static final String BUILD_TIME = "${buildTime}";
+ |}
+ """.stripMargin()
+
+ new File(outputDir, "BuildInfo.java").text = content
+ }
+}
+
+// Make sure the file is included in the compiled sources
+sourceSets {
+ main {
+ java {
+ srcDir layout.buildDirectory.file("generated/sources/buildinfo/java/main").get().asFile
+ }
+ }
+}
+
+tasks.withType(JavaCompile).configureEach {
+ mustRunAfter("generateBuildInfo")
+}
+
+tasks.withType(Pmd).configureEach {
+ mustRunAfter("generateBuildInfo")
+}
+
+tasks.withType(Checkstyle).configureEach {
+ exclude '**/module-info.java', '**/package-info.java'
+
+ mustRunAfter("generateBuildInfo")
+}
repositories {
gradlePluginPortal()
- mavenLocal()
mavenCentral()
- // Sonatype OSSRH
+ // JavaCC 8 Snapshots
maven {
- url = uri('https://s01.oss.sonatype.org/content/repositories/snapshots/')
+ url = uri('https://central.sonatype.com/repository/maven-snapshots/')
}
+
+ maven { url "https://dev.saxonica.com/maven" }
}
configurations {
@@ -88,99 +147,222 @@ configurations {
dependencies {
testImplementation 'commons-io:commons-io:2.+'
testImplementation 'org.apache.commons:commons-text:+'
- testImplementation 'org.mockito:mockito-core:4.+'
- testImplementation 'org.assertj:assertj-core:3.+'
- testImplementation 'org.hamcrest:hamcrest-core:2.+'
- testImplementation 'org.apache.commons:commons-lang3:3.+'
- testImplementation 'com.h2database:h2:2.+'
+ 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:5.+'
- testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.+'
- testImplementation 'org.junit.jupiter:junit-jupiter-params:+'
+ testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.11.4'
+ testImplementation 'org.junit.jupiter:junit-jupiter-api:5.11.4'
+ testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.11.4'
+ testImplementation 'org.junit.jupiter:junit-jupiter-params:5.11.4'
// https://mvnrepository.com/artifact/org.mockito/mockito-junit-jupiter
- testImplementation 'org.mockito:mockito-junit-jupiter:+'
+ testImplementation 'org.mockito:mockito-junit-jupiter:5.18.0'
// 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:+'
+ xmlDoclet ('com.manticore-projects.tools:xml-doclet:+'){ changing = true }
// enforce latest version of JavaCC
- testImplementation 'net.java.dev.javacc:javacc:+'
- javacc 'net.java.dev.javacc:javacc:+'
+ testImplementation('org.javacc:core:8.1.0-SNAPSHOT') { changing = true }
+ testImplementation('org.javacc.generator:java:8.1.0-SNAPSHOT') { changing = true }
+
+ jmh 'org.openjdk.jmh:jmh-core:1.37'
+ jmh 'org.openjdk.jmh:jmh-generator-annprocess:1.37'
+ javacc('org.javacc:core:8.1.0-SNAPSHOT') { changing = true }
+ javacc('org.javacc.generator:java:8.1.0-SNAPSHOT') { changing = true }
+}
+configurations.configureEach {
+ resolutionStrategy.eachDependency { DependencyResolveDetails details ->
+ if (details.requested.group in ['org.javacc:core', 'org.javacc.generator']) {
+ // Check for updates every build
+ resolutionStrategy.cacheChangingModulesFor 30, 'seconds'
+ }
+ }
}
compileJavacc {
- arguments = [grammar_encoding: 'UTF-8', static: 'false', java_template_type: 'modern']
+ arguments = [
+ grammar_encoding: 'UTF-8',
+ static: 'false',
+ java_template_type: 'modern',
+ // Comment this in to build the parser with tracing.
+ DEBUG_PARSER: 'false',
+ DEBUG_LOOKAHEAD: 'false'
+ ]
+}
+
+// Post-process the generated CCJSqlParserTokenManager.java to split large static
+// array initializers into separate methods, preventing the method from
+// exceeding the JVM's 64KB bytecode limit (which breaks ASM-based tools like sbt-assembly).
+tasks.register('splitTokenManagerStaticInit') {
+ dependsOn(compileJavacc)
+
+ def tokenManagerFile = layout.buildDirectory.file(
+ "generated/javacc/net/sf/jsqlparser/parser/CCJSqlParserTokenManager.java"
+ )
+
+ inputs.file(tokenManagerFile)
+ outputs.file(tokenManagerFile)
+
+ doLast {
+ def file = tokenManagerFile.get().asFile
+ if (!file.exists()) {
+ throw new GradleException("CCJSqlParserTokenManager.java not found at ${file}")
+ }
+ def content = file.text
+
+ // Pattern matches static final array field declarations with inline initialization.
+ // We extract large ones and move their initialization into separate methods.
+ def fieldsToExtract = [
+ // [regex-safe field name, array type for method return]
+ ['stringLiterals', 'int[]'],
+ ['jjstrLiteralImages', 'String[]'],
+ ['jjmatchKinds', 'int[]'],
+ ['jjnewLexState', 'int[]'],
+ ]
+
+ fieldsToExtract.each { entry ->
+ def fieldName = entry[0]
+ def arrayType = entry[1]
+
+ // Match: = { ... };
+ // The field declaration may use 'public' or 'private' and 'static final'
+ def pattern = ~"(?s)((?:public|private)\\s+static\\s+final\\s+${java.util.regex.Pattern.quote(arrayType)}\\s+${fieldName}\\s*=\\s*)\\{(.*?)\\};"
+ def matcher = pattern.matcher(content)
+ if (matcher.find()) {
+ def prefix = matcher.group(1)
+ def body = matcher.group(2)
+ def methodName = "_init_${fieldName}"
+ def replacement = "${prefix}${methodName}();\n" +
+ " private static ${arrayType} ${methodName}() { return new ${arrayType} {${body}}; }"
+ content = matcher.replaceFirst(java.util.regex.Matcher.quoteReplacement(replacement))
+ logger.lifecycle("splitTokenManagerStaticInit: extracted ${fieldName} initialization into ${methodName}()")
+ }
+ }
+
+ // Handle int[][] arrays separately (jjcompositeState, jjnextStateSet)
+ def arrayArrayFields = ['jjcompositeState', 'jjnextStateSet']
+ arrayArrayFields.each { fieldName ->
+ def pattern = ~"(?s)(private\\s+static\\s+final\\s+int\\[\\]\\[\\]\\s+${fieldName}\\s*=\\s*)\\{(.*?)\\};"
+ def matcher = pattern.matcher(content)
+ if (matcher.find()) {
+ def prefix = matcher.group(1)
+ def body = matcher.group(2)
+ def methodName = "_init_${fieldName}"
+ def replacement = "${prefix}${methodName}();\n" +
+ " private static int[][] ${methodName}() { return new int[][] {${body}}; }"
+ content = matcher.replaceFirst(java.util.regex.Matcher.quoteReplacement(replacement))
+ logger.lifecycle("splitTokenManagerStaticInit: extracted ${fieldName} initialization into ${methodName}()")
+ }
+ }
+
+ file.text = content
+ }
+}
+
+tasks.withType(JavaCompile).configureEach {
+ dependsOn('splitTokenManagerStaticInit')
}
java {
withSourcesJar()
withJavadocJar()
- sourceCompatibility = '1.8'
- targetCompatibility = '1.8'
+ sourceCompatibility = '11'
+ targetCompatibility = '11'
+
// needed for XML-Doclet to work (since Doclet changed again with Java 13)
toolchain {
- languageVersion.set(JavaLanguageVersion.of(11))
+ languageVersion.set(JavaLanguageVersion.of(17))
}
}
-compileJava {
- options.release = 8
-}
-
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"
+ )
+ }
+
+ bundle {
+ properties.empty()
+ bnd(
+ "Created-By": System.properties.get('user.name'),
+ "Bundle-SymbolicName": "net.sf.jsqlparser",
+ "Import-Package": "*",
+ "Export-Package": "net.sf.jsqlparser.*",
+ "Automatic-Module-Name": "net.sf.jsqlparser"
+ )
+ }
+
+ dependsOn(generateBuildInfo)
+}
+
tasks.register('xmldoc', Javadoc) {
+ dependsOn(compileJavacc)
+
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
+ // add any generated Java sources
+ source += fileTree(layout.buildDirectory.dir("generated/javacc").get().asFile) {
+ include '**/*.java'
+ }
+ source += fileTree(layout.buildDirectory.dir("generated/jjtree").get().asFile) {
+ include '**/*.java'
+ }
+
+ classpath = sourceSets.main.runtimeClasspath
+
destinationDir = reporting.file("xmlDoclet")
options.docletpath = configurations.xmlDoclet.files as List
- options.doclet = "com.github.markusbernhardt.xmldoclet.XmlDoclet"
+ options.doclet = "com.manticore.tools.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/"
+ from outFile
+ into layout.projectDirectory.dir("src/site/sphinx/").asFile
}
}
}
test {
- environment = [ 'EXPORT_TEST_TO_FILE': 'True' ]
+ environment = [ 'EXPORT_TEST_TO_FILE': 'False' ]
useJUnitPlatform()
// set heap size for the test JVM(s)
- minHeapSize = "128m"
- maxHeapSize = "1G"
+ minHeapSize = "1G"
+ maxHeapSize = "4G"
+
+ // set JVM stack size
+ jvmArgs = ['-Xss2m', '--add-opens=java.base/java.lang=ALL-UNNAMED']
+
+ jacoco {
+ excludes = ['net/sf/jsqlparser/parser/CCJSqlParserTokenManager']
+ }
}
coveralls {
@@ -196,24 +378,25 @@ jacocoTestReport {
}
}
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.80
+ minimum = 0.50
}
excludes = [
'net.sf.jsqlparser.util.validation.*',
'net.sf.jsqlparser.**.*Adapter',
- 'net.sf.jsqlparser.parser.JJTCCJSqlParserState',
- 'net.sf.jsqlparser.parser.TokenMgrError',
- 'net.sf.jsqlparser.parser.StreamProvider',
- 'net.sf.jsqlparser.parser.CCJSqlParserTokenManager',
- 'net.sf.jsqlparser.parser.ParseException',
- 'net.sf.jsqlparser.parser.SimpleNode',
- 'net.sf.jsqlparser.parser.SimpleCharStream',
- 'net.sf.jsqlparser.parser.StringProvider',
+ 'net.sf.jsqlparser.parser.**'
]
}
rule {
@@ -223,19 +406,12 @@ jacocoTestCoverageVerification {
value = 'MISSEDCOUNT'
//@todo: temporarily increased to 7000, we need to bring that down to 5500 after accepting the Keywords PR
- maximum = 7000
- }
+ maximum = 20000
+ }
excludes = [
'net.sf.jsqlparser.util.validation.*',
'net.sf.jsqlparser.**.*Adapter',
- 'net.sf.jsqlparser.parser.JJTCCJSqlParserState',
- 'net.sf.jsqlparser.parser.TokenMgrError',
- 'net.sf.jsqlparser.parser.StreamProvider',
- 'net.sf.jsqlparser.parser.CCJSqlParserTokenManager',
- 'net.sf.jsqlparser.parser.ParseException',
- 'net.sf.jsqlparser.parser.SimpleNode',
- 'net.sf.jsqlparser.parser.SimpleCharStream',
- 'net.sf.jsqlparser.parser.StringProvider',
+ 'net.sf.jsqlparser.parser.**'
]
}
// rule {
@@ -245,17 +421,10 @@ jacocoTestCoverageVerification {
// value = 'MISSEDRATIO'
// maximum = 0.3
// }
-// excludes = [
+// excludes = [
// 'net.sf.jsqlparser.util.validation.*',
// 'net.sf.jsqlparser.**.*Adapter',
-// 'net.sf.jsqlparser.parser.JJTCCJSqlParserState',
-// 'net.sf.jsqlparser.parser.TokenMgrError',
-// 'net.sf.jsqlparser.parser.StreamProvider',
-// 'net.sf.jsqlparser.parser.CCJSqlParserTokenManager',
-// 'net.sf.jsqlparser.parser.ParseException',
-// 'net.sf.jsqlparser.parser.SimpleNode',
-// 'net.sf.jsqlparser.parser.SimpleCharStream',
-// 'net.sf.jsqlparser.parser.StringProvider',
+// 'net.sf.jsqlparser.parser.**'
// ]
// }
}
@@ -263,42 +432,57 @@ jacocoTestCoverageVerification {
spotbugsMain {
reports {
- html {
- enabled = true
- destination = file("build/reports/spotbugs/main/spotbugs.html")
- stylesheet = 'fancy-hist.xsl'
- }
+ html.required.set(true)
+ html.outputLocation.set( layout.buildDirectory.file("reports/spotbugs/main/spotbugs.html").get().asFile )
+ 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
+// do not run over the test, although we should do that eventually
+tasks.named('spotbugsTest').configure {
+ enabled = false
}
pmd {
+ // later versions throw NPE
+ toolVersion = '7.17.0'
+
consoleOutput = true
sourceSets = [sourceSets.main]
// clear the ruleset in order to use configured rules only
ruleSets = []
-
- //rulesMinimumPriority = 1
+ rulesMinimumPriority = 2
ruleSetFiles = files("config/pmd/ruleset.xml")
+}
- pmdMain {
- excludes = [
- "build/generated/*"
- ]
- }
+tasks.named('pmdMain').configure {
+ excludes = [
+ "build/generated/*"
+ , "**/net/sf/jsqlparser/parser/SimpleCharStream.java"
+ ]
}
checkstyle {
sourceSets = [sourceSets.main, sourceSets.test]
- configFile =rootProject.file('config/checkstyle/checkstyle.xml')
+ configFile = rootProject.file('config/checkstyle/checkstyle.xml')
+}
+
+tasks.withType(Checkstyle).configureEach {
+ reports {
+ xml.required = false
+ html.required = true
+ }
+ excludes = [
+ "**/module-info.java"
+ , "net/sf/jsqlparser/parser/SimpleCharStream.java"
+ ]
}
spotless {
@@ -311,78 +495,81 @@ spotless {
// define the steps to apply to those files
trimTrailingWhitespace()
- indentWithSpaces(4) // or spaces. Takes an integer argument if you don't like 4
+ leadingTabsToSpaces(4)
endWithNewline()
}
java {
- indentWithSpaces(4)
+ leadingTabsToSpaces(4)
eclipse().configFile('config/formatter/eclipse-java-google-style.xml')
}
}
-tasks.withType(Checkstyle).configureEach {
- reports {
- xml.required = false
- html.required = true
- }
-}
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
+ def rrDir = layout.buildDirectory.dir("rr").get().asFile
+
+ // Download convert.war
download.run {
src 'http://manticore-projects.com/download/convert.war'
- dest "$buildDir/rr/convert.war"
+ dest new File(rrDir, "convert.war")
overwrite false
+ onlyIfModified true
}
+ // Download rr.war
download.run {
src 'http://manticore-projects.com/download/rr.war'
- dest "$buildDir/rr/rr.war"
+ dest new File(rrDir, "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"
- ]
+ // Convert JJ file to EBNF
+ def ebnfFile = new File(rrDir, "JSqlParserCC.ebnf")
+ def jjFile = layout.buildDirectory.dir("generated/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jj").get().asFile.absolutePath
+
+ def convertProc = new ProcessBuilder('java', '-jar',
+ new File(rrDir, "convert.war").absolutePath,
+ jjFile)
+ .redirectOutput(ebnfFile)
+ .redirectErrorStream(true)
+ .start()
+ if (convertProc.waitFor() != 0) {
+ throw new GradleException("Failed to convert JJ to EBNF")
}
- 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"
- ]
+ // Generate RR diagrams
+ def rrProc = new ProcessBuilder('java', '-jar',
+ new File(rrDir, "rr.war").absolutePath,
+ "-noepsilon",
+ "-color:#4D88FF",
+ "-offset:0",
+ "-width:800",
+ "-out:${new File(rrDir, "JSqlParserCC.xhtml")}",
+ new File(rrDir, "JSqlParserCC.ebnf").absolutePath)
+ .redirectErrorStream(true)
+ .start()
+ rrProc.inputStream.eachLine { logger.info(it) }
+ if (rrProc.waitFor() != 0) {
+ throw new GradleException("Failed to generate RR diagrams")
}
}
}
+
tasks.register('gitChangelogTask', GitChangelogTask) {
- fromRepo = file("$projectDir")
- file = new File("${projectDir}/src/site/sphinx/changelog.rst")
- fromRef = "4.0"
+ fromRepo.set( file("$projectDir").toString() )
+ file.set( new File("${projectDir}/src/site/sphinx/changelog.rst") )
+ fromRevision.set( "4.0")
//toRef = "1.1";
// switch off the formatter since the indentation matters for Mark-down
// @formatter:off
- templateContent ="""
+ templateContent.set ("""
************************
Changelog
************************
@@ -409,43 +596,43 @@ Version {{name}}
{{/issues}}
{{/tags}}
-"""
+""")
// @formatter:on
}
tasks.register('updateKeywords', JavaExec) {
group = "Execution"
- description = "Run the main class with JavaExecTask"
+ description = "Generate the Reserved Keywords documentation"
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")
+ mainClass.set("net.sf.jsqlparser.parser.ParserKeywordsUtils")
dependsOn(compileJava)
}
-xslt {
+tasks.register('xslt', SaxonXsltTask) {
def outFile = version.endsWith("-SNAPSHOT")
- ? file("src/site/sphinx/syntax_snapshot.rst")
- : file("src/site/sphinx/syntax_stable.rst")
+ ? file("src/site/sphinx/syntax_snapshot.rst")
+ : file("src/site/sphinx/syntax_stable.rst")
dependsOn(renderRR)
- stylesheet 'src/main/resources/rr/xhtml2rst.xsl'
+ stylesheet file('src/main/resources/rr/xhtml2rst.xsl')
- parameters (
- "withFloatingToc": System.getenv().getOrDefault("FLOATING_TOC", "true"),
+ parameters(
+ "withFloatingToc": System.getProperty("FLOATING_TOC", "false"),
"isSnapshot": Boolean.toString(version.endsWith("-SNAPSHOT"))
)
// Transform every .xml file in the "input" directory.
- input "$buildDir/rr/JSqlParserCC.xhtml"
+ input layout.buildDirectory.file("rr/JSqlParserCC.xhtml").get()
output outFile
}
tasks.register('sphinx', Exec) {
- dependsOn(gitChangelogTask, renderRR, xslt, updateKeywords, xmldoc)
+ dependsOn(gitChangelogTask, renderRR, xslt, xmldoc)
String PROLOG = """
.. |_| unicode:: U+00A0
@@ -471,7 +658,7 @@ tasks.register('sphinx', Exec) {
, "-Drelease=${getVersion(false)}"
, "-Drst_prolog=$PROLOG"
, "${projectDir}/src/site/sphinx"
- , "${project.buildDir}/sphinx"
+ , layout.buildDirectory.file("sphinx").get().asFile
]
executable "sphinx-build"
@@ -486,7 +673,7 @@ tasks.register('sphinx', Exec) {
}
publish {
- dependsOn(check, gitChangelogTask, renderRR, xslt, updateKeywords, xmldoc)
+ dependsOn(check, gitChangelogTask, renderRR, xslt, xmldoc)
}
publishing {
@@ -495,6 +682,7 @@ publishing {
artifactId = 'jsqlparser'
from components.java
+
versionMapping {
usage('java-api') {
fromResolutionOf('runtimeClasspath')
@@ -503,67 +691,70 @@ publishing {
fromResolutionResult()
}
}
+
pom {
- name = 'JSQLParser library'
- description = 'Parse SQL Statements into Abstract Syntax Trees (AST)'
- url = 'https://github.com/JSQLParser/JSqlParser'
+ name.set('JSQLParser library')
+ description.set('Parse SQL Statements into Abstract Syntax Trees (AST)')
+ url.set('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'
+ name.set('GNU Library or Lesser General Public License (LGPL) V2.1')
+ url.set('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'
+ name.set('The Apache Software License, Version 2.0')
+ url.set('http://www.apache.org/licenses/LICENSE-2.0.txt')
}
}
+
developers {
developer {
- id = 'twa'
- name = 'Tobias Warneke'
- email = 't.warneke@gmx.net'
+ id.set('twa')
+ name.set('Tobias Warneke')
+ email.set('t.warneke@gmx.net')
}
developer {
- id = 'are'
- name = 'Andreas Reichel'
- email = 'andreas@manticore-projects.com'
+ id.set('are')
+ name.set('Andreas Reichel')
+ email.set('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'
+ connection.set('scm:git:https://github.com/JSQLParser/JSqlParser.git')
+ developerConnection.set('scm:git:ssh://git@github.com:JSQLParser/JSqlParser.git')
+ url.set('https://github.com/JSQLParser/JSqlParser.git')
}
}
}
}
+
repositories {
maven {
- name "ossrh"
+ name = "ossrh"
+ def releasesRepoUrl = "https://central.sonatype.com/repository/maven-releases"
+ def snapshotsRepoUrl = "https://central.sonatype.com/repository/maven-snapshots/"
+ url(version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl)
- def releasesRepoUrl = "https://oss.sonatype.org/content/repositories/snapshots"
- def snapshotsRepoUrl= "https://oss.sonatype.org/service/local/staging/deploy/maven2"
- url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
- //credentials(PasswordCredentials)
credentials {
- username = System.getenv("ossrhUsername")
- password = System.getenv("ossrhPassword")
+ username = providers.environmentVariable("ossrhUsername").orNull
+ password = providers.environmentVariable("ossrhPassword").orNull
}
}
-// maven {
-// name = "GitHubPackages"
-//
-// url = uri("https://maven.pkg.github.com/JSQLParser/jsqlparser")
-// credentials(PasswordCredentials)
-// }
}
}
+
signing {
//def signingKey = findProperty("signingKey")
//def signingPassword = findProperty("signingPassword")
//useInMemoryPgpKeys(signingKey, signingPassword)
- sign publishing.publications.mavenJava
+
+ // don't sign SNAPSHOTS
+ if (!version.endsWith('SNAPSHOT')) {
+ sign publishing.publications.mavenJava
+ }
}
tasks.withType(JavaCompile).configureEach {
@@ -572,12 +763,13 @@ tasks.withType(JavaCompile).configureEach {
remotes {
webServer {
- host = findProperty("${project.name}.host")
- user = findProperty("${project.name}.username")
- identity = new File("${System.properties['user.home']}/.ssh/id_rsa")
+ host = findProperty("${project.name}.host") ?: "defaultHost" // Provide default if not found
+ user = findProperty("${project.name}.username") ?: "defaultUsername" // Provide default if not found
+ identity = file("${System.getProperty('user.home')}/.ssh/id_rsa")
}
}
+
tasks.register('upload') {
doFirst {
if (findProperty("${project.name}.host") == null) {
@@ -594,12 +786,24 @@ tasks.register('upload') {
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()) {
+ for (File file: fileTree(include:['*.jar'], dir: layout.buildDirectory.dir("libs").get()).collect()) {
put from: file, into: "download/${project.name}-${versionStable}"
}
}
}
}
+
+ dependsOn(check, assemble, gitChangelogTask, renderRR, xslt, xmldoc)
+}
+
+check {
+ dependsOn jacocoTestCoverageVerification
}
-upload.dependsOn(check, assemble, gitChangelogTask, renderRR, xslt, updateKeywords, xmldoc)
+jmh {
+ includes = ['.*JSQLParserBenchmark.*']
+ warmupIterations = 2
+ fork = 3
+ iterations = 5
+ timeOnIteration = '1s'
+}
\ No newline at end of file
diff --git a/config/pmd/ruleset.xml b/config/pmd/ruleset.xml
index 6b8c681a6..45804d4c0 100644
--- a/config/pmd/ruleset.xml
+++ b/config/pmd/ruleset.xml
@@ -20,20 +20,18 @@ under the License.
- 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.
+ Custom PMD ruleset, compatible with PMD 7.x.
- This ruleset might be used as a starting point for an own customized ruleset [0].
+ Based on the old (pre PMD 6.0.0) rulesets java-basic, java-empty, java-imports,
+ java-unnecessary, java-unusedcode, migrated for PMD 7.
- [0] https://pmd.github.io/latest/pmd_userdocs_making_rulesets.html
-
+ 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
+
-
-
-
@@ -49,7 +47,14 @@ under the License.
-
+
+
+
+
+
+
@@ -67,24 +72,15 @@ under the License.
-
-
-
-
-
-
+
+
+
@@ -94,16 +90,10 @@ under the License.
+
+
-
-
-
-
-
-
-
-
-
+
@@ -111,15 +101,14 @@ under the License.
-
-
-
+
+
-
+
-
+
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index 522c7558e..8b590d14c 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,6 +1,6 @@
# 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.jvmargs=-Xmx8G -Xss8m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError
org.gradle.caching=true
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 7f93135c4..d997cfc60 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 3fa8f862f..dbc3ce4a0 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
diff --git a/gradlew b/gradlew
index 0adc8e1a5..f640dbced 100755
--- a/gradlew
+++ b/gradlew
@@ -1,7 +1,7 @@
#!/bin/sh
#
-# Copyright © 2015-2021 the original authors.
+# Copyright © 2015 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.
@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+# SPDX-License-Identifier: Apache-2.0
+#
##############################################################################
#
@@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
-# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# https://github.com/gradle/gradle/blob//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/.
@@ -84,7 +86,7 @@ done
# 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
+APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -112,7 +114,6 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
@@ -145,7 +146,7 @@ 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=SC3045
+ # shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
@@ -153,7 +154,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
- # shellcheck disable=SC3045
+ # shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@@ -170,7 +171,6 @@ fi
# 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" )
@@ -202,16 +202,15 @@ 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, and $GRADLE_OPTS can contain fragments of
-# shell script including quotes and variable substitutions, so put them in
-# double quotes to make sure that they get re-expanded; and
-# * put everything else in single quotes, so that it's not re-expanded.
+# Collect all arguments for the java command:
+# * DEFAULT_JVM_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 \
+ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
diff --git a/gradlew.bat b/gradlew.bat
index 6689b85be..c4bdd3ab8 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -1,92 +1,93 @@
-@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.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto execute
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-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
+@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
+@rem SPDX-License-Identifier: Apache-2.0
+@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
+
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
+
+: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
index 8771b5deb..2c751cae4 100644
--- a/nb-configuration.xml
+++ b/nb-configuration.xml
@@ -19,7 +19,7 @@
LF
false
true
- JDK_1.8
+ JDK_11
false
none
4
diff --git a/pom.xml b/pom.xml
index da8534c2e..5ac0c4666 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2,11 +2,11 @@
4.0.0
com.github.jsqlparser
jsqlparser
- 4.8
- JSQLParser library
+ 5.4-SNAPSHOT
+ JSQLParser library
2004
- JSQLParser
+ JSQLParser
bundle
https://github.com/JSQLParser/JSqlParser
@@ -24,65 +24,121 @@
+
+
+ javacc8-snapshots
+
+ true
+
+ false
+ https://central.sonatype.com/repository/maven-snapshots/
+
+
+ ossrh-snapshots
+ https://central.sonatype.com/repository/maven-snapshots/
+ true
+ false
+
+
+
+
+ javacc8-snapshots
+
+ true
+
+ false
+ https://central.sonatype.com/repository/maven-snapshots/
+
+
+ ossrh-snapshots
+ https://central.sonatype.com/repository/maven-snapshots/
+ true
+ false
+
+
+
- net.java.dev.javacc
- javacc
- 7.0.13
+ org.javacc
+ core
+ 8.1.0-SNAPSHOT
+ pom
+ test
+
+
+ org.javacc.generator
+ java
+ 8.1.0-SNAPSHOT
test
-
commons-io
commons-io
- 2.11.0
+ 2.18.0
test
- org.junit.jupiter
- junit-jupiter
- 5.9.2
- test
+ org.junit.jupiter
+ junit-jupiter
+ 5.11.4
+ test
org.mockito
mockito-core
- 4.8.0
+ 5.15.2
test
org.mockito
mockito-junit-jupiter
- 4.8.0
+ 5.15.2
test
org.assertj
assertj-core
- 3.23.1
+ (3.27.7,)
test
org.apache.commons
commons-lang3
- 3.12.0
+ [3.18.0,)
test
com.h2database
h2
- 2.1.214
+ [2.3.232,)
test
org.hamcrest
- hamcrest-all
- 1.3
+ hamcrest
+ 2.2
+ test
+
+
+
+
+ org.openjdk.jmh
+ jmh-core
+ 1.37
test
+
+
+
+ org.openjdk.jmh
+ jmh-generator-annprocess
+ 1.37
+ provided
+
+
@@ -95,11 +151,13 @@
sonatype-nexus-staging
- https://oss.sonatype.org/service/local/staging/deploy/maven2/
+ https://central.sonatype.com/repository/maven-releases
sonatype-nexus-snapshots
- https://oss.sonatype.org/content/repositories/snapshots/
+ https://central.sonatype.com/repository/maven-snapshots/
+ false
+ true
@@ -107,7 +165,7 @@
scm:git:https://github.com/JSQLParser/JSqlParser.git
scm:git:ssh://git@github.com:JSQLParser/JSqlParser.git
https://github.com/JSQLParser/JSqlParser.git
- jsqlparser-4.8
+ HEAD
@@ -121,7 +179,7 @@
org.codehaus.mojo
exec-maven-plugin
- 3.1.0
+ 3.5.0
net.sf.jsqlparser.parser.ParserKeywordsUtils
@@ -133,8 +191,9 @@
org.apache.maven.plugins
maven-pmd-plugin
- 3.19.0
+ 3.26.0
+ 2
${project.basedir}/config/pmd/ruleset.xml
@@ -142,6 +201,7 @@
**/*Bean.java
**/generated/*.java
+ **/net/sf/jsqlparser/parser/SimpleCharStream.java
target/generated-sources
@@ -169,22 +229,12 @@
pmd-java
${pmdVersion}
-
- net.sourceforge.pmd
- pmd-javascript
- ${pmdVersion}
-
-
- net.sourceforge.pmd
- pmd-jsp
- ${pmdVersion}
-
org.codehaus.mojo
build-helper-maven-plugin
- 3.2.0
+ 3.6.0
add-source
@@ -203,20 +253,31 @@
maven-compiler-plugin
- 3.10.1
+ 3.14.0
- 1.8
- 1.8
+ 11
+ 11
true
${project.build.sourceEncoding}
true
+ 2000m
+
+ -J-Xss4M
+
+ true
+
+
+ org.openjdk.jmh
+ jmh-generator-annprocess
+ 1.37
+
+
org.javacc.plugin
javacc-maven-plugin
- 3.0.3
-
+ 3.8.0
javacc
@@ -224,26 +285,31 @@
jjtree-javacc
+
+
+ -CODE_GENERATOR="Java"
+ -GRAMMAR_ENCODING="UTF-8"
+
+
+ -GRAMMAR_ENCODING="UTF-8"
+ -CODE_GENERATOR="Java"
+
+
- net.java.dev.javacc
- javacc
- 7.0.13
+ org.javacc.generator
+ java
+ 8.1.0-SNAPSHOT
+
+
+ org.javacc
+ core
+ 8.1.0-SNAPSHOT
-
- org.apache.maven.plugins
- maven-eclipse-plugin
- 2.9
-
-
- /target/generated-sources/javacc
-
-
-
org.apache.maven.plugins
maven-resources-plugin
@@ -279,27 +345,23 @@
org.apache.maven.plugins
maven-release-plugin
- 3.0.0-M7
+
+ 3.1.1
true
false
forked-path
sign-release-artifacts
-
org.apache.maven.plugins
maven-source-plugin
- 3.2.1
+ 3.3.1
attach-sources
@@ -312,7 +374,7 @@
org.apache.maven.plugins
maven-javadoc-plugin
- 3.4.1
+ 3.11.2
attach-javadocs
@@ -320,6 +382,10 @@
${javadoc.opts}
net.sf.jsqlparser.parser
none
+ true
+ 2g
+ 800m
+ -J-Xss4m
jar
@@ -328,38 +394,15 @@
- 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
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.4.2
- xml
-
-
- net/sf/jsqlparser/parser/*.class
- net/sf/jsqlparser/JSQLParserException.class
-
-
-
+
+
+ net.sf.jsqlparser
+
+
@@ -371,15 +414,22 @@
org.apache.maven.plugins
maven-surefire-plugin
- 3.2.3
+ 3.5.2
false
+
+ false
+
+ --add-opens=java.base/java.lang=ALL-UNNAMED
+ --add-opens=java.base/java.util=ALL-UNNAMED
+ -Xmx2G -Xms800m -Xss4m
+
org.jacoco
jacoco-maven-plugin
- 0.8.10
+ 0.8.13
@@ -398,7 +448,7 @@
com.diffplug.spotless
spotless-maven-plugin
- 2.28.0
+ 2.44.4
origin/master
@@ -439,28 +489,40 @@
+
+ org.sonatype.central
+ central-publishing-maven-plugin
+ 0.10.0
+ true
+
+ sonatype-nexus
+
+
-
+
org.apache.maven.plugins
maven-surefire-report-plugin
- 3.0.0-M7
+ 3.5.2
${project.reporting.outputDirectory}/testresults
+ -Xmx2G -Xms800m -Xss4m
org.apache.maven.plugins
maven-javadoc-plugin
- 3.4.1
+ 3.11.2
true
- 800m
none
-
+ 2g
+ 800m
+ -J-Xss2m
+
`'ü'`
+ 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/HighExpression.java b/src/main/java/net/sf/jsqlparser/expression/HighExpression.java
new file mode 100644
index 000000000..4d3a9cd63
--- /dev/null
+++ b/src/main/java/net/sf/jsqlparser/expression/HighExpression.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;
+
+import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
+
+public class HighExpression extends ASTNodeAccessImpl implements Expression {
+ private Expression expression;
+
+ public HighExpression() {
+ // empty constructor
+ }
+
+ public HighExpression(Expression expression) {
+ this.expression = expression;
+ }
+
+ @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;
+ }
+
+ @Override
+ public String toString() {
+ return "HIGH " + expression.toString();
+ }
+}
diff --git a/src/main/java/net/sf/jsqlparser/expression/IntervalExpression.java b/src/main/java/net/sf/jsqlparser/expression/IntervalExpression.java
index 26584c63d..9c028c769 100644
--- a/src/main/java/net/sf/jsqlparser/expression/IntervalExpression.java
+++ b/src/main/java/net/sf/jsqlparser/expression/IntervalExpression.java
@@ -15,9 +15,9 @@
public class IntervalExpression extends ASTNodeAccessImpl implements Expression {
+ private final boolean intervalKeyword;
private String parameter = null;
private String intervalType = null;
- private final boolean intervalKeyword;
private Expression expression = null;
public IntervalExpression() {
@@ -28,6 +28,13 @@ 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;
}
@@ -64,8 +71,8 @@ public String toString() {
}
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
public IntervalExpression withParameter(String parameter) {
diff --git a/src/main/java/net/sf/jsqlparser/expression/Inverse.java b/src/main/java/net/sf/jsqlparser/expression/Inverse.java
new file mode 100644
index 000000000..b50d8be8f
--- /dev/null
+++ b/src/main/java/net/sf/jsqlparser/expression/Inverse.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;
+
+import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
+
+public class Inverse extends ASTNodeAccessImpl implements Expression {
+ private Expression expression;
+
+ public Inverse() {
+ // empty constructor
+ }
+
+ public Inverse(Expression expression) {
+ this.expression = expression;
+ }
+
+ @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;
+ }
+
+ @Override
+ public String toString() {
+ return "INVERSE (" + expression.toString() + ")";
+ }
+}
diff --git a/src/main/java/net/sf/jsqlparser/expression/JdbcNamedParameter.java b/src/main/java/net/sf/jsqlparser/expression/JdbcNamedParameter.java
index 9e8d83792..84aa0b34e 100644
--- a/src/main/java/net/sf/jsqlparser/expression/JdbcNamedParameter.java
+++ b/src/main/java/net/sf/jsqlparser/expression/JdbcNamedParameter.java
@@ -39,8 +39,8 @@ public void setName(String name) {
}
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
@Override
diff --git a/src/main/java/net/sf/jsqlparser/expression/JdbcParameter.java b/src/main/java/net/sf/jsqlparser/expression/JdbcParameter.java
index a03d230ff..f512c47fc 100644
--- a/src/main/java/net/sf/jsqlparser/expression/JdbcParameter.java
+++ b/src/main/java/net/sf/jsqlparser/expression/JdbcParameter.java
@@ -11,20 +11,43 @@
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() {}
- public JdbcParameter(Integer index, boolean useFixedIndex) {
+ 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() {
@@ -44,13 +67,13 @@ public void setUseFixedIndex(boolean useFixedIndex) {
}
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
@Override
public String toString() {
- return useFixedIndex ? "?" + index : "?";
+ return useFixedIndex ? parameterCharacter + index : parameterCharacter;
}
public JdbcParameter withIndex(Integer index) {
diff --git a/src/main/java/net/sf/jsqlparser/expression/JsonAggregateFunction.java b/src/main/java/net/sf/jsqlparser/expression/JsonAggregateFunction.java
index c49086106..20bfa272e 100644
--- a/src/main/java/net/sf/jsqlparser/expression/JsonAggregateFunction.java
+++ b/src/main/java/net/sf/jsqlparser/expression/JsonAggregateFunction.java
@@ -11,29 +11,27 @@
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 final OrderByClause expressionOrderBy = new OrderByClause();
-
private boolean usingKeyKeyword = false;
- private String key;
+ private Object key;
private boolean usingValueKeyword = false;
private Object value;
-
+
private boolean usingFormatJson = false;
-
+
private JsonAggregateOnNullType onNullType;
private JsonAggregateUniqueKeysType uniqueKeysType;
-
+
public JsonAggregateOnNullType getOnNullType() {
return onNullType;
@@ -42,7 +40,7 @@ public JsonAggregateOnNullType getOnNullType() {
public void setOnNullType(JsonAggregateOnNullType onNullType) {
this.onNullType = onNullType;
}
-
+
public JsonAggregateFunction withOnNullType(JsonAggregateOnNullType onNullType) {
this.setOnNullType(onNullType);
return this;
@@ -55,7 +53,7 @@ public JsonAggregateUniqueKeysType getUniqueKeysType() {
public void setUniqueKeysType(JsonAggregateUniqueKeysType uniqueKeysType) {
this.uniqueKeysType = uniqueKeysType;
}
-
+
public JsonAggregateFunction withUniqueKeysType(JsonAggregateUniqueKeysType uniqueKeysType) {
this.setUniqueKeysType(uniqueKeysType);
return this;
@@ -64,21 +62,25 @@ public JsonAggregateFunction withUniqueKeysType(JsonAggregateUniqueKeysType uniq
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");
+ 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 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(String typeName) {
this.setType(typeName);
return this;
@@ -91,7 +93,7 @@ public Expression getExpression() {
public void setExpression(Expression expression) {
this.expression = expression;
}
-
+
public JsonAggregateFunction withExpression(Expression expression) {
this.setExpression(expression);
return this;
@@ -104,21 +106,21 @@ public boolean isUsingKeyKeyword() {
public void setUsingKeyKeyword(boolean usingKeyKeyword) {
this.usingKeyKeyword = usingKeyKeyword;
}
-
+
public JsonAggregateFunction withUsingKeyKeyword(boolean usingKeyKeyword) {
this.setUsingKeyKeyword(usingKeyKeyword);
return this;
}
- public String getKey() {
+ public Object getKey() {
return key;
}
- public void setKey(String key) {
+ public void setKey(Object key) {
this.key = key;
}
-
- public JsonAggregateFunction withKey(String key) {
+
+ public JsonAggregateFunction withKey(Object key) {
this.setKey(key);
return this;
}
@@ -130,7 +132,7 @@ public boolean isUsingValueKeyword() {
public void setUsingValueKeyword(boolean usingValueKeyword) {
this.usingValueKeyword = usingValueKeyword;
}
-
+
public JsonAggregateFunction withUsingValueKeyword(boolean usingValueKeyword) {
this.setUsingValueKeyword(usingValueKeyword);
return this;
@@ -143,12 +145,12 @@ public Object getValue() {
public void setValue(Object value) {
this.value = value;
}
-
+
public JsonAggregateFunction withValue(Object value) {
this.setValue(value);
return this;
}
-
+
public boolean isUsingFormatJson() {
return usingFormatJson;
}
@@ -156,12 +158,12 @@ public boolean isUsingFormatJson() {
public void setUsingFormatJson(boolean usingFormatJson) {
this.usingFormatJson = usingFormatJson;
}
-
+
public JsonAggregateFunction withUsingFormatJson(boolean usingFormatJson) {
this.setUsingFormatJson(usingFormatJson);
return this;
}
-
+
public List getExpressionOrderByElements() {
return expressionOrderBy.getOrderByElements();
}
@@ -169,22 +171,24 @@ public List getExpressionOrderByElements() {
public void setExpressionOrderByElements(List orderByElements) {
expressionOrderBy.setOrderByElements(orderByElements);
}
-
- public JsonAggregateFunction withExpressionOrderByElements(List orderByElements) {
+
+ public JsonAggregateFunction withExpressionOrderByElements(
+ List orderByElements) {
this.setExpressionOrderByElements(orderByElements);
return this;
}
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ 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:
+ case MYSQL_OBJECT:
appendObject(builder);
break;
case ARRAY:
@@ -192,11 +196,12 @@ public StringBuilder append(StringBuilder builder) {
break;
default:
// this should never happen really
- throw new UnsupportedOperationException("JSON Aggregate Function of the type " + functionType.name() + " has not been implemented yet.");
+ 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( ");
@@ -205,16 +210,18 @@ public StringBuilder appendObject(StringBuilder builder) {
builder.append("KEY ");
}
builder.append(key).append(" VALUE ").append(value);
+ } else if (functionType == JsonFunctionType.MYSQL_OBJECT) {
+ builder.append(key).append(", ").append(value);
} else {
builder.append(key).append(":").append(value);
}
-
+
if (usingFormatJson) {
builder.append(" FORMAT JSON");
}
-
- if (onNullType!=null) {
- switch(onNullType) {
+
+ if (onNullType != null) {
+ switch (onNullType) {
case NULL:
builder.append(" NULL ON NULL");
break;
@@ -225,9 +232,9 @@ public StringBuilder appendObject(StringBuilder builder) {
// this should never happen
}
}
-
- if (uniqueKeysType!=null) {
- switch(uniqueKeysType) {
+
+ if (uniqueKeysType != null) {
+ switch (uniqueKeysType) {
case WITH:
builder.append(" WITH UNIQUE KEYS");
break;
@@ -238,29 +245,29 @@ public StringBuilder appendObject(StringBuilder builder) {
// 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) {
+
+ if (onNullType != null) {
+ switch (onNullType) {
case NULL:
builder.append(" NULL ON NULL ");
break;
@@ -272,17 +279,17 @@ public StringBuilder appendArray(StringBuilder builder) {
}
}
builder.append(") ");
-
-
+
+
// FILTER( WHERE expression ) OVER windowNameOrSpecification
super.append(builder);
-
+
return builder;
}
@Override
public String toString() {
- StringBuilder builder = new StringBuilder();
- return append(builder).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
index d9e2b4c51..898dad7c0 100644
--- a/src/main/java/net/sf/jsqlparser/expression/JsonAggregateOnNullType.java
+++ b/src/main/java/net/sf/jsqlparser/expression/JsonAggregateOnNullType.java
@@ -26,7 +26,6 @@
package net.sf.jsqlparser.expression;
/**
- *
* @author Andreas Reichel
*/
public enum JsonAggregateOnNullType {
diff --git a/src/main/java/net/sf/jsqlparser/expression/JsonAggregateUniqueKeysType.java b/src/main/java/net/sf/jsqlparser/expression/JsonAggregateUniqueKeysType.java
index aa1370595..097aad552 100644
--- a/src/main/java/net/sf/jsqlparser/expression/JsonAggregateUniqueKeysType.java
+++ b/src/main/java/net/sf/jsqlparser/expression/JsonAggregateUniqueKeysType.java
@@ -26,7 +26,6 @@
package net.sf.jsqlparser.expression;
/**
- *
* @author Andreas Reichel
*/
public enum JsonAggregateUniqueKeysType {
diff --git a/src/main/java/net/sf/jsqlparser/expression/JsonExpression.java b/src/main/java/net/sf/jsqlparser/expression/JsonExpression.java
index 9fe2811cf..f258e855c 100644
--- a/src/main/java/net/sf/jsqlparser/expression/JsonExpression.java
+++ b/src/main/java/net/sf/jsqlparser/expression/JsonExpression.java
@@ -18,10 +18,9 @@
import java.util.Map;
public class JsonExpression extends ASTNodeAccessImpl implements Expression {
+ private final List> idents = new ArrayList<>();
private Expression expr;
- private final List> idents = new ArrayList<>();
-
public JsonExpression() {
}
@@ -30,14 +29,14 @@ public JsonExpression(Expression expr) {
this.expr = expr;
}
- public JsonExpression(Expression expr, List> idents) {
+ public JsonExpression(Expression expr, List> idents) {
this.expr = expr;
this.idents.addAll(idents);
}
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
public Expression getExpression() {
@@ -48,26 +47,26 @@ public void setExpression(Expression expr) {
this.expr = expr;
}
- public void addIdent(String ident, String operator) {
+ public void addIdent(Expression ident, String operator) {
idents.add(new AbstractMap.SimpleEntry<>(ident, operator));
}
- public void addAllIdents(Collection> idents) {
+ public void addAllIdents(Collection> idents) {
this.idents.addAll(idents);
}
- public List> getIdentList() {
+ public List> getIdentList() {
return idents;
}
- public Map.Entry getIdent(int index) {
+ public Map.Entry getIdent(int index) {
return idents.get(index);
}
@Deprecated
- public List getIdents() {
- ArrayList l = new ArrayList<>();
- for (Map.Entry ident : idents) {
+ public List getIdents() {
+ ArrayList l = new ArrayList<>();
+ for (Map.Entry ident : idents) {
l.add(ident.getKey());
}
@@ -77,7 +76,7 @@ public List getIdents() {
@Deprecated
public List getOperators() {
ArrayList l = new ArrayList<>();
- for (Map.Entry ident : idents) {
+ for (Map.Entry ident : idents) {
l.add(ident.getValue());
}
return l;
@@ -87,7 +86,7 @@ public List getOperators() {
public String toString() {
StringBuilder b = new StringBuilder();
b.append(expr.toString());
- for (Map.Entry ident : idents) {
+ for (Map.Entry ident : idents) {
b.append(ident.getValue()).append(ident.getKey());
}
return b.toString();
diff --git a/src/main/java/net/sf/jsqlparser/expression/JsonFunction.java b/src/main/java/net/sf/jsqlparser/expression/JsonFunction.java
index 9d09b9711..3cc409873 100644
--- a/src/main/java/net/sf/jsqlparser/expression/JsonFunction.java
+++ b/src/main/java/net/sf/jsqlparser/expression/JsonFunction.java
@@ -11,251 +11,598 @@
import java.util.ArrayList;
import java.util.Objects;
+
import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
+import net.sf.jsqlparser.statement.create.table.ColDataType;
/**
+ * Represents a JSON-Function.
+ * Currently supported are the types in {@link JsonFunctionType}.
+ *
+ * For JSON_OBJECT the parameters are available from {@link #getKeyValuePairs()}
+ *
+ * For JSON_ARRAY the parameters are availble from {@link #getExpressions()}.
*
* @author Andreas Reichel
*/
-
public class JsonFunction extends ASTNodeAccessImpl implements Expression {
- private JsonFunctionType functionType;
- private final ArrayList keyValuePairs = new ArrayList<>();
- private final ArrayList expressions = new ArrayList<>();
- 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 JsonFunction withType(JsonFunctionType type) {
- this.setType(type);
- return this;
- }
-
- 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(String typeName) {
- this.setType(typeName);
- return this;
- }
-
- @Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
- }
-
- // 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 ");
+ public enum JsonOnResponseBehaviorType {
+ ERROR, NULL, DEFAULT, EMPTY, EMPTY_ARRAY, EMPTY_OBJECT, TRUE, FALSE, UNKNOWN
+ }
+
+ public enum JsonWrapperType {
+ WITHOUT, WITH
+ }
+
+ public enum JsonWrapperMode {
+ CONDITIONAL, UNCONDITIONAL
+ }
+
+ public enum JsonQuotesType {
+ KEEP, OMIT
+ }
+
+ public enum ScalarsType {
+ ALLOW, DISALLOW
+ }
+
+ public static class JsonOnResponseBehavior {
+ private JsonOnResponseBehaviorType type;
+ private Expression expression;
+
+ public JsonOnResponseBehavior(JsonOnResponseBehaviorType type) {
+ this(type, null);
+ }
+
+ public JsonOnResponseBehavior(JsonOnResponseBehaviorType type, Expression expression) {
+ this.type = type;
+ this.expression = expression;
+ }
+
+ public JsonOnResponseBehaviorType getType() {
+ return type;
+ }
+
+ public void setType(JsonOnResponseBehaviorType type) {
+ this.type = type;
+ }
+
+ public Expression getExpression() {
+ return expression;
+ }
+
+ public void setExpression(Expression expression) {
+ this.expression = expression;
+ }
+
+ public StringBuilder append(StringBuilder builder) {
+ switch (type) {
+ case ERROR:
+ builder.append("ERROR");
+ break;
+ case NULL:
+ builder.append("NULL");
+ break;
+ case DEFAULT:
+ builder.append("DEFAULT ").append(expression);
+ break;
+ case EMPTY:
+ builder.append("EMPTY ");
+ break;
+ case EMPTY_ARRAY:
+ builder.append("EMPTY ARRAY");
+ break;
+ case EMPTY_OBJECT:
+ builder.append("EMPTY OBJECT");
+ break;
+ case TRUE:
+ builder.append("TRUE");
+ break;
+ case FALSE:
+ builder.append("FALSE");
+ break;
+ case UNKNOWN:
+ builder.append("UNKNOWN");
+ break;
+ default:
+ throw new IllegalStateException("Unhandled JsonOnResponseBehavior: " + type);
+ // this should never happen
+ }
+ return builder;
}
- 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();
- }
+
+ @Override
+ public String toString() {
+ return append(new StringBuilder()).toString();
+ }
+ }
+
+ private final ArrayList keyValuePairs = new ArrayList<>();
+ private final ArrayList expressions = new ArrayList<>();
+ private final ArrayList passingExpressions = new ArrayList<>();
+ private final ArrayList additionalQueryPathArguments = new ArrayList<>();
+ private JsonFunctionType functionType;
+ private JsonAggregateOnNullType onNullType;
+ private JsonAggregateUniqueKeysType uniqueKeysType;
+
+ private boolean isStrict = false;
+ private JsonFunctionExpression inputExpression;
+ private Expression jsonPathExpression;
+ private ColDataType returningType;
+ private boolean returningFormatJson;
+ private String returningEncoding;
+ private JsonOnResponseBehavior onEmptyBehavior;
+ private JsonOnResponseBehavior onErrorBehavior;
+ private JsonWrapperType wrapperType;
+ private JsonWrapperMode wrapperMode;
+ private boolean wrapperArray;
+ private JsonQuotesType quotesType;
+ private boolean quotesOnScalarString;
+ private ScalarsType scalarsType;
+
+ public JsonFunction() {}
+
+ public JsonFunction(JsonFunctionType functionType) {
+ this.functionType = functionType;
+ }
+
+ /**
+ * Returns the Parameters of an JSON_OBJECT
+ * The KeyValuePairs may not have both key and value set, in some cases only the Key is set.
+ *
+ * @see net.sf.jsqlparser.parser.feature.Feature#allowCommaAsKeyValueSeparator
+ *
+ * @return A List of KeyValuePairs, never NULL
+ */
+ public ArrayList getKeyValuePairs() {
+ return keyValuePairs;
+ }
+
+ /**
+ * Returns the parameters of JSON_ARRAY
+ *
+ * @return A List of {@link JsonFunctionExpression}s, never NULL
+ */
+ 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 ArrayList getPassingExpressions() {
+ return passingExpressions;
+ }
+
+ public boolean addPassingExpression(Expression expression) {
+ return passingExpressions.add(expression);
+ }
+
+ public ArrayList getAdditionalQueryPathArguments() {
+ return additionalQueryPathArguments;
+ }
+
+ public boolean addAdditionalQueryPathArgument(String argument) {
+ return additionalQueryPathArguments.add(argument);
+ }
+
+ public JsonFunctionExpression getInputExpression() {
+ return inputExpression;
+ }
+
+ public void setInputExpression(JsonFunctionExpression inputExpression) {
+ this.inputExpression = inputExpression;
+ }
+
+ public Expression getJsonPathExpression() {
+ return jsonPathExpression;
+ }
+
+ public void setJsonPathExpression(Expression jsonPathExpression) {
+ this.jsonPathExpression = jsonPathExpression;
+ }
+
+ public ColDataType getReturningType() {
+ return returningType;
+ }
+
+ public void setReturningType(ColDataType returningType) {
+ this.returningType = returningType;
+ }
+
+ public boolean isReturningFormatJson() {
+ return returningFormatJson;
+ }
+
+ public void setReturningFormatJson(boolean returningFormatJson) {
+ this.returningFormatJson = returningFormatJson;
+ }
+
+ public String getReturningEncoding() {
+ return returningEncoding;
+ }
+
+ public void setReturningEncoding(String returningEncoding) {
+ this.returningEncoding = returningEncoding;
+ }
+
+ public JsonOnResponseBehavior getOnEmptyBehavior() {
+ return onEmptyBehavior;
+ }
+
+ public void setOnEmptyBehavior(JsonOnResponseBehavior onEmptyBehavior) {
+ this.onEmptyBehavior = onEmptyBehavior;
+ }
+
+ public JsonOnResponseBehavior getOnErrorBehavior() {
+ return onErrorBehavior;
+ }
+
+ public void setOnErrorBehavior(JsonOnResponseBehavior onErrorBehavior) {
+ this.onErrorBehavior = onErrorBehavior;
+ }
+
+ public JsonWrapperType getWrapperType() {
+ return wrapperType;
+ }
+
+ public void setWrapperType(JsonWrapperType wrapperType) {
+ this.wrapperType = wrapperType;
+ }
+
+ public JsonWrapperMode getWrapperMode() {
+ return wrapperMode;
+ }
+
+ public void setWrapperMode(JsonWrapperMode wrapperMode) {
+ this.wrapperMode = wrapperMode;
+ }
+
+ public boolean isWrapperArray() {
+ return wrapperArray;
+ }
+
+ public void setWrapperArray(boolean wrapperArray) {
+ this.wrapperArray = wrapperArray;
+ }
+
+ public JsonQuotesType getQuotesType() {
+ return quotesType;
+ }
+
+ public void setQuotesType(JsonQuotesType quotesType) {
+ this.quotesType = quotesType;
+ }
+
+ public boolean isQuotesOnScalarString() {
+ return quotesOnScalarString;
+ }
+
+ public void setQuotesOnScalarString(boolean quotesOnScalarString) {
+ this.quotesOnScalarString = quotesOnScalarString;
+ }
+
+ public ScalarsType getScalarsType() {
+ return scalarsType;
+ }
+
+ public void setScalarsType(ScalarsType type) {
+ this.scalarsType = type;
+ }
+
+ 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;
+ }
+
+ public boolean isStrict() {
+ return isStrict;
+ }
+
+ public void setStrict(boolean strict) {
+ isStrict = strict;
+ }
+
+ public JsonFunction withStrict(boolean strict) {
+ this.setStrict(strict);
+ 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:
+ case POSTGRES_OBJECT:
+ case MYSQL_OBJECT:
+ appendObject(builder);
+ break;
+ case ARRAY:
+ appendArray(builder);
+ break;
+ case VALUE:
+ appendValue(builder);
+ break;
+ case QUERY:
+ appendQuery(builder);
+ break;
+ case EXISTS:
+ appendExists(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(", ");
+ }
+ keyValuePair.append(builder);
+ i++;
+ }
+
+ appendOnNullType(builder);
+ if (isStrict) {
+ builder.append(" STRICT");
+ }
+ appendUniqueKeys(builder);
+ appendReturningClause(builder, true);
+
+ builder.append(" ) ");
+
+ return builder;
+ }
+
+ private void appendOnNullType(StringBuilder builder) {
+ 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
+ }
+ }
+ }
+
+ private void appendUniqueKeys(StringBuilder builder) {
+ 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
+ }
+ }
+ }
+
+ @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++;
+ }
+
+ appendOnNullType(builder);
+ appendReturningClause(builder, true);
+ builder.append(") ");
+
+ return builder;
+ }
+
+ @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"})
+ public StringBuilder appendValue(StringBuilder builder) {
+ builder.append("JSON_VALUE(");
+ appendValueOrQueryPrefix(builder);
+
+ if (returningType != null) {
+ builder.append(" RETURNING ").append(returningType);
+ }
+
+ appendOnResponseClause(builder, onEmptyBehavior, "EMPTY");
+ appendOnResponseClause(builder, onErrorBehavior, "ERROR");
+
+ builder.append(")");
+ return builder;
+ }
+
+ @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"})
+ public StringBuilder appendQuery(StringBuilder builder) {
+ builder.append("JSON_QUERY(");
+ appendValueOrQueryPrefix(builder);
+
+ appendReturningClause(builder, true);
+
+ appendWrapperClause(builder);
+ appendQuotesClause(builder);
+ appendOnResponseClause(builder, onEmptyBehavior, "EMPTY");
+ appendOnResponseClause(builder, onErrorBehavior, "ERROR");
+
+ for (String additionalQueryPathArgument : additionalQueryPathArguments) {
+ builder.append(", ").append(additionalQueryPathArgument);
+ }
+
+ builder.append(")");
+ return builder;
+ }
+
+ @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"})
+ public StringBuilder appendExists(StringBuilder builder) {
+ builder.append("JSON_EXISTS(");
+ appendValueOrQueryPrefix(builder);
+ appendOnResponseClause(builder, onErrorBehavior, "ERROR");
+ builder.append(")");
+ return builder;
+ }
+
+ private void appendValueOrQueryPrefix(StringBuilder builder) {
+ if (inputExpression != null) {
+ inputExpression.append(builder);
+ }
+
+ if (jsonPathExpression != null) {
+ if (inputExpression != null) {
+ builder.append(", ");
+ }
+ builder.append(jsonPathExpression);
+ }
+
+ if (!passingExpressions.isEmpty()) {
+ builder.append(" PASSING ");
+ boolean comma = false;
+ for (Expression passingExpression : passingExpressions) {
+ if (comma) {
+ builder.append(", ");
+ } else {
+ comma = true;
+ }
+ builder.append(passingExpression);
+ }
+ }
+ }
+
+ private void appendOnResponseClause(StringBuilder builder, JsonOnResponseBehavior behavior,
+ String clause) {
+ if (behavior != null) {
+ builder.append(" ");
+ behavior.append(builder);
+ builder.append(" ON ").append(clause);
+ }
+ }
+
+ private void appendReturningClause(StringBuilder builder, boolean formatJsonAllowed) {
+ if (returningType != null) {
+ builder.append(" RETURNING ").append(returningType);
+ if (formatJsonAllowed && returningFormatJson) {
+ builder.append(" FORMAT JSON");
+ if (returningEncoding != null) {
+ builder.append(" ENCODING ").append(returningEncoding);
+ }
+ }
+ }
+ }
+
+ private void appendWrapperClause(StringBuilder builder) {
+ if (wrapperType != null) {
+ builder.append(" ").append(wrapperType);
+ if (wrapperMode != null) {
+ builder.append(" ").append(wrapperMode);
+ }
+ if (wrapperArray) {
+ builder.append(" ARRAY");
+ }
+ builder.append(" WRAPPER");
+ }
+ }
+
+ private void appendQuotesClause(StringBuilder builder) {
+ if (quotesType != null) {
+ builder.append(" ").append(quotesType).append(" QUOTES");
+ if (quotesOnScalarString) {
+ builder.append(" ON SCALAR STRING");
+ }
+ }
+ }
+
+ @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
index e23aa2751..738c09fc2 100644
--- a/src/main/java/net/sf/jsqlparser/expression/JsonFunctionExpression.java
+++ b/src/main/java/net/sf/jsqlparser/expression/JsonFunctionExpression.java
@@ -14,7 +14,6 @@
import java.util.Objects;
/**
- *
* @author Andreas Reichel
*/
@@ -22,10 +21,12 @@ public class JsonFunctionExpression implements Serializable {
private final Expression expression;
private boolean usingFormatJson = false;
+ private String encoding;
public JsonFunctionExpression(Expression expression) {
this.expression = Objects.requireNonNull(expression, "The EXPRESSION must not be null");
}
+
public Expression getExpression() {
return expression;
}
@@ -37,14 +38,34 @@ public boolean isUsingFormatJson() {
public void setUsingFormatJson(boolean usingFormatJson) {
this.usingFormatJson = usingFormatJson;
}
-
+
public JsonFunctionExpression withUsingFormatJson(boolean usingFormatJson) {
this.setUsingFormatJson(usingFormatJson);
return this;
}
-
+
+ public String getEncoding() {
+ return encoding;
+ }
+
+ public void setEncoding(String encoding) {
+ this.encoding = encoding;
+ }
+
+ public JsonFunctionExpression withEncoding(String encoding) {
+ this.setEncoding(encoding);
+ return this;
+ }
+
public StringBuilder append(StringBuilder builder) {
- return builder.append(getExpression()).append(isUsingFormatJson() ? " FORMAT JSON" : "");
+ builder.append(getExpression());
+ if (isUsingFormatJson()) {
+ builder.append(" FORMAT JSON");
+ if (encoding != null) {
+ builder.append(" ENCODING ").append(encoding);
+ }
+ }
+ return builder;
}
@Override
diff --git a/src/main/java/net/sf/jsqlparser/expression/JsonFunctionType.java b/src/main/java/net/sf/jsqlparser/expression/JsonFunctionType.java
index 4502b3ca6..ebd497e79 100644
--- a/src/main/java/net/sf/jsqlparser/expression/JsonFunctionType.java
+++ b/src/main/java/net/sf/jsqlparser/expression/JsonFunctionType.java
@@ -11,11 +11,22 @@
package net.sf.jsqlparser.expression;
/**
- *
* @author Andreas Reichel
*/
public enum JsonFunctionType {
- OBJECT, ARRAY, POSTGRES_OBJECT, MYSQL_OBJECT;
+ OBJECT, ARRAY, VALUE, QUERY, EXISTS,
+
+ /**
+ * Not used anymore
+ */
+ @Deprecated
+ POSTGRES_OBJECT,
+
+ /**
+ * Not used anymore
+ */
+ @Deprecated
+ 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
index b85274caa..18fb4752d 100644
--- a/src/main/java/net/sf/jsqlparser/expression/JsonKeyValuePair.java
+++ b/src/main/java/net/sf/jsqlparser/expression/JsonKeyValuePair.java
@@ -14,114 +14,168 @@
import java.util.Objects;
/**
- *
* @author Andreas Reichel
*/
public class JsonKeyValuePair implements Serializable {
- private final String key;
- private boolean usingKeyKeyword = false;
- private final Object value;
- 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();
- }
+ private final Object key;
+ private final Object value;
+ private boolean usingKeyKeyword;
+ private JsonKeyValuePairSeparator separator;
+ private boolean usingFormatJson = false;
+ private String encoding;
+
+ /**
+ * Please use the Constructor with {@link JsonKeyValuePairSeparator} parameter.
+ */
+ @Deprecated
+ public JsonKeyValuePair(Object key, Object value, boolean usingKeyKeyword,
+ boolean usingValueKeyword) {
+ this(key, value, usingKeyKeyword, usingValueKeyword ? JsonKeyValuePairSeparator.VALUE
+ : JsonKeyValuePairSeparator.COLON);
+ }
+
+ public JsonKeyValuePair(Object key, Object value, boolean usingKeyKeyword,
+ JsonKeyValuePairSeparator separator) {
+ this.key = Objects.requireNonNull(key, "The KEY of the Pair must not be null");
+ this.value = value;
+ this.usingKeyKeyword = usingKeyKeyword;
+ this.separator =
+ Objects.requireNonNull(separator, "The KeyValuePairSeparator must not be NULL");
+ }
+
+ public boolean isUsingKeyKeyword() {
+ return usingKeyKeyword;
+ }
+
+ public void setUsingKeyKeyword(boolean usingKeyKeyword) {
+ this.usingKeyKeyword = usingKeyKeyword;
+ }
+
+ public JsonKeyValuePair withUsingKeyKeyword(boolean usingKeyKeyword) {
+ this.setUsingKeyKeyword(usingKeyKeyword);
+ return this;
+ }
+
+ /**
+ * Use {@link #getSeparator()}
+ */
+ @Deprecated
+ public boolean isUsingValueKeyword() {
+ return separator == JsonKeyValuePairSeparator.VALUE;
+ }
+
+ /**
+ * Use {@link #setSeparator(JsonKeyValuePairSeparator)}
+ */
+ @Deprecated
+ public void setUsingValueKeyword(boolean usingValueKeyword) {
+ separator = usingValueKeyword ? JsonKeyValuePairSeparator.VALUE
+ : JsonKeyValuePairSeparator.COLON;
+ }
+
+ /**
+ * Use {@link #withSeparator(JsonKeyValuePairSeparator)}
+ */
+ @Deprecated
+ public JsonKeyValuePair withUsingValueKeyword(boolean usingValueKeyword) {
+ this.setUsingValueKeyword(usingValueKeyword);
+ return this;
+ }
+
+ public JsonKeyValuePairSeparator getSeparator() {
+ return separator;
+ }
+
+ public void setSeparator(JsonKeyValuePairSeparator separator) {
+ this.separator = separator;
+ }
+
+ public JsonKeyValuePair withSeparator(JsonKeyValuePairSeparator separator) {
+ this.setSeparator(separator);
+ 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;
+ }
+
+ public String getEncoding() {
+ return encoding;
+ }
+
+ public void setEncoding(String encoding) {
+ this.encoding = encoding;
+ }
+
+ public JsonKeyValuePair withEncoding(String encoding) {
+ this.setEncoding(encoding);
+ 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 Object getKey() {
+ return key;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public StringBuilder append(StringBuilder builder) {
+ if (isUsingKeyKeyword() && getSeparator() == JsonKeyValuePairSeparator.VALUE) {
+ builder.append("KEY ");
+ }
+ builder.append(getKey());
+
+ if (getValue() != null) {
+ builder.append(getSeparator().getSeparatorString());
+ builder.append(getValue());
+ }
+
+ if (isUsingFormatJson()) {
+ builder.append(" FORMAT JSON");
+ if (encoding != null) {
+ builder.append(" ENCODING ").append(encoding);
+ }
+ }
+
+ return builder;
+ }
+
+ @Override
+ public String toString() {
+ return append(new StringBuilder()).toString();
+ }
}
diff --git a/src/main/java/net/sf/jsqlparser/expression/JsonKeyValuePairSeparator.java b/src/main/java/net/sf/jsqlparser/expression/JsonKeyValuePairSeparator.java
new file mode 100644
index 000000000..e4e998aa5
--- /dev/null
+++ b/src/main/java/net/sf/jsqlparser/expression/JsonKeyValuePairSeparator.java
@@ -0,0 +1,33 @@
+/*-
+ * #%L
+ * JSQLParser library
+ * %%
+ * Copyright (C) 2004 - 2025 JSQLParser
+ * %%
+ * Dual licensed under GNU LGPL 2.1 or Apache License 2.0
+ * #L%
+ */
+package net.sf.jsqlparser.expression;
+
+/**
+ * Describes the string used to separate the key from the value.
+ */
+public enum JsonKeyValuePairSeparator {
+ VALUE(" VALUE "), COLON(":"),
+
+ // Used in MySQL dialect
+ COMMA(","),
+
+ // Is used in case they KeyValuePair has only a key and no value
+ NOT_USED("");
+
+ private final String separator;
+
+ JsonKeyValuePairSeparator(String separator) {
+ this.separator = separator;
+ }
+
+ public String getSeparatorString() {
+ return separator;
+ }
+}
diff --git a/src/main/java/net/sf/jsqlparser/expression/JsonTableFunction.java b/src/main/java/net/sf/jsqlparser/expression/JsonTableFunction.java
new file mode 100644
index 000000000..5ee166e6f
--- /dev/null
+++ b/src/main/java/net/sf/jsqlparser/expression/JsonTableFunction.java
@@ -0,0 +1,864 @@
+/*-
+ * #%L
+ * JSQLParser library
+ * %%
+ * Copyright (C) 2004 - 2026 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;
+import java.util.Objects;
+
+import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
+import net.sf.jsqlparser.statement.create.table.ColDataType;
+
+public class JsonTableFunction extends Function {
+
+ private Expression jsonInputExpression;
+ private Expression jsonPathExpression;
+ private String pathName;
+ private final List passingClauses = new ArrayList<>();
+ private JsonTableColumnsClause columnsClause;
+ private JsonTablePlanClause planClause;
+ private JsonTableOnErrorClause onErrorClause;
+ private JsonTableParsingTypeClause parsingTypeClause;
+ private JsonTableOnEmptyClause onEmptyClause;
+ private boolean formatJson;
+
+ public enum JsonTablePlanOperator {
+ COMMA(", "), INNER(" INNER "), OUTER(" OUTER "), CROSS(" CROSS "), UNION(" UNION ");
+
+ private final String display;
+
+ JsonTablePlanOperator(String display) {
+ this.display = display;
+ }
+
+ public String getDisplay() {
+ return display;
+ }
+ }
+
+ public enum JsonTableOnErrorType {
+ ERROR, NULL, EMPTY, TRUE, FALSE
+ }
+
+ public enum JsonTableOnEmptyType {
+ ERROR, NULL, EMPTY, TRUE, FALSE
+ }
+
+ public enum JsonTableParsingType {
+ STRICT, LAX
+ }
+
+ public static class JsonTablePassingClause extends ASTNodeAccessImpl implements Serializable {
+ private Expression valueExpression;
+ private String parameterName;
+
+ public JsonTablePassingClause() {}
+
+ public JsonTablePassingClause(Expression valueExpression, String parameterName) {
+ this.valueExpression = valueExpression;
+ this.parameterName = parameterName;
+ }
+
+ public Expression getValueExpression() {
+ return valueExpression;
+ }
+
+ public JsonTablePassingClause setValueExpression(Expression valueExpression) {
+ this.valueExpression = valueExpression;
+ return this;
+ }
+
+ public String getParameterName() {
+ return parameterName;
+ }
+
+ public JsonTablePassingClause setParameterName(String parameterName) {
+ this.parameterName = parameterName;
+ return this;
+ }
+
+ public void collectExpressions(List expressions) {
+ if (valueExpression != null) {
+ expressions.add(valueExpression);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return valueExpression + " AS " + parameterName;
+ }
+ }
+
+ public static class JsonTableWrapperClause extends ASTNodeAccessImpl implements Serializable {
+ private boolean beforePathExpression;
+ private JsonFunction.JsonWrapperType wrapperType;
+ private JsonFunction.JsonWrapperMode wrapperMode;
+ private boolean array;
+
+ /**
+ * Creates a wrapper clause. Depending on the dialect, this clause can come before or after
+ * the PATH expression.
+ *
+ * - Trino: after PATH
+ * - Oracle: before PATH
+ *
+ *
+ * @param beforePathExpression A flag to determine wether the clause is rendered before or
+ * after the PATH expression
+ */
+ public JsonTableWrapperClause(boolean beforePathExpression) {
+ this.beforePathExpression = beforePathExpression;
+ }
+
+ public boolean isBeforePathExpression() {
+ return beforePathExpression;
+ }
+
+ public JsonFunction.JsonWrapperType getWrapperType() {
+ return wrapperType;
+ }
+
+ public JsonTableWrapperClause setWrapperType(JsonFunction.JsonWrapperType wrapperType) {
+ this.wrapperType = wrapperType;
+ return this;
+ }
+
+ public JsonFunction.JsonWrapperMode getWrapperMode() {
+ return wrapperMode;
+ }
+
+ public JsonTableWrapperClause setWrapperMode(JsonFunction.JsonWrapperMode wrapperMode) {
+ this.wrapperMode = wrapperMode;
+ return this;
+ }
+
+ public boolean isArray() {
+ return array;
+ }
+
+ public JsonTableWrapperClause setArray(boolean array) {
+ this.array = array;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(wrapperType);
+ if (wrapperMode != null) {
+ builder.append(" ").append(wrapperMode);
+ }
+ if (array) {
+ builder.append(" ARRAY");
+ }
+ builder.append(" WRAPPER");
+ return builder.toString();
+ }
+ }
+
+ public static class JsonTableQuotesClause extends ASTNodeAccessImpl implements Serializable {
+ private JsonFunction.JsonQuotesType quotesType;
+ private boolean onScalarString;
+
+ public JsonFunction.JsonQuotesType getQuotesType() {
+ return quotesType;
+ }
+
+ public JsonTableQuotesClause setQuotesType(JsonFunction.JsonQuotesType quotesType) {
+ this.quotesType = quotesType;
+ return this;
+ }
+
+ public boolean isOnScalarString() {
+ return onScalarString;
+ }
+
+ public JsonTableQuotesClause setOnScalarString(boolean onScalarString) {
+ this.onScalarString = onScalarString;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(quotesType).append(" QUOTES");
+ if (onScalarString) {
+ builder.append(" ON SCALAR STRING");
+ }
+ return builder.toString();
+ }
+ }
+
+ public static class JsonTableOnErrorClause extends ASTNodeAccessImpl implements Serializable {
+ private JsonTableOnErrorType type;
+ private boolean beforeColumns = true;
+
+ public JsonTableOnErrorClause(boolean beforeColumns) {
+ this.beforeColumns = beforeColumns;
+ }
+
+ public boolean isBeforeColumns() {
+ return beforeColumns;
+ }
+
+ public JsonTableOnErrorType getType() {
+ return type;
+ }
+
+ public JsonTableOnErrorClause setType(JsonTableOnErrorType type) {
+ this.type = type;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return type + " ON ERROR";
+ }
+ }
+
+ public static class JsonTableOnEmptyClause extends ASTNodeAccessImpl implements Serializable {
+ private JsonTableOnEmptyType type;
+
+ public JsonTableOnEmptyType getType() {
+ return type;
+ }
+
+ public JsonTableOnEmptyClause setType(JsonTableOnEmptyType type) {
+ this.type = type;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return type + " ON EMPTY";
+ }
+ }
+
+ public static class JsonTableParsingTypeClause extends ASTNodeAccessImpl
+ implements Serializable {
+ private JsonTableParsingType type;
+
+ public JsonTableParsingType getType() {
+ return type;
+ }
+
+ public JsonTableParsingTypeClause setType(JsonTableParsingType type) {
+ this.type = type;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return "TYPE(" + type + ")";
+ }
+ }
+
+ public static class JsonTablePlanTerm extends ASTNodeAccessImpl implements Serializable {
+ private JsonTablePlanExpression nestedPlanExpression;
+ private String name;
+ private Expression expression;
+
+ public JsonTablePlanExpression getNestedPlanExpression() {
+ return nestedPlanExpression;
+ }
+
+ public JsonTablePlanTerm setNestedPlanExpression(
+ JsonTablePlanExpression nestedPlanExpression) {
+ this.nestedPlanExpression = nestedPlanExpression;
+ return this;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public JsonTablePlanTerm setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public Expression getExpression() {
+ return expression;
+ }
+
+ public JsonTablePlanTerm setExpression(Expression expression) {
+ this.expression = expression;
+ return this;
+ }
+
+ public void collectExpressions(List expressions) {
+ if (expression != null) {
+ expressions.add(expression);
+ }
+ if (nestedPlanExpression != null) {
+ nestedPlanExpression.collectExpressions(expressions);
+ }
+ }
+
+ @Override
+ public String toString() {
+ if (nestedPlanExpression != null) {
+ return "(" + nestedPlanExpression + ")";
+ }
+ if (name != null) {
+ return name;
+ }
+ return expression != null ? expression.toString() : "";
+ }
+ }
+
+ public static class JsonTablePlanExpression extends ASTNodeAccessImpl implements Serializable {
+ private final List terms = new ArrayList<>();
+ private final List operators = new ArrayList<>();
+
+ public List getTerms() {
+ return terms;
+ }
+
+ public JsonTablePlanExpression addTerm(JsonTablePlanTerm term) {
+ terms.add(term);
+ return this;
+ }
+
+ public List getOperators() {
+ return operators;
+ }
+
+ public JsonTablePlanExpression addOperator(JsonTablePlanOperator operator) {
+ operators.add(operator);
+ return this;
+ }
+
+ public void collectExpressions(List expressions) {
+ for (JsonTablePlanTerm term : terms) {
+ if (term != null) {
+ term.collectExpressions(expressions);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ if (!terms.isEmpty()) {
+ builder.append(terms.get(0));
+ }
+ for (int i = 0; i < operators.size() && i + 1 < terms.size(); i++) {
+ builder.append(operators.get(i).getDisplay()).append(terms.get(i + 1));
+ }
+ return builder.toString();
+ }
+ }
+
+ public static class JsonTablePlanClause extends ASTNodeAccessImpl implements Serializable {
+ private boolean defaultPlan;
+ private JsonTablePlanExpression planExpression;
+
+ public boolean isDefaultPlan() {
+ return defaultPlan;
+ }
+
+ public JsonTablePlanClause setDefaultPlan(boolean defaultPlan) {
+ this.defaultPlan = defaultPlan;
+ return this;
+ }
+
+ public JsonTablePlanExpression getPlanExpression() {
+ return planExpression;
+ }
+
+ public JsonTablePlanClause setPlanExpression(JsonTablePlanExpression planExpression) {
+ this.planExpression = planExpression;
+ return this;
+ }
+
+ public void collectExpressions(List expressions) {
+ if (planExpression != null) {
+ planExpression.collectExpressions(expressions);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder("PLAN");
+ if (defaultPlan) {
+ builder.append(" DEFAULT");
+ }
+ builder.append(" (").append(planExpression).append(")");
+ return builder.toString();
+ }
+ }
+
+ public abstract static class JsonTableColumnDefinition extends ASTNodeAccessImpl
+ implements Serializable {
+ public abstract void collectExpressions(List expressions);
+ }
+
+ public static class JsonTableNestedColumnDefinition extends JsonTableColumnDefinition {
+ private boolean pathKeyword;
+ private Expression pathExpression;
+ private String pathName;
+ private JsonTableColumnsClause columnsClause;
+
+ public boolean isPathKeyword() {
+ return pathKeyword;
+ }
+
+ public JsonTableNestedColumnDefinition setPathKeyword(boolean pathKeyword) {
+ this.pathKeyword = pathKeyword;
+ return this;
+ }
+
+ public Expression getPathExpression() {
+ return pathExpression;
+ }
+
+ public JsonTableNestedColumnDefinition setPathExpression(Expression pathExpression) {
+ this.pathExpression = pathExpression;
+ return this;
+ }
+
+ public String getPathName() {
+ return pathName;
+ }
+
+ public JsonTableNestedColumnDefinition setPathName(String pathName) {
+ this.pathName = pathName;
+ return this;
+ }
+
+ public JsonTableColumnsClause getColumnsClause() {
+ return columnsClause;
+ }
+
+ public JsonTableNestedColumnDefinition setColumnsClause(
+ JsonTableColumnsClause columnsClause) {
+ this.columnsClause = columnsClause;
+ return this;
+ }
+
+ @Override
+ public void collectExpressions(List expressions) {
+ if (pathExpression != null) {
+ expressions.add(pathExpression);
+ }
+ if (columnsClause != null) {
+ columnsClause.collectExpressions(expressions);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder("NESTED");
+ if (pathKeyword) {
+ builder.append(" PATH");
+ }
+ builder.append(" ").append(pathExpression);
+ if (pathName != null) {
+ builder.append(" AS ").append(pathName);
+ }
+ builder.append(" ").append(columnsClause);
+ return builder.toString();
+ }
+ }
+
+ public static class JsonTableValueColumnDefinition extends JsonTableColumnDefinition {
+ private String columnName;
+ private boolean forOrdinality;
+ private ColDataType dataType;
+ private boolean formatJson;
+ private boolean exists;
+ private boolean onEmptyAfterOnError;
+ private String encoding;
+ private Expression pathExpression;
+ private JsonTableWrapperClause wrapperClause;
+ private JsonTableQuotesClause quotesClause;
+ private JsonFunction.JsonOnResponseBehavior onEmptyBehavior;
+ private JsonFunction.JsonOnResponseBehavior onErrorBehavior;
+ private JsonFunction.ScalarsType scalarsType;
+
+ public String getColumnName() {
+ return columnName;
+ }
+
+ public JsonTableValueColumnDefinition setColumnName(String columnName) {
+ this.columnName = columnName;
+ return this;
+ }
+
+ public boolean isExists() {
+ return exists;
+ }
+
+ public JsonTableValueColumnDefinition setExistsKeyword(boolean exists) {
+ this.exists = exists;
+ return this;
+ }
+
+ public JsonTableValueColumnDefinition setOnEmptyAfterOnError(boolean b) {
+ this.onEmptyAfterOnError = b;
+ return this;
+ }
+
+ public boolean isForOrdinality() {
+ return forOrdinality;
+ }
+
+ public JsonTableValueColumnDefinition setForOrdinality(boolean forOrdinality) {
+ this.forOrdinality = forOrdinality;
+ return this;
+ }
+
+ public ColDataType getDataType() {
+ return dataType;
+ }
+
+ public JsonTableValueColumnDefinition setDataType(ColDataType dataType) {
+ this.dataType = dataType;
+ return this;
+ }
+
+ public boolean isFormatJson() {
+ return formatJson;
+ }
+
+ public JsonTableValueColumnDefinition setFormatJson(boolean formatJson) {
+ this.formatJson = formatJson;
+ return this;
+ }
+
+ public String getEncoding() {
+ return encoding;
+ }
+
+ public JsonTableValueColumnDefinition setEncoding(String encoding) {
+ this.encoding = encoding;
+ return this;
+ }
+
+ public Expression getPathExpression() {
+ return pathExpression;
+ }
+
+ public JsonTableValueColumnDefinition setPathExpression(Expression pathExpression) {
+ this.pathExpression = pathExpression;
+ return this;
+ }
+
+ public JsonTableWrapperClause getWrapperClause() {
+ return wrapperClause;
+ }
+
+ public JsonTableValueColumnDefinition setWrapperClause(
+ JsonTableWrapperClause wrapperClause) {
+ this.wrapperClause = wrapperClause;
+ return this;
+ }
+
+ public JsonTableQuotesClause getQuotesClause() {
+ return quotesClause;
+ }
+
+ public JsonTableValueColumnDefinition setQuotesClause(JsonTableQuotesClause quotesClause) {
+ this.quotesClause = quotesClause;
+ return this;
+ }
+
+ public JsonFunction.JsonOnResponseBehavior getOnEmptyBehavior() {
+ return onEmptyBehavior;
+ }
+
+ public JsonTableValueColumnDefinition setOnEmptyBehavior(
+ JsonFunction.JsonOnResponseBehavior onEmptyBehavior) {
+ this.onEmptyBehavior = onEmptyBehavior;
+ return this;
+ }
+
+ public JsonFunction.JsonOnResponseBehavior getOnErrorBehavior() {
+ return onErrorBehavior;
+ }
+
+ public JsonTableValueColumnDefinition setOnErrorBehavior(
+ JsonFunction.JsonOnResponseBehavior onErrorBehavior) {
+ this.onErrorBehavior = onErrorBehavior;
+ return this;
+ }
+
+ public void setScalarsType(JsonFunction.ScalarsType scalarsType) {
+ this.scalarsType = scalarsType;
+ }
+
+ public JsonFunction.ScalarsType getScalarsType() {
+ return scalarsType;
+ }
+
+ @Override
+ public void collectExpressions(List expressions) {
+ if (pathExpression != null) {
+ expressions.add(pathExpression);
+ }
+ if (onEmptyBehavior != null && onEmptyBehavior.getExpression() != null) {
+ expressions.add(onEmptyBehavior.getExpression());
+ }
+ if (onErrorBehavior != null && onErrorBehavior.getExpression() != null) {
+ expressions.add(onErrorBehavior.getExpression());
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder(columnName);
+ if (forOrdinality) {
+ builder.append(" FOR ORDINALITY");
+ return builder.toString();
+ }
+ if (exists) {
+ builder.append(" EXISTS");
+ }
+ if (dataType != null) {
+ builder.append(" ").append(dataType);
+ }
+ if (formatJson) {
+ builder.append(" FORMAT JSON");
+ if (encoding != null) {
+ builder.append(" ENCODING ").append(encoding);
+ }
+ }
+ if (scalarsType != null) {
+ builder.append(" ");
+ builder.append(scalarsType);
+ builder.append(" SCALARS");
+ }
+ if (wrapperClause != null && wrapperClause.isBeforePathExpression()) {
+ builder.append(" ").append(wrapperClause);
+ }
+ if (pathExpression != null) {
+ builder.append(" PATH ").append(pathExpression);
+ }
+ if (wrapperClause != null && !wrapperClause.isBeforePathExpression()) {
+ builder.append(" ").append(wrapperClause);
+ }
+ if (quotesClause != null) {
+ builder.append(" ").append(quotesClause);
+ }
+ if (onEmptyBehavior != null && !onEmptyAfterOnError) {
+ builder.append(" ").append(onEmptyBehavior).append(" ON EMPTY");
+ }
+ if (onErrorBehavior != null) {
+ builder.append(" ").append(onErrorBehavior).append(" ON ERROR");
+ }
+ if (onEmptyBehavior != null && onEmptyAfterOnError) {
+ builder.append(" ").append(onEmptyBehavior).append(" ON EMPTY");
+ }
+ return builder.toString();
+ }
+ }
+
+ public static class JsonTableColumnsClause extends ASTNodeAccessImpl implements Serializable {
+ private final List columnDefinitions = new ArrayList<>();
+
+ public List getColumnDefinitions() {
+ return columnDefinitions;
+ }
+
+ public JsonTableColumnsClause addColumnDefinition(
+ JsonTableColumnDefinition columnDefinition) {
+ columnDefinitions.add(columnDefinition);
+ return this;
+ }
+
+ public void collectExpressions(List expressions) {
+ for (JsonTableColumnDefinition columnDefinition : columnDefinitions) {
+ if (columnDefinition != null) {
+ columnDefinition.collectExpressions(expressions);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder("COLUMNS (");
+ boolean first = true;
+ for (JsonTableColumnDefinition columnDefinition : columnDefinitions) {
+ if (!first) {
+ builder.append(", ");
+ }
+ builder.append(columnDefinition);
+ first = false;
+ }
+ builder.append(")");
+ return builder.toString();
+ }
+ }
+
+ public JsonTableFunction() {
+ setName("JSON_TABLE");
+ }
+
+ public boolean getFormatJson() {
+ return formatJson;
+ }
+
+ public JsonTableFunction setFormatJson(boolean formatJson) {
+ this.formatJson = formatJson;
+ return this;
+ }
+
+ public Expression getJsonInputExpression() {
+ return jsonInputExpression;
+ }
+
+ public JsonTableFunction setJsonInputExpression(Expression jsonInputExpression) {
+ this.jsonInputExpression = jsonInputExpression;
+ return this;
+ }
+
+ public Expression getJsonPathExpression() {
+ return jsonPathExpression;
+ }
+
+ public JsonTableFunction setJsonPathExpression(Expression jsonPathExpression) {
+ this.jsonPathExpression = jsonPathExpression;
+ return this;
+ }
+
+ public String getPathName() {
+ return pathName;
+ }
+
+ public JsonTableFunction setPathName(String pathName) {
+ this.pathName = pathName;
+ return this;
+ }
+
+ public List getPassingClauses() {
+ return passingClauses;
+ }
+
+ public JsonTableFunction addPassingClause(JsonTablePassingClause passingClause) {
+ passingClauses.add(Objects.requireNonNull(passingClause, "passingClause"));
+ return this;
+ }
+
+ public JsonTableColumnsClause getColumnsClause() {
+ return columnsClause;
+ }
+
+ public JsonTableFunction setColumnsClause(JsonTableColumnsClause columnsClause) {
+ this.columnsClause = columnsClause;
+ return this;
+ }
+
+ public JsonTablePlanClause getPlanClause() {
+ return planClause;
+ }
+
+ public JsonTableFunction setPlanClause(JsonTablePlanClause planClause) {
+ this.planClause = planClause;
+ return this;
+ }
+
+ public JsonTableOnErrorClause getOnErrorClause() {
+ return onErrorClause;
+ }
+
+ public JsonTableFunction setOnErrorClause(JsonTableOnErrorClause onErrorClause) {
+ this.onErrorClause = onErrorClause;
+ return this;
+ }
+
+ public JsonTableParsingTypeClause getParsingTypeClause() {
+ return parsingTypeClause;
+ }
+
+ public JsonTableFunction setParsingTypeClause(JsonTableParsingTypeClause parsingTypeClause) {
+ this.parsingTypeClause = parsingTypeClause;
+ return this;
+ }
+
+ public JsonTableOnEmptyClause getOnEmptyClause() {
+ return onEmptyClause;
+ }
+
+ public JsonTableFunction setOnEmptyClause(JsonTableOnEmptyClause onEmptyClause) {
+ this.onEmptyClause = onEmptyClause;
+ return this;
+ }
+
+ public List getAllExpressions() {
+ List expressions = new ArrayList<>();
+ if (jsonInputExpression != null) {
+ expressions.add(jsonInputExpression);
+ }
+ if (jsonPathExpression != null) {
+ expressions.add(jsonPathExpression);
+ }
+ for (JsonTablePassingClause passingClause : passingClauses) {
+ passingClause.collectExpressions(expressions);
+ }
+ if (columnsClause != null) {
+ columnsClause.collectExpressions(expressions);
+ }
+ if (planClause != null) {
+ planClause.collectExpressions(expressions);
+ }
+ return expressions;
+ }
+
+ @Override
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder("JSON_TABLE(");
+ builder.append(jsonInputExpression);
+ if (formatJson) {
+ builder.append(" FORMAT JSON");
+ }
+ if (jsonPathExpression != null) {
+ builder.append(", ").append(jsonPathExpression);
+ }
+ if (pathName != null) {
+ builder.append(" AS ").append(pathName);
+ }
+ if (!passingClauses.isEmpty()) {
+ builder.append(" PASSING ");
+ boolean first = true;
+ for (JsonTablePassingClause passingClause : passingClauses) {
+ if (!first) {
+ builder.append(", ");
+ }
+ builder.append(passingClause);
+ first = false;
+ }
+ }
+ if (onErrorClause != null && onErrorClause.isBeforeColumns()) {
+ builder.append(" ").append(onErrorClause);
+ }
+ if (parsingTypeClause != null) {
+ builder.append(" ").append(parsingTypeClause);
+ }
+ if (onEmptyClause != null) {
+ builder.append(" ").append(onEmptyClause);
+ }
+ builder.append(" ").append(columnsClause);
+ if (planClause != null) {
+ builder.append(" ").append(planClause);
+ }
+ if (onErrorClause != null && !onErrorClause.isBeforeColumns()) {
+ builder.append(" ").append(onErrorClause);
+ }
+ builder.append(")");
+ return builder.toString();
+ }
+}
diff --git a/src/main/java/net/sf/jsqlparser/expression/KeepExpression.java b/src/main/java/net/sf/jsqlparser/expression/KeepExpression.java
index 2712a0e46..b8e493244 100644
--- a/src/main/java/net/sf/jsqlparser/expression/KeepExpression.java
+++ b/src/main/java/net/sf/jsqlparser/expression/KeepExpression.java
@@ -14,6 +14,7 @@
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;
@@ -24,8 +25,8 @@ public class KeepExpression extends ASTNodeAccessImpl implements Expression {
private boolean first = false;
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
public List getOrderByElements() {
@@ -94,13 +95,15 @@ public KeepExpression withFirst(boolean first) {
}
public KeepExpression addOrderByElements(OrderByElement... orderByElements) {
- List collection = Optional.ofNullable(getOrderByElements()).orElseGet(ArrayList::new);
+ List collection =
+ Optional.ofNullable(getOrderByElements()).orElseGet(ArrayList::new);
Collections.addAll(collection, orderByElements);
return this.withOrderByElements(collection);
}
public KeepExpression addOrderByElements(Collection extends OrderByElement> orderByElements) {
- List collection = Optional.ofNullable(getOrderByElements()).orElseGet(ArrayList::new);
+ 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/KeyExpression.java b/src/main/java/net/sf/jsqlparser/expression/KeyExpression.java
new file mode 100644
index 000000000..2bb063875
--- /dev/null
+++ b/src/main/java/net/sf/jsqlparser/expression/KeyExpression.java
@@ -0,0 +1,44 @@
+/*-
+ * #%L
+ * JSQLParser library
+ * %%
+ * Copyright (C) 2004 - 2026 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;
+
+/**
+ * Dialect specific expression for constructs such as {@code KEY chain.entity}.
+ */
+public class KeyExpression extends ASTNodeAccessImpl implements Expression {
+ private final Expression expression;
+
+ public KeyExpression(Expression expression) {
+ this.expression = Objects.requireNonNull(expression,
+ "The EXPRESSION of the KEY expression must not be null");
+ }
+
+ 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("KEY ").append(expression);
+ return builder;
+ }
+
+ @Override
+ public String toString() {
+ return appendTo(new StringBuilder()).toString();
+ }
+}
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 000000000..e2819060f
--- /dev/null
+++ b/src/main/java/net/sf/jsqlparser/expression/LambdaExpression.java
@@ -0,0 +1,83 @@
+/*-
+ * #%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.expression.operators.relational.ExpressionList;
+import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
+
+import java.util.ArrayList;
+import java.util.Collections;
+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 = Collections.singletonList(identifier);
+ this.expression = expression;
+ }
+
+ public LambdaExpression(List identifiers, Expression expression) {
+ this.identifiers = identifiers;
+ this.expression = expression;
+ }
+
+ public static LambdaExpression from(ExpressionList extends Expression> expressionList,
+ Expression expression) {
+ List identifiers = new ArrayList<>(expressionList.size());
+ for (Expression variable : expressionList) {
+ identifiers.add(variable.toString());
+ }
+ return new LambdaExpression(identifiers, 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
index 014802fb5..eeba186cc 100644
--- a/src/main/java/net/sf/jsqlparser/expression/LongValue.java
+++ b/src/main/java/net/sf/jsqlparser/expression/LongValue.java
@@ -26,6 +26,9 @@ public LongValue() {
}
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);
@@ -38,22 +41,22 @@ public LongValue(long value) {
}
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
public long getValue() {
return Long.parseLong(stringValue);
}
- public BigInteger getBigIntegerValue() {
- return new BigInteger(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;
diff --git a/src/main/java/net/sf/jsqlparser/expression/LowExpression.java b/src/main/java/net/sf/jsqlparser/expression/LowExpression.java
new file mode 100644
index 000000000..2d2882a53
--- /dev/null
+++ b/src/main/java/net/sf/jsqlparser/expression/LowExpression.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;
+
+import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
+
+public class LowExpression extends ASTNodeAccessImpl implements Expression {
+ private Expression expression;
+
+ public LowExpression() {
+ // empty constructor
+ }
+
+ public LowExpression(Expression expression) {
+ this.expression = expression;
+ }
+
+ @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;
+ }
+
+ @Override
+ public String toString() {
+ return "LOW " + expression.toString();
+ }
+}
diff --git a/src/main/java/net/sf/jsqlparser/expression/MySQLGroupConcat.java b/src/main/java/net/sf/jsqlparser/expression/MySQLGroupConcat.java
index 49161bbee..aa4a53357 100644
--- a/src/main/java/net/sf/jsqlparser/expression/MySQLGroupConcat.java
+++ b/src/main/java/net/sf/jsqlparser/expression/MySQLGroupConcat.java
@@ -59,8 +59,8 @@ public void setSeparator(String separator) {
}
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
@Override
diff --git a/src/main/java/net/sf/jsqlparser/expression/NextValExpression.java b/src/main/java/net/sf/jsqlparser/expression/NextValExpression.java
index 4838bbcf4..a56723851 100644
--- a/src/main/java/net/sf/jsqlparser/expression/NextValExpression.java
+++ b/src/main/java/net/sf/jsqlparser/expression/NextValExpression.java
@@ -1,8 +1,8 @@
-/* -
+/*-
* #%L
* JSQLParser library
* %%
- * Copyright (C) 2004 - 2019 JSQLParser
+ * Copyright (C) 2004 - 2024 JSQLParser
* %%
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
* #L%
@@ -11,11 +11,13 @@
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);
+ 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;
@@ -63,7 +65,7 @@ public String toString() {
}
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ 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
index d61ae2bb9..bb2769fdd 100644
--- a/src/main/java/net/sf/jsqlparser/expression/NotExpression.java
+++ b/src/main/java/net/sf/jsqlparser/expression/NotExpression.java
@@ -42,8 +42,8 @@ public final void setExpression(Expression expression) {
}
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
@Override
diff --git a/src/main/java/net/sf/jsqlparser/expression/NullValue.java b/src/main/java/net/sf/jsqlparser/expression/NullValue.java
index b6397030f..fb096eff6 100644
--- a/src/main/java/net/sf/jsqlparser/expression/NullValue.java
+++ b/src/main/java/net/sf/jsqlparser/expression/NullValue.java
@@ -14,8 +14,8 @@
public class NullValue extends ASTNodeAccessImpl implements Expression {
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
@Override
diff --git a/src/main/java/net/sf/jsqlparser/expression/NumericBind.java b/src/main/java/net/sf/jsqlparser/expression/NumericBind.java
index 8f5ac4088..f38ff15d4 100644
--- a/src/main/java/net/sf/jsqlparser/expression/NumericBind.java
+++ b/src/main/java/net/sf/jsqlparser/expression/NumericBind.java
@@ -24,8 +24,8 @@ public void setBindId(int bindId) {
}
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
@Override
diff --git a/src/main/java/net/sf/jsqlparser/expression/OracleHierarchicalExpression.java b/src/main/java/net/sf/jsqlparser/expression/OracleHierarchicalExpression.java
index a3644e441..693128d03 100644
--- a/src/main/java/net/sf/jsqlparser/expression/OracleHierarchicalExpression.java
+++ b/src/main/java/net/sf/jsqlparser/expression/OracleHierarchicalExpression.java
@@ -13,10 +13,10 @@
public class OracleHierarchicalExpression extends ASTNodeAccessImpl implements Expression {
+ boolean connectFirst = false;
private Expression startExpression;
private Expression connectExpression;
private boolean noCycle = false;
- boolean connectFirst = false;
public Expression getStartExpression() {
return startExpression;
@@ -51,8 +51,8 @@ public void setConnectFirst(boolean connectFirst) {
}
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
@Override
@@ -65,11 +65,11 @@ public String toString() {
}
b.append(connectExpression.toString());
if (startExpression != null) {
- b.append(" START WITH ").append(startExpression.toString());
+ b.append(" START WITH ").append(startExpression);
}
} else {
if (startExpression != null) {
- b.append(" START WITH ").append(startExpression.toString());
+ b.append(" START WITH ").append(startExpression);
}
b.append(" CONNECT BY ");
if (isNoCycle()) {
diff --git a/src/main/java/net/sf/jsqlparser/expression/OracleHint.java b/src/main/java/net/sf/jsqlparser/expression/OracleHint.java
index c229e880e..4ab164f98 100644
--- a/src/main/java/net/sf/jsqlparser/expression/OracleHint.java
+++ b/src/main/java/net/sf/jsqlparser/expression/OracleHint.java
@@ -24,7 +24,7 @@ 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);
+ Pattern.compile("/\\*\\+ *([^ ].*[^ ]) *\\*+/", Pattern.MULTILINE | Pattern.DOTALL);
private String value;
private boolean singleLine = false;
@@ -33,6 +33,17 @@ 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);
@@ -65,8 +76,8 @@ public void setSingleLine(boolean singleLine) {
}
@Override
- public void accept(ExpressionVisitor visitor) {
- visitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
@Override
@@ -87,15 +98,4 @@ public OracleHint withSingleLine(boolean singleLine) {
this.setSingleLine(singleLine);
return this;
}
-
- 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;
- }
- }
}
diff --git a/src/main/java/net/sf/jsqlparser/expression/OracleNamedFunctionParameter.java b/src/main/java/net/sf/jsqlparser/expression/OracleNamedFunctionParameter.java
index 51a7a433c..0dd76b5f6 100644
--- a/src/main/java/net/sf/jsqlparser/expression/OracleNamedFunctionParameter.java
+++ b/src/main/java/net/sf/jsqlparser/expression/OracleNamedFunctionParameter.java
@@ -10,10 +10,10 @@
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 {
@@ -21,8 +21,10 @@ public class OracleNamedFunctionParameter extends ASTNodeAccessImpl implements E
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.");
+ 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() {
@@ -34,18 +36,18 @@ public Expression getExpression() {
}
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
-
+
public StringBuilder appendTo(StringBuilder builder) {
builder.append(name)
- .append(" => ")
- .append(expression);
-
+ .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
index 7c71ddfa0..fbf21de87 100644
--- a/src/main/java/net/sf/jsqlparser/expression/OrderByClause.java
+++ b/src/main/java/net/sf/jsqlparser/expression/OrderByClause.java
@@ -15,6 +15,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Optional;
+
import net.sf.jsqlparser.statement.select.OrderByElement;
public class OrderByClause implements Serializable {
@@ -46,13 +47,15 @@ public OrderByClause withOrderByElements(List orderByElements) {
}
public OrderByClause addOrderByElements(OrderByElement... orderByElements) {
- List collection = Optional.ofNullable(getOrderByElements()).orElseGet(ArrayList::new);
+ List collection =
+ Optional.ofNullable(getOrderByElements()).orElseGet(ArrayList::new);
Collections.addAll(collection, orderByElements);
return this.withOrderByElements(collection);
}
public OrderByClause addOrderByElements(Collection extends OrderByElement> orderByElements) {
- List collection = Optional.ofNullable(getOrderByElements()).orElseGet(ArrayList::new);
+ 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
index f9e41b4bc..09ccd6183 100644
--- a/src/main/java/net/sf/jsqlparser/expression/OverlapsCondition.java
+++ b/src/main/java/net/sf/jsqlparser/expression/OverlapsCondition.java
@@ -12,36 +12,30 @@
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
-public class OverlapsCondition extends ASTNodeAccessImpl implements Expression{
+public class OverlapsCondition extends ASTNodeAccessImpl implements Expression {
+ private final ExpressionList> left;
+ private final ExpressionList> right;
-
- private ExpressionList left;
- private ExpressionList right;
-
-
- public OverlapsCondition(ExpressionList left, ExpressionList right) {
+ public OverlapsCondition(ExpressionList> left, ExpressionList> right) {
this.left = left;
this.right = right;
}
- public ExpressionList getLeft() {
+ public ExpressionList> getLeft() {
return left;
}
- public ExpressionList getRight() {
+ public ExpressionList> getRight() {
return right;
}
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ 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()
- );
+ 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
index 51d3ef707..c162cd1ef 100644
--- a/src/main/java/net/sf/jsqlparser/expression/Parenthesis.java
+++ b/src/main/java/net/sf/jsqlparser/expression/Parenthesis.java
@@ -9,45 +9,29 @@
*/
package net.sf.jsqlparser.expression;
-import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
+import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;
/**
- * It represents an expression like "(" expression ")"
+ * @deprecated This class is deprecated since version 5.0. Use {@link ParenthesedExpressionList}
+ * instead. The reason for deprecation is the ambiguity and redundancy.
*/
-public class Parenthesis extends ASTNodeAccessImpl implements Expression {
-
- private Expression expression;
-
- public Parenthesis() {}
-
- public Parenthesis(Expression expression) {
- setExpression(expression);
- }
-
+@Deprecated(since = "5.0", forRemoval = true)
+public class Parenthesis extends ParenthesedExpressionList {
public Expression getExpression() {
- return expression;
- }
-
- public final void setExpression(Expression expression) {
- this.expression = expression;
- }
-
- @Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ return isEmpty() ? null : get(0);
}
- @Override
- public String toString() {
- return "(" + (expression != null ? expression : "") + ")";
+ public Parenthesis setExpression(Expression expression) {
+ this.set(0, expression);
+ return this;
}
public Parenthesis withExpression(Expression expression) {
- this.setExpression(expression);
- return this;
+ 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
index 0b0f70663..6ad41e994 100644
--- a/src/main/java/net/sf/jsqlparser/expression/PartitionByClause.java
+++ b/src/main/java/net/sf/jsqlparser/expression/PartitionByClause.java
@@ -14,37 +14,51 @@
import java.io.Serializable;
-public class PartitionByClause implements Serializable {
- ExpressionList partitionExpressionList;
+public class PartitionByClause extends ExpressionList implements Serializable {
boolean brackets = false;
- public ExpressionList getPartitionExpressionList() {
- return partitionExpressionList;
+ @Deprecated
+ public ExpressionList getPartitionExpressionList() {
+ return this;
}
-
- public void setPartitionExpressionList(ExpressionList partitionExpressionList) {
+
+ @Deprecated
+ public void setPartitionExpressionList(ExpressionList partitionExpressionList) {
setPartitionExpressionList(partitionExpressionList, false);
}
- public void setPartitionExpressionList(ExpressionList partitionExpressionList, boolean brackets) {
- this.partitionExpressionList = partitionExpressionList;
+ @Deprecated
+ public void setPartitionExpressionList(ExpressionList partitionExpressionList,
+ boolean brackets) {
+ setExpressions(partitionExpressionList, brackets);
+ }
+
+ public PartitionByClause setExpressions(ExpressionList partitionExpressionList,
+ boolean brackets) {
+ clear();
+ if (partitionExpressionList != null) {
+ addAll(partitionExpressionList);
+ }
this.brackets = brackets;
+ return this;
}
public void toStringPartitionBy(StringBuilder b) {
- if (partitionExpressionList != null && !partitionExpressionList.getExpressions().isEmpty()) {
+ if (!isEmpty()) {
b.append("PARTITION BY ");
- b.append(PlainSelect.
- getStringList(partitionExpressionList.getExpressions(), true, brackets));
+ b.append(PlainSelect.getStringList(this, true,
+ brackets));
b.append(" ");
}
}
-
+
public boolean isBrackets() {
return brackets;
}
- public PartitionByClause withPartitionExpressionList(ExpressionList partitionExpressionList) {
+ @Deprecated
+ public PartitionByClause withPartitionExpressionList(
+ ExpressionList partitionExpressionList) {
this.setPartitionExpressionList(partitionExpressionList);
return this;
}
diff --git a/src/main/java/net/sf/jsqlparser/expression/PostgresNamedFunctionParameter.java b/src/main/java/net/sf/jsqlparser/expression/PostgresNamedFunctionParameter.java
new file mode 100644
index 000000000..573ad60f2
--- /dev/null
+++ b/src/main/java/net/sf/jsqlparser/expression/PostgresNamedFunctionParameter.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 net.sf.jsqlparser.parser.ASTNodeAccessImpl;
+
+import java.util.Objects;
+
+/**
+ * @author Andreas Reichel
+ */
+public class PostgresNamedFunctionParameter extends ASTNodeAccessImpl implements Expression {
+ private final String name;
+ private final Expression expression;
+
+ public PostgresNamedFunctionParameter(String name, Expression expression) {
+ this.name = Objects.requireNonNull(name,
+ "The NAME of the PostgresNamedFunctionParameter must not be null.");
+ this.expression = Objects.requireNonNull(expression,
+ "The EXPRESSION of the PostgresNamedFunctionParameter 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/PreferringClause.java b/src/main/java/net/sf/jsqlparser/expression/PreferringClause.java
new file mode 100644
index 000000000..2db468dbb
--- /dev/null
+++ b/src/main/java/net/sf/jsqlparser/expression/PreferringClause.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.expression.operators.relational.ExpressionList;
+
+import java.io.Serializable;
+
+public class PreferringClause implements Serializable {
+ private Expression preferring;
+ private PartitionByClause partitionBy;
+
+ public PreferringClause(Expression preferring) {
+ this.preferring = preferring;
+ }
+
+ public void setPartitionExpressionList(ExpressionList expressionList,
+ boolean brackets) {
+ if (this.partitionBy == null) {
+ this.partitionBy = new PartitionByClause();
+ }
+ partitionBy.setExpressions(expressionList, brackets);
+ }
+
+ public void toStringPreferring(StringBuilder b) {
+ b.append("PREFERRING ");
+ b.append(preferring.toString());
+
+ if (partitionBy != null) {
+ b.append(" ");
+ partitionBy.toStringPartitionBy(b);
+ }
+ }
+
+ public Expression getPreferring() {
+ return preferring;
+ }
+
+ public PreferringClause setPreferring(Expression preferring) {
+ this.preferring = preferring;
+ return this;
+ }
+
+ public PartitionByClause getPartitionBy() {
+ return partitionBy;
+ }
+
+ public PreferringClause setPartitionBy(PartitionByClause partitionBy) {
+ this.partitionBy = partitionBy;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ toStringPreferring(sb);
+ return sb.toString();
+ }
+}
diff --git a/src/main/java/net/sf/jsqlparser/expression/RangeExpression.java b/src/main/java/net/sf/jsqlparser/expression/RangeExpression.java
index 69cba377d..dd05827d6 100644
--- a/src/main/java/net/sf/jsqlparser/expression/RangeExpression.java
+++ b/src/main/java/net/sf/jsqlparser/expression/RangeExpression.java
@@ -44,7 +44,7 @@ public String toString() {
}
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
}
diff --git a/src/main/java/net/sf/jsqlparser/expression/RawFunction.java b/src/main/java/net/sf/jsqlparser/expression/RawFunction.java
new file mode 100644
index 000000000..1c2d5b874
--- /dev/null
+++ b/src/main/java/net/sf/jsqlparser/expression/RawFunction.java
@@ -0,0 +1,41 @@
+/*-
+ * #%L
+ * JSQLParser library
+ * %%
+ * Copyright (C) 2004 - 2026 JSQLParser
+ * %%
+ * Dual licensed under GNU LGPL 2.1 or Apache License 2.0
+ * #L%
+ */
+package net.sf.jsqlparser.expression;
+
+/**
+ * Function with a raw argument body preserved as-is for deparsing.
+ */
+public class RawFunction extends Function {
+ private String rawArguments;
+
+ public RawFunction() {}
+
+ public RawFunction(String name, String rawArguments) {
+ setName(name);
+ this.rawArguments = rawArguments;
+ }
+
+ public String getRawArguments() {
+ return rawArguments;
+ }
+
+ public void setRawArguments(String rawArguments) {
+ this.rawArguments = rawArguments;
+ }
+
+ @Override
+ public String toString() {
+ String name = getName();
+ if (rawArguments == null) {
+ return name + "()";
+ }
+ return name + "(" + rawArguments + ")";
+ }
+}
diff --git a/src/main/java/net/sf/jsqlparser/expression/RowConstructor.java b/src/main/java/net/sf/jsqlparser/expression/RowConstructor.java
index 6eabf3b70..2f5844ea0 100644
--- a/src/main/java/net/sf/jsqlparser/expression/RowConstructor.java
+++ b/src/main/java/net/sf/jsqlparser/expression/RowConstructor.java
@@ -36,8 +36,13 @@ public String toString() {
return (name != null ? name : "") + super.toString();
}
- public RowConstructor withName(String name) {
+ 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
index 16376a470..0aaefa1aa 100644
--- a/src/main/java/net/sf/jsqlparser/expression/RowGetExpression.java
+++ b/src/main/java/net/sf/jsqlparser/expression/RowGetExpression.java
@@ -21,8 +21,8 @@ public RowGetExpression(Expression expression, String columnName) {
}
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
@Override
diff --git a/src/main/java/net/sf/jsqlparser/expression/SQLServerHints.java b/src/main/java/net/sf/jsqlparser/expression/SQLServerHints.java
index 84b7c2f38..34d14964b 100644
--- a/src/main/java/net/sf/jsqlparser/expression/SQLServerHints.java
+++ b/src/main/java/net/sf/jsqlparser/expression/SQLServerHints.java
@@ -18,8 +18,7 @@ public class SQLServerHints implements Serializable {
private Boolean noLock;
private String indexName;
- public SQLServerHints() {
- }
+ public SQLServerHints() {}
public SQLServerHints withNoLock() {
this.noLock = true;
diff --git a/src/main/java/net/sf/jsqlparser/expression/SignedExpression.java b/src/main/java/net/sf/jsqlparser/expression/SignedExpression.java
index dabc2f9c5..725d449e8 100644
--- a/src/main/java/net/sf/jsqlparser/expression/SignedExpression.java
+++ b/src/main/java/net/sf/jsqlparser/expression/SignedExpression.java
@@ -48,8 +48,8 @@ public final void setExpression(Expression expression) {
}
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
@Override
diff --git a/src/main/java/net/sf/jsqlparser/expression/SpannerInterleaveIn.java b/src/main/java/net/sf/jsqlparser/expression/SpannerInterleaveIn.java
index 18cb16ac4..0bf49925d 100644
--- a/src/main/java/net/sf/jsqlparser/expression/SpannerInterleaveIn.java
+++ b/src/main/java/net/sf/jsqlparser/expression/SpannerInterleaveIn.java
@@ -16,14 +16,6 @@
public class SpannerInterleaveIn {
- public enum OnDelete {
- CASCADE, NO_ACTION;
-
- public static OnDelete from(String action) {
- return Enum.valueOf(OnDelete.class, action.toUpperCase());
- }
- }
-
private Table table;
private OnDelete onDelete;
@@ -77,4 +69,12 @@ 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
index 743efea46..a16536fab 100644
--- a/src/main/java/net/sf/jsqlparser/expression/StringValue.java
+++ b/src/main/java/net/sf/jsqlparser/expression/StringValue.java
@@ -20,11 +20,11 @@
*/
public final class StringValue extends ASTNodeAccessImpl implements Expression {
- private String value = "";
- private String prefix = null;
-
public static final List ALLOWED_PREFIXES =
Arrays.asList("N", "U", "E", "R", "B", "RB", "_utf8", "Q");
+ private String value = "";
+ private String prefix = null;
+ private String quoteStr = "'";
public StringValue() {
// empty constructor
@@ -36,6 +36,11 @@ public StringValue(String escapedValue) {
&& escapedValue.endsWith("'")) {
value = escapedValue.substring(1, escapedValue.length() - 1);
return;
+ } else if (escapedValue.length() >= 4 && escapedValue.startsWith("$$")
+ && escapedValue.endsWith("$$")) {
+ value = escapedValue.substring(2, escapedValue.length() - 2);
+ quoteStr = "$$";
+ return;
}
if (escapedValue.length() > 2) {
@@ -57,10 +62,27 @@ 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 getQuoteStr() {
+ return quoteStr;
+ }
+
+ public StringValue setQuoteStr(String quoteStr) {
+ this.quoteStr = quoteStr;
+ return this;
+ }
+
public String getNotExcapedValue() {
StringBuilder buffer = new StringBuilder(value);
int index = 0;
@@ -73,22 +95,14 @@ public String getNotExcapedValue() {
return buffer.toString();
}
- public void setValue(String string) {
- value = string;
- }
-
- public void setPrefix(String prefix) {
- this.prefix = prefix;
- }
-
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
@Override
public String toString() {
- return (prefix != null ? prefix : "") + "'" + value + "'";
+ return (prefix != null ? prefix : "") + quoteStr + value + quoteStr;
}
public StringValue withPrefix(String 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 000000000..4bd38a693
--- /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
index f2c4cfa65..759da0e7a 100644
--- a/src/main/java/net/sf/jsqlparser/expression/TimeKeyExpression.java
+++ b/src/main/java/net/sf/jsqlparser/expression/TimeKeyExpression.java
@@ -24,8 +24,8 @@ public TimeKeyExpression(final String value) {
}
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
public String getStringValue() {
diff --git a/src/main/java/net/sf/jsqlparser/expression/TimeValue.java b/src/main/java/net/sf/jsqlparser/expression/TimeValue.java
index 100945147..2b3f74394 100644
--- a/src/main/java/net/sf/jsqlparser/expression/TimeValue.java
+++ b/src/main/java/net/sf/jsqlparser/expression/TimeValue.java
@@ -25,12 +25,15 @@ public TimeValue() {
}
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 void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
public Time getValue() {
diff --git a/src/main/java/net/sf/jsqlparser/expression/TimestampValue.java b/src/main/java/net/sf/jsqlparser/expression/TimestampValue.java
index c6e84a4ea..a06082304 100644
--- a/src/main/java/net/sf/jsqlparser/expression/TimestampValue.java
+++ b/src/main/java/net/sf/jsqlparser/expression/TimestampValue.java
@@ -19,26 +19,26 @@
*/
public final class TimestampValue extends ASTNodeAccessImpl implements Expression {
+ private static final char QUOTATION = '\'';
private Timestamp value;
private String rawValue;
- private static final char QUOTATION = '\'';
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."));
+ // 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 void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
public Timestamp getValue() {
diff --git a/src/main/java/net/sf/jsqlparser/expression/TimezoneExpression.java b/src/main/java/net/sf/jsqlparser/expression/TimezoneExpression.java
index 6fe53154f..a67572ace 100644
--- a/src/main/java/net/sf/jsqlparser/expression/TimezoneExpression.java
+++ b/src/main/java/net/sf/jsqlparser/expression/TimezoneExpression.java
@@ -12,41 +12,52 @@
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;
- private ExpressionList timezoneExpressions = new ExpressionList<>();
+
+ 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 void setLeftExpression(Expression expression) {
- leftExpression = expression;
+ public TimezoneExpression setLeftExpression(Expression expression) {
+ this.leftExpression = expression;
+ return this;
}
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
public List getTimezoneExpressions() {
return timezoneExpressions;
}
- public void addTimezoneExpression(Expression timezoneExpr) {
- this.timezoneExpressions.add(timezoneExpr);
+ public void addTimezoneExpression(Expression... timezoneExpr) {
+ this.timezoneExpressions.addAll(Arrays.asList(timezoneExpr));
}
@Override
public String toString() {
- String returnValue = getLeftExpression().toString();
+ StringBuilder returnValue = new StringBuilder(leftExpression.toString());
for (Expression expr : timezoneExpressions) {
- returnValue += " AT TIME ZONE " + expr.toString();
+ returnValue.append(" AT TIME ZONE ").append(expr.toString());
}
- return returnValue;
+ 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
index 61fa05290..b68f1dfb7 100644
--- a/src/main/java/net/sf/jsqlparser/expression/TranscodingFunction.java
+++ b/src/main/java/net/sf/jsqlparser/expression/TranscodingFunction.java
@@ -10,20 +10,58 @@
package net.sf.jsqlparser.expression;
import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
+import net.sf.jsqlparser.statement.create.table.ColDataType;
+
+import java.util.Objects;
public class TranscodingFunction extends ASTNodeAccessImpl implements Expression {
+ private String keyword = "CONVERT";
+ private boolean isTranscodeStyle = true;
+ private ColDataType colDataType;
private Expression expression;
private String transcodingName;
+ public TranscodingFunction(String keyword, Expression expression, String transcodingName) {
+ this.keyword = Objects.requireNonNullElse(keyword, "CONVERT").toUpperCase();
+ this.expression = expression;
+ this.transcodingName = transcodingName;
+ }
+
public TranscodingFunction(Expression expression, String transcodingName) {
this.expression = expression;
this.transcodingName = transcodingName;
}
+ public TranscodingFunction(String keyword, ColDataType colDataType, Expression expression,
+ String transcodingName) {
+ this.keyword = Objects.requireNonNullElse(keyword, "CONVERT").toUpperCase();
+ this.colDataType = colDataType;
+ this.expression = expression;
+ this.transcodingName = transcodingName;
+ this.isTranscodeStyle = false;
+ }
+
+ 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 String getKeyword() {
+ return keyword;
+ }
+
+ public TranscodingFunction setKeyword(String keyword) {
+ this.keyword = Objects.requireNonNullElse(keyword, "CONVERT").toUpperCase();
+ return this;
+ }
+
public Expression getExpression() {
return expression;
}
@@ -51,17 +89,49 @@ public TranscodingFunction withTranscodingName(String transcodingName) {
}
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(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) {
- return builder
- .append("CONVERT( ")
- .append(expression)
- .append(" USING ")
- .append(transcodingName)
- .append(" )");
+ if (isTranscodeStyle) {
+ return builder
+ .append(keyword)
+ .append("( ")
+ .append(expression)
+ .append(" USING ")
+ .append(transcodingName)
+ .append(" )");
+ } else {
+ return builder
+ .append(keyword)
+ .append("( ")
+ .append(colDataType)
+ .append(", ")
+ .append(expression)
+ .append(transcodingName != null && !transcodingName.isEmpty()
+ ? ", " + transcodingName
+ : "")
+ .append(" )");
+ }
}
@Override
diff --git a/src/main/java/net/sf/jsqlparser/expression/TrimFunction.java b/src/main/java/net/sf/jsqlparser/expression/TrimFunction.java
index def6ec3d1..e8ea0305d 100644
--- a/src/main/java/net/sf/jsqlparser/expression/TrimFunction.java
+++ b/src/main/java/net/sf/jsqlparser/expression/TrimFunction.java
@@ -12,10 +12,6 @@
import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
public class TrimFunction extends ASTNodeAccessImpl implements Expression {
- public enum TrimSpecification {
- LEADING, TRAILING, BOTH
- }
-
private TrimSpecification trimSpecification;
private Expression expression;
private Expression fromExpression;
@@ -92,8 +88,8 @@ public TrimFunction withUsingFromKeyword(boolean useFromKeyword) {
}
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
public StringBuilder appendTo(StringBuilder builder) {
@@ -121,4 +117,8 @@ public StringBuilder appendTo(StringBuilder builder) {
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
index f7abde5f8..b0599a3c5 100644
--- a/src/main/java/net/sf/jsqlparser/expression/UserVariable.java
+++ b/src/main/java/net/sf/jsqlparser/expression/UserVariable.java
@@ -24,7 +24,7 @@ public UserVariable() {
}
public UserVariable(String name) {
- this.name = name;
+ setName(name);
}
public String getName() {
@@ -32,12 +32,20 @@ public String getName() {
}
public void setName(String name) {
- this.name = name;
+ if (name.startsWith("@@")) {
+ this.name = name.substring(2);
+ doubleAdd = true;
+ } else if (name.startsWith("@")) {
+ this.name = name.substring(1);
+ doubleAdd = false;
+ } else {
+ this.name = name;
+ }
}
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
public boolean isDoubleAdd() {
diff --git a/src/main/java/net/sf/jsqlparser/expression/VariableAssignment.java b/src/main/java/net/sf/jsqlparser/expression/VariableAssignment.java
index a92f639e2..2ad773205 100644
--- a/src/main/java/net/sf/jsqlparser/expression/VariableAssignment.java
+++ b/src/main/java/net/sf/jsqlparser/expression/VariableAssignment.java
@@ -50,9 +50,9 @@ public String toString() {
}
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ 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
index 2d4de0b29..5ed58de11 100644
--- a/src/main/java/net/sf/jsqlparser/expression/WhenClause.java
+++ b/src/main/java/net/sf/jsqlparser/expression/WhenClause.java
@@ -12,17 +12,23 @@
import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
/**
- * A clause of following syntax: WHEN condition THEN expression. Which is part
- * of a CaseExpression.
+ * 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 void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
public Expression getThenExpression() {
diff --git a/src/main/java/net/sf/jsqlparser/expression/WindowDefinition.java b/src/main/java/net/sf/jsqlparser/expression/WindowDefinition.java
index b15ec6fcb..60760baee 100644
--- a/src/main/java/net/sf/jsqlparser/expression/WindowDefinition.java
+++ b/src/main/java/net/sf/jsqlparser/expression/WindowDefinition.java
@@ -11,6 +11,7 @@
import java.io.Serializable;
import java.util.List;
+
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.statement.select.OrderByElement;
@@ -50,12 +51,13 @@ public ExpressionList getPartitionExpressionList() {
return partitionBy.getPartitionExpressionList();
}
- public void setPartitionExpressionList(ExpressionList partitionExpressionList) {
+ public void setPartitionExpressionList(ExpressionList partitionExpressionList) {
setPartitionExpressionList(partitionExpressionList, false);
}
- public void setPartitionExpressionList(ExpressionList partitionExpressionList, boolean brackets) {
- partitionBy.setPartitionExpressionList(partitionExpressionList, brackets);
+ public void setPartitionExpressionList(ExpressionList partitionExpressionList,
+ boolean brackets) {
+ partitionBy.setExpressions(partitionExpressionList, brackets);
}
public String getWindowName() {
@@ -65,7 +67,7 @@ public String getWindowName() {
public void setWindowName(String windowName) {
this.windowName = windowName;
}
-
+
public WindowDefinition withWindowName(String windowName) {
setWindowName(windowName);
return this;
diff --git a/src/main/java/net/sf/jsqlparser/expression/WindowElement.java b/src/main/java/net/sf/jsqlparser/expression/WindowElement.java
index 89c85e79a..97260ce97 100644
--- a/src/main/java/net/sf/jsqlparser/expression/WindowElement.java
+++ b/src/main/java/net/sf/jsqlparser/expression/WindowElement.java
@@ -13,14 +13,6 @@
public class WindowElement implements Serializable {
- public enum Type {
- ROWS, RANGE;
-
- public static Type from(String type) {
- return Enum.valueOf(Type.class, type.toUpperCase());
- }
- }
-
private Type type;
private WindowOffset offset;
private WindowRange range;
@@ -77,4 +69,12 @@ public WindowElement withRange(WindowRange 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
index 843f14150..0303b4f06 100644
--- a/src/main/java/net/sf/jsqlparser/expression/WindowOffset.java
+++ b/src/main/java/net/sf/jsqlparser/expression/WindowOffset.java
@@ -13,14 +13,6 @@
public class WindowOffset implements Serializable {
- public enum Type {
- PRECEDING, FOLLOWING, CURRENT, EXPR;
-
- public static Type from(String type) {
- return Enum.valueOf(Type.class, type.toUpperCase());
- }
- }
-
private Expression expression;
private Type type;
@@ -83,4 +75,12 @@ 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
index 5c2568258..fc5f0af36 100644
--- a/src/main/java/net/sf/jsqlparser/expression/WindowRange.java
+++ b/src/main/java/net/sf/jsqlparser/expression/WindowRange.java
@@ -34,12 +34,10 @@ public void setStart(WindowOffset start) {
@Override
public String toString() {
- StringBuilder buffer = new StringBuilder();
- buffer.append(" BETWEEN");
- buffer.append(start);
- buffer.append(" AND");
- buffer.append(end);
- return buffer.toString();
+ return " BETWEEN" +
+ start +
+ " AND" +
+ end;
}
public WindowRange withStart(WindowOffset start) {
diff --git a/src/main/java/net/sf/jsqlparser/expression/XMLSerializeExpr.java b/src/main/java/net/sf/jsqlparser/expression/XMLSerializeExpr.java
index 89f59242b..d2534e62d 100644
--- a/src/main/java/net/sf/jsqlparser/expression/XMLSerializeExpr.java
+++ b/src/main/java/net/sf/jsqlparser/expression/XMLSerializeExpr.java
@@ -10,7 +10,9 @@
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;
@@ -22,8 +24,8 @@ public class XMLSerializeExpr extends ASTNodeAccessImpl implements Expression {
private ColDataType dataType;
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
public Expression getExpression() {
@@ -49,11 +51,12 @@ public ColDataType getDataType() {
public void setDataType(ColDataType dataType) {
this.dataType = dataType;
}
-
+
@Override
public String toString() {
return "xmlserialize(xmlagg(xmltext(" + expression + ")"
- + (orderByElements != null ? " ORDER BY " + orderByElements.stream().map(item -> item.toString()).collect(joining(", ")) : "")
+ + (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
index cc1d9620e..fca4fd08d 100644
--- a/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/Addition.java
+++ b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/Addition.java
@@ -15,9 +15,15 @@
public class Addition extends BinaryExpression {
+ public Addition() {}
+
+ public Addition(Expression leftExpression, Expression rightExpression) {
+ super(leftExpression, rightExpression);
+ }
+
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
@Override
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
index 54e031bf5..f5eac9390 100644
--- a/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/BitwiseAnd.java
+++ b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/BitwiseAnd.java
@@ -15,9 +15,15 @@
public class BitwiseAnd extends BinaryExpression {
+ public BitwiseAnd() {}
+
+ public BitwiseAnd(Expression leftExpression, Expression rightExpression) {
+ super(leftExpression, rightExpression);
+ }
+
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
@Override
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
index ddd529f2e..0f2094ce9 100644
--- a/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/BitwiseLeftShift.java
+++ b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/BitwiseLeftShift.java
@@ -15,9 +15,15 @@
public class BitwiseLeftShift extends BinaryExpression {
+ public BitwiseLeftShift() {}
+
+ public BitwiseLeftShift(Expression leftExpression, Expression rightExpression) {
+ super(leftExpression, rightExpression);
+ }
+
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
@Override
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
index f381bb8c9..2d11d9e6f 100644
--- a/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/BitwiseOr.java
+++ b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/BitwiseOr.java
@@ -15,9 +15,15 @@
public class BitwiseOr extends BinaryExpression {
+ public BitwiseOr() {}
+
+ public BitwiseOr(Expression leftExpression, Expression rightExpression) {
+ super(leftExpression, rightExpression);
+ }
+
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
@Override
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
index f697cfd06..13b5dbdd3 100644
--- a/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/BitwiseRightShift.java
+++ b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/BitwiseRightShift.java
@@ -15,9 +15,15 @@
public class BitwiseRightShift extends BinaryExpression {
+ public BitwiseRightShift() {}
+
+ public BitwiseRightShift(Expression leftExpression, Expression rightExpression) {
+ super(leftExpression, rightExpression);
+ }
+
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
@Override
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
index d543ee42b..89cbcb64a 100644
--- a/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/BitwiseXor.java
+++ b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/BitwiseXor.java
@@ -15,9 +15,15 @@
public class BitwiseXor extends BinaryExpression {
+ public BitwiseXor() {}
+
+ public BitwiseXor(Expression leftExpression, Expression rightExpression) {
+ super(leftExpression, rightExpression);
+ }
+
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
@Override
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
index 50ad8cf7b..1ad8bb119 100644
--- a/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/Concat.java
+++ b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/Concat.java
@@ -15,9 +15,15 @@
public class Concat extends BinaryExpression {
+ public Concat() {}
+
+ public Concat(Expression leftExpression, Expression rightExpression) {
+ super(leftExpression, rightExpression);
+ }
+
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
@Override
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
index c980d87c3..a963d63f3 100644
--- a/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/Division.java
+++ b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/Division.java
@@ -15,9 +15,15 @@
public class Division extends BinaryExpression {
+ public Division() {}
+
+ public Division(Expression leftExpression, Expression rightExpression) {
+ super(leftExpression, rightExpression);
+ }
+
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
@Override
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
index f9ac96fb6..73489eb8d 100644
--- a/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/IntegerDivision.java
+++ b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/IntegerDivision.java
@@ -15,9 +15,15 @@
public class IntegerDivision extends BinaryExpression {
+ public IntegerDivision() {}
+
+ public IntegerDivision(Expression leftExpression, Expression rightExpression) {
+ super(leftExpression, rightExpression);
+ }
+
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
@Override
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
index ff0ad6eb4..63482d2a2 100644
--- a/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/Modulo.java
+++ b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/Modulo.java
@@ -18,12 +18,15 @@
*/
public class Modulo extends BinaryExpression {
- public Modulo() {
+ public Modulo() {}
+
+ public Modulo(Expression leftExpression, Expression rightExpression) {
+ super(leftExpression, rightExpression);
}
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
@Override
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
index db66fb1d4..774db570b 100644
--- a/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/Multiplication.java
+++ b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/Multiplication.java
@@ -15,9 +15,15 @@
public class Multiplication extends BinaryExpression {
+ public Multiplication() {}
+
+ public Multiplication(Expression leftExpression, Expression rightExpression) {
+ super(leftExpression, rightExpression);
+ }
+
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
@Override
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
index 87615f6c1..dc2b74c7b 100644
--- a/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/Subtraction.java
+++ b/src/main/java/net/sf/jsqlparser/expression/operators/arithmetic/Subtraction.java
@@ -15,9 +15,15 @@
public class Subtraction extends BinaryExpression {
+ public Subtraction() {}
+
+ public Subtraction(Expression leftExpression, Expression rightExpression) {
+ super(leftExpression, rightExpression);
+ }
+
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
@Override
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
index 38152158b..0d9d36377 100644
--- a/src/main/java/net/sf/jsqlparser/expression/operators/conditional/AndExpression.java
+++ b/src/main/java/net/sf/jsqlparser/expression/operators/conditional/AndExpression.java
@@ -25,17 +25,17 @@ public AndExpression(Expression leftExpression, Expression rightExpression) {
setRightExpression(rightExpression);
}
- public void setUseOperator(boolean useOperator) {
- this.useOperator = useOperator;
- }
-
public boolean isUseOperator() {
return useOperator;
}
+ public void setUseOperator(boolean useOperator) {
+ this.useOperator = useOperator;
+ }
+
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
@Override
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
index 11503b475..f08cc7de2 100644
--- a/src/main/java/net/sf/jsqlparser/expression/operators/conditional/OrExpression.java
+++ b/src/main/java/net/sf/jsqlparser/expression/operators/conditional/OrExpression.java
@@ -35,8 +35,8 @@ public OrExpression withRightExpression(Expression expression) {
}
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
@Override
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
index 00e93fc5a..9bab8b53f 100644
--- a/src/main/java/net/sf/jsqlparser/expression/operators/conditional/XorExpression.java
+++ b/src/main/java/net/sf/jsqlparser/expression/operators/conditional/XorExpression.java
@@ -35,8 +35,8 @@ public XorExpression withRightExpression(Expression expression) {
}
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
@Override
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
index e220a1dcb..8998863ef 100644
--- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/Between.java
+++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/Between.java
@@ -22,47 +22,70 @@ public class Between extends ASTNodeAccessImpl implements Expression {
private boolean not = false;
private Expression betweenExpressionStart;
private Expression betweenExpressionEnd;
+ private boolean usingSymmetric = false;
+ private boolean usingAsymmetric = false;
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 setBetweenExpressionEnd(Expression expression) {
- betweenExpressionEnd = expression;
+ public void setNot(boolean b) {
+ not = b;
}
- public void setBetweenExpressionStart(Expression expression) {
- betweenExpressionStart = expression;
+ public boolean isUsingSymmetric() {
+ return usingSymmetric;
}
- public void setLeftExpression(Expression expression) {
- leftExpression = expression;
+ public Between setUsingSymmetric(boolean usingSymmetric) {
+ this.usingSymmetric = usingSymmetric;
+ return this;
}
- public void setNot(boolean b) {
- not = b;
+ public boolean isUsingAsymmetric() {
+ return usingAsymmetric;
+ }
+
+ public Between setUsingAsymmetric(boolean usingAsymmetric) {
+ this.usingAsymmetric = usingAsymmetric;
+ return this;
}
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
@Override
public String toString() {
- return leftExpression + " " + (not ? "NOT " : "") + "BETWEEN " + betweenExpressionStart + " AND "
+ return leftExpression + " " + (not ? "NOT " : "") + "BETWEEN "
+ + (usingSymmetric ? "SYMMETRIC " : "") + (usingAsymmetric ? "ASYMMETRIC " : "")
+ + betweenExpressionStart
+ + " AND "
+ betweenExpressionEnd;
}
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
index b0a5abcbb..15562a408 100644
--- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/ContainedBy.java
+++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ContainedBy.java
@@ -18,8 +18,8 @@ public ContainedBy() {
}
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ 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
index c362dc2be..dbfda1027 100644
--- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/Contains.java
+++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/Contains.java
@@ -18,8 +18,8 @@ public Contains() {
}
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public T accept(ExpressionVisitor expressionVisitor, S context) {
+ return expressionVisitor.visit(this, context);
}
}
diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/CosineSimilarity.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/CosineSimilarity.java
new file mode 100644
index 000000000..00aad962d
--- /dev/null
+++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/CosineSimilarity.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 CosineSimilarity extends ComparisonOperator {
+
+ public CosineSimilarity() {
+ super("<=>");
+ }
+
+ public CosineSimilarity(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/DoubleAnd.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/DoubleAnd.java
index c060cbc52..372e123b1 100644
--- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/DoubleAnd.java
+++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/DoubleAnd.java
@@ -18,7 +18,7 @@ public DoubleAnd() {
}
@Override
- public void accept(ExpressionVisitor expressionVisitor) {
- expressionVisitor.visit(this);
+ public