From decf781a6e3e2009fa33fbab29ea841dcae8729e Mon Sep 17 00:00:00 2001 From: Karen <64801825+karenc-bq@users.noreply.github.com> Date: Wed, 17 Jan 2024 23:08:16 -0800 Subject: [PATCH] chore: upstream changes from 8.0.33 (#536) --- .github/workflows/main.yml | 2 + .github/workflows/pull_request.yml | 7 +- src/build/misc/debian.in/control | 8 +- src/build/misc/debian.in/links | 1 + src/build/misc/debian.in/rules | 4 +- src/build/misc/rpm.spec.in | 73 ++-- .../core-api/java/com/mysql/cj/QueryInfo.java | 113 +++-- .../mysql/cj/conf/PropertyDefinitions.java | 5 +- .../java/com/mysql/cj/conf/PropertyKey.java | 1 + .../com/mysql/cj/protocol/ServerSession.java | 7 +- .../java/com/mysql/cj/NativeSession.java | 2 + .../com/mysql/cj/ServerPreparedQuery.java | 11 +- .../mysql/cj/protocol/a/NativeProtocol.java | 5 +- .../cj/protocol/a/NativeServerSession.java | 18 +- .../cj/protocol/a/ReaderValueEncoder.java | 11 +- .../AuthenticationOciClient.java | 79 +++- .../mysql/cj/protocol/x/XServerSession.java | 12 +- .../cj/LocalizedErrorMessages.properties | 17 +- .../java/com/mysql/cj/xdevapi/Session.java | 11 +- .../com/mysql/cj/jdbc/DatabaseMetaData.java | 95 ++-- .../jdbc/DatabaseMetaDataUsingInfoSchema.java | 6 +- .../regression/MetaDataRegressionTest.java | 413 ++++++++++-------- .../regression/StatementRegressionTest.java | 333 +++++++++++++- .../java/testsuite/simple/StatementsTest.java | 205 ++++++++- 24 files changed, 1061 insertions(+), 378 deletions(-) create mode 100644 src/build/misc/debian.in/links diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5812db65a..04cc9b84c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -23,6 +23,7 @@ jobs: - name: 'Set up JDK 8' uses: actions/setup-java@v4 with: + distribution: 'corretto' java-version: 8 - name: 'Configure AWS Credentials' uses: aws-actions/configure-aws-credentials@v4 @@ -72,6 +73,7 @@ jobs: - name: 'Set up JDK 8' uses: actions/setup-java@v4 with: + distribution: 'corretto' java-version: 8 - name: 'Run Community Tests' run: | diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 1c6dc6a3c..00ddf0d97 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -17,19 +17,20 @@ jobs: runs-on: ubuntu-latest steps: - name: 'Clone Repository' - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 50 - name: 'Set up JDK 8' - uses: actions/setup-java@v1 + uses: actions/setup-java@v4 with: + distribution: 'corretto' java-version: 8 - name: 'Run Community Tests' run: | ./gradlew --no-parallel --no-daemon test-community-docker - name: 'Archive junit results' if: always() - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: 'junit-report' path: build/reports/tests/ diff --git a/src/build/misc/debian.in/control b/src/build/misc/debian.in/control index 691443ac5..00692a5d9 100644 --- a/src/build/misc/debian.in/control +++ b/src/build/misc/debian.in/control @@ -3,15 +3,15 @@ Section: database Priority: optional Maintainer: Oracle MySQL Product Engineering Team Standards-Version: 3.9.2 -Build-Depends: debhelper (>= 8.9.4) +Build-Depends: debhelper (>= 11) Homepage: http://dev.mysql.com/downloads/connector/j/ Package: @PRODUCT_NAME@@PACKAGE_SUFFIX@ Section: database Architecture: all Depends: ${shlibs:Depends}, ${misc:Depends} -Replaces: mysql-connector-java@PACKAGE_SUFFIX@ (<< 8.0.31) -Breaks: mysql-connector-java@PACKAGE_SUFFIX@ (<< 8.0.31) -Provides: mysql-connector-java@PACKAGE_SUFFIX@ +Replaces: @PRODUCT_LEGACY_NAME@@PACKAGE_SUFFIX@ (<< 8.0.31) +Breaks: @PRODUCT_LEGACY_NAME@@PACKAGE_SUFFIX@ (<< 8.0.31) +Provides: @PRODUCT_LEGACY_NAME@@PACKAGE_SUFFIX@ Description: MySQL Connector/J Standardized MySQL database driver for Java diff --git a/src/build/misc/debian.in/links b/src/build/misc/debian.in/links new file mode 100644 index 000000000..a9050d807 --- /dev/null +++ b/src/build/misc/debian.in/links @@ -0,0 +1 @@ +usr/share/java/@PRODUCT_NAME@-@VERSION_FULL@.jar usr/share/java/@PRODUCT_LEGACY_NAME@@PACKAGE_SUFFIX@-@VERSION_FULL@.jar diff --git a/src/build/misc/debian.in/rules b/src/build/misc/debian.in/rules index 5e0308383..9e40df939 100644 --- a/src/build/misc/debian.in/rules +++ b/src/build/misc/debian.in/rules @@ -1,5 +1,5 @@ #!/usr/bin/make -f -# Copyright (c) 2016, 2022, Oracle and/or its affiliates. +# Copyright (c) 2016, 2023, Oracle and/or its affiliates. # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License, version 2.0, as published by the @@ -26,6 +26,8 @@ # this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +export DH_VERBOSE=1 + upstream_version := $(shell dpkg-parsechangelog | sed -n -e'/^Version: / { s/Version: //; s/-[^-]\+$$//; p }') base_version = $(shell echo $(upstream_version) | sed -e's/r[0-9]\+$$//') diff --git a/src/build/misc/rpm.spec.in b/src/build/misc/rpm.spec.in index 29dd19ed4..cb6495714 100644 --- a/src/build/misc/rpm.spec.in +++ b/src/build/misc/rpm.spec.in @@ -1,4 +1,4 @@ -# Copyright (c) 2017, 2022, Oracle and/or its affiliates. +# Copyright (c) 2017, 2023, Oracle and/or its affiliates. # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License, version 2.0, as published by the @@ -25,7 +25,7 @@ # this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -# You can pass these options to "rpmbuild" +# The following options can be passed to "rpmbuild" # # --define="commercial " # --define="with_ant " @@ -40,62 +40,67 @@ # /en/pdf/connector-j.pdf # /en/txt/connector-j.txt -# Some linux distributions doesn't set the "dist" macro. There is a -# list how to identify dists here +# Some linux distributions doesn't set the "dist" macro. There is a list how to identify dists here # https://en.opensuse.org/openSUSE:Build_Service_cross_distribution_howto # SuSE will not set "dist", others do %if 0%{?suse_version} == 1315 -%global dist .sles12 -%global sles12 1 +%global dist .sles12 +%global sles12 1 %endif %if 0%{?suse_version} == 1500 -%global dist .sl15 -%global sles15 1 +%global dist .sl15 +%global sles15 1 %endif +%global legacy_name @MYSQL_CJ_EXTENDED_LEGACY_PROD_NAME@@MYSQL_CJ_RPM_PACKAGE_SUFFIX@ + %if 0%{?commercial:1} -%global lic_tag Commercial +%global legacy_name_community @MYSQL_CJ_EXTENDED_LEGACY_PROD_NAME@ +%global name_community @MYSQL_CJ_EXTENDED_PROD_NAME@ +%global lic_tag Commercial %else -%global lic_tag GPLv2 +%global lic_tag GPLv2 %endif -Summary: Standardized MySQL database driver for Java -Name: @MYSQL_CJ_EXTENDED_PROD_NAME@@MYSQL_CJ_RPM_PACKAGE_SUFFIX@ -Version: @MYSQL_CJ_VERSION_NUMERIC@ -Release: @MYSQL_CJ_RPM_RELEASE_FULL@%{?dist} -Epoch: 1 -License: %{lic_tag} -Group: Development/Libraries -URL: http://dev.mysql.com/downloads/connector/j/ -Source0: https://cdn.mysql.com/Downloads/Connector-J/@MYSQL_CJ_FULL_PROD_NAME@.tar.gz +Summary: Standardized MySQL database driver for Java +Name: @MYSQL_CJ_EXTENDED_PROD_NAME@@MYSQL_CJ_RPM_PACKAGE_SUFFIX@ +Version: @MYSQL_CJ_VERSION_NUMERIC@ +Release: @MYSQL_CJ_RPM_RELEASE_FULL@%{?dist} +Epoch: 1 +License: %{lic_tag} +Group: Development/Libraries +URL: http://dev.mysql.com/downloads/connector/j/ +Source0: https://cdn.mysql.com/Downloads/Connector-J/@MYSQL_CJ_FULL_PROD_NAME@.tar.gz -BuildRoot: %{_tmppath}/%{name}-%{version}-build -BuildArch: noarch +BuildRoot: %{_tmppath}/%{name}-%{version}-build +BuildArch: noarch -Obsoletes: mysql-connector-java@MYSQL_CJ_RPM_PACKAGE_SUFFIX@ < %{version}-%{release} -Provides: mysql-connector-java@MYSQL_CJ_RPM_PACKAGE_SUFFIX@ = %{version}-%{release} +# Handle package renaming in 8.0.31 +Obsoletes: %{legacy_name} < 1:8.0.31 +Provides: %{legacy_name} = %{epoch}:%{version}-%{release} +# Commercial obsoletes community, incl. package renaming in 8.0.31 %if 0%{?commercial:1} -Obsoletes: mysql-connector-java < %{version}-%{release} -Provides: mysql-connector-java = %{version}-%{release} -Obsoletes: @MYSQL_CJ_EXTENDED_PROD_NAME@ < %{version}-%{release} -Provides: @MYSQL_CJ_EXTENDED_PROD_NAME@ = %{version}-%{release} +Obsoletes: %{name_community} < %{epoch}:%{version}-%{release} +Provides: %{name_community} = %{epoch}:%{version}-%{release} +Obsoletes: %{legacy_name_community} < 1:8.0.31 +Provides: %{legacy_name_community} = %{epoch}:%{version}-%{release} %endif %if 0%{!?with_ant:1} -BuildRequires: ant +BuildRequires: ant %endif %if 0%{!?with_java:1} -BuildRequires: java-devel >= 1:1.8.0 +BuildRequires: java-devel >= 1:1.8.0 %endif %if 0%{?sles12:1} || 0%{?sles15:1} -Requires: java-headless >= 1.8.0 +Requires: java-headless >= 1.8.0 %else -Requires: java-headless >= 1:1.8.0 +Requires: java-headless >= 1:1.8.0 %endif %description @@ -175,6 +180,11 @@ cp %{with_docs}/en/txt/connector-j.txt package-content/docs/ install -d -m 0755 %{buildroot}%{_javadir} install -p -m 0644 package-content/@MYSQL_CJ_FULL_PROD_NAME@.jar %{buildroot}%{_javadir}/%{name}.jar +# To make it easier for users, provide a soft link with the old +# "mysql-connector-java.jar" name. We can't use "install", it will +# dereference the link +ln -s %{name}.jar %{buildroot}%{_javadir}/%{legacy_name}.jar + %clean rm -rf %{buildroot} @@ -195,6 +205,7 @@ rm -rf %{buildroot} %endif %{_javadir}/%{name}.jar +%{_javadir}/%{legacy_name}.jar %changelog * Mon Nov 27 2017 MySQL Release Engineering - 8.0.9-1 diff --git a/src/main/core-api/java/com/mysql/cj/QueryInfo.java b/src/main/core-api/java/com/mysql/cj/QueryInfo.java index 6ba7c2761..606be68e2 100644 --- a/src/main/core-api/java/com/mysql/cj/QueryInfo.java +++ b/src/main/core-api/java/com/mysql/cj/QueryInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022, Oracle and/or its affiliates. + * Copyright (c) 2017, 2023, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -134,16 +134,26 @@ public QueryInfo(String sql, Session session, String encoding) { int generalEndpointStart = 0; int valuesEndpointStart = 0; int valuesClauseBegin = -1; + boolean valuesClauseBeginFound = false; int valuesClauseEnd = -1; + boolean valuesClauseEndFound = false; boolean withinValuesClause = false; + boolean valueStrMayBeTableName = true; int parensLevel = 0; int matchEnd = -1; + int lastPos = -1; + char lastChar = 0; // Endpoints for the satement's static sections (parts around placeholders). ArrayList staticEndpoints = new ArrayList<>(); while (strInspector.indexOfNextChar() != -1) { - if (strInspector.getChar() == '?') { // Process placeholder. + int currPos = strInspector.getPosition(); + char currChar = strInspector.getChar(); + + if (currChar == '?') { // Process placeholder. + valueStrMayBeTableName = false; // At this point a string "VALUE" cannot be a table name. + this.numberOfPlaceholders++; int endpointEnd = strInspector.getPosition(); staticEndpoints.add(generalEndpointStart); @@ -152,9 +162,9 @@ public QueryInfo(String sql, Session session, String encoding) { generalEndpointStart = strInspector.getPosition(); // Next section starts after the placeholder. if (rewritableAsMultiValues) { - if (valuesClauseBegin == -1) { // There's a placeholder before the VALUES clause. + if (!valuesClauseBeginFound) { // There's a placeholder before the VALUES clause. rewritableAsMultiValues = false; - } else if (valuesClauseEnd != -1) { // There's a placeholder after the end of the VALUES clause. + } else if (valuesClauseEndFound) { // There's a placeholder after the end of the VALUES clause. rewritableAsMultiValues = false; } else if (withinValuesClause) { this.valuesEndpoints.add(valuesEndpointStart); @@ -163,14 +173,18 @@ public QueryInfo(String sql, Session session, String encoding) { } } - } else if (strInspector.getChar() == ';') { // Multi-query SQL. + } else if (currChar == ';') { // Multi-query SQL. + valueStrMayBeTableName = false; // At this point a string "VALUE" cannot be a table name. + strInspector.incrementPosition(); if (strInspector.indexOfNextNonWsChar() != -1) { this.numberOfQueries++; if (rewritableAsMultiValues) { rewritableAsMultiValues = false; + valuesClauseBeginFound = false; valuesClauseBegin = -1; + valuesClauseEndFound = false; valuesClauseEnd = -1; withinValuesClause = false; parensLevel = 0; @@ -182,38 +196,55 @@ public QueryInfo(String sql, Session session, String encoding) { } else { isInsert = strInspector.matchesIgnoreCase(INSERT_STATEMENT) != -1; if (isInsert) { - strInspector.incrementPosition(INSERT_STATEMENT.length()); // Advance to the end of "INSERT". + strInspector.incrementPosition(INSERT_STATEMENT.length() - 1); // Advance to the end of "INSERT" and capture last character. + currPos = strInspector.getPosition(); + currChar = strInspector.getChar(); + strInspector.incrementPosition(); } lookForOnDuplicateKeyUpdate = isInsert; } } } else { - int currPos = strInspector.getPosition(); - if (rewritableAsMultiValues) { - if (valuesClauseBegin == -1 && strInspector.matchesIgnoreCase(VALUE_CLAUSE) != -1) { // VALUE(S) clause found. - strInspector.incrementPosition(VALUE_CLAUSE.length()); // Advance to the end of "VALUE". + if ((!valuesClauseBeginFound || valueStrMayBeTableName) && strInspector.matchesIgnoreCase(VALUE_CLAUSE) != -1) { // VALUE(S) clause found. + boolean leftBound = currPos > lastPos + 1 || lastChar == ')'; // ')' would mark the ending of the columns list. + + strInspector.incrementPosition(VALUE_CLAUSE.length() - 1); // Advance to the end of "VALUE" and capture last character. + currPos = strInspector.getPosition(); + currChar = strInspector.getChar(); + strInspector.incrementPosition(); boolean matchedValues = false; if (strInspector.matchesIgnoreCase("S") != -1) { // Check for the "S" in "VALUE(S)" and advance 1 more character if needed. + currPos = strInspector.getPosition(); + currChar = strInspector.getChar(); strInspector.incrementPosition(); matchedValues = true; } - if (matchedValues && this.containsOnDuplicateKeyUpdate) { // VALUES after ODKU is a function, not a clause. - rewritableAsMultiValues = false; - } else { - withinValuesClause = true; - strInspector.indexOfNextChar(); // Position on the first values list character. - valuesClauseBegin = strInspector.getPosition(); - valuesEndpointStart = valuesClauseBegin; + int endPos = strInspector.getPosition(); + int nextPos = strInspector.indexOfNextChar(); // Position on the first meaningful character after VALUE(S). + boolean rightBound = nextPos > endPos || strInspector.getChar() == '('; // '(' would mark the beginning of the VALUE(S) list. + + if (leftBound && rightBound) { // VALUE(S) keyword must not be part of another string, such as a table or column name. + if (matchedValues) { + valueStrMayBeTableName = false; // At this point a string "VALUE" cannot be a table name. + } + if (matchedValues && this.containsOnDuplicateKeyUpdate) { // VALUES after ODKU is a function, not a clause. + rewritableAsMultiValues = false; + } else { + withinValuesClause = true; + valuesClauseBegin = strInspector.getPosition(); + valuesClauseBeginFound = true; + valuesEndpointStart = valuesClauseBegin; + } } - } else if (withinValuesClause && strInspector.getChar() == '(') { + } else if (withinValuesClause && currChar == '(') { parensLevel++; strInspector.incrementPosition(); - } else if (withinValuesClause && strInspector.getChar() == ')') { + } else if (withinValuesClause && currChar == ')') { parensLevel--; if (parensLevel < 0) { parensLevel = 0; // Keep going, not checking for syntax validity. @@ -222,22 +253,34 @@ public QueryInfo(String sql, Session session, String encoding) { valuesClauseEnd = strInspector.getPosition(); // It may not be the end of the VALUES clause yet but save it for later. } else if (withinValuesClause && parensLevel == 0 && isInsert && strInspector.matchesIgnoreCase(AS_CLAUSE) != -1) { // End of VALUES clause. + valueStrMayBeTableName = false; // At this point a string "VALUE" cannot be a table name. + if (valuesClauseEnd == -1) { valuesClauseEnd = strInspector.getPosition(); } + valuesClauseEndFound = true; withinValuesClause = false; - strInspector.incrementPosition(AS_CLAUSE.length()); // Advance to the end of "AS". + strInspector.incrementPosition(AS_CLAUSE.length() - 1); // Advance to the end of "AS" and capture last character. + currPos = strInspector.getPosition(); + currChar = strInspector.getChar(); + strInspector.incrementPosition(); this.valuesEndpoints.add(valuesEndpointStart); this.valuesEndpoints.add(valuesClauseEnd); } else if (withinValuesClause && parensLevel == 0 && isInsert // && (matchEnd = strInspector.matchesIgnoreCase(ODKU_CLAUSE)) != -1) { // End of VALUES clause. + valueStrMayBeTableName = false; // At this point a string "VALUE" cannot be a table name. + if (valuesClauseEnd == -1) { valuesClauseEnd = strInspector.getPosition(); } + valuesClauseEndFound = true; withinValuesClause = false; - strInspector.incrementPosition(matchEnd - strInspector.getPosition()); // Advance to the end of "ON DUPLICATE KEY UPDATE". + strInspector.incrementPosition(matchEnd - strInspector.getPosition() - 1); // Advance to the end of "ODKU" and capture last character. + currPos = strInspector.getPosition(); + currChar = strInspector.getChar(); + strInspector.incrementPosition(); this.valuesEndpoints.add(valuesEndpointStart); this.valuesEndpoints.add(valuesClauseEnd); @@ -247,12 +290,18 @@ public QueryInfo(String sql, Session session, String encoding) { } else if (strInspector.matchesIgnoreCase(LAST_INSERT_ID_FUNC) != -1) { // Can't rewrite as multi-values if LAST_INSERT_ID function is used. rewritableAsMultiValues = false; - strInspector.incrementPosition(LAST_INSERT_ID_FUNC.length()); // Advance to the end of "LAST_INSERT_ID". + strInspector.incrementPosition(LAST_INSERT_ID_FUNC.length() - 1); // Advance to the end of "LAST_INSERT_ID" and capture last character. + currPos = strInspector.getPosition(); + currChar = strInspector.getChar(); + strInspector.incrementPosition(); } } if (lookForOnDuplicateKeyUpdate && currPos == strInspector.getPosition() && (matchEnd = strInspector.matchesIgnoreCase(ODKU_CLAUSE)) != -1) { - strInspector.incrementPosition(matchEnd - strInspector.getPosition()); // Advance to the end of "ON DUPLICATE KEY UPDATE". + strInspector.incrementPosition(matchEnd - strInspector.getPosition() - 1); // Advance to the end of "ODKU" and capture last character. + currPos = strInspector.getPosition(); + currChar = strInspector.getChar(); + strInspector.incrementPosition(); this.containsOnDuplicateKeyUpdate = true; lookForOnDuplicateKeyUpdate = false; @@ -262,18 +311,26 @@ public QueryInfo(String sql, Session session, String encoding) { strInspector.incrementPosition(); } } + + lastPos = currPos; + lastChar = currChar; } staticEndpoints.add(generalEndpointStart); staticEndpoints.add(this.queryLength); if (rewritableAsMultiValues) { - if (withinValuesClause) { + if (!valuesClauseEndFound) { + if (valuesClauseEnd == -1) { + valuesClauseEnd = this.queryLength; + } + valuesClauseEndFound = true; withinValuesClause = false; + this.valuesEndpoints.add(valuesEndpointStart); - this.valuesEndpoints.add(valuesClauseEnd != -1 ? valuesClauseEnd : this.queryLength); + this.valuesEndpoints.add(valuesClauseEnd); } - if (valuesClauseBegin != -1) { - this.valuesClauseLength = (valuesClauseEnd != -1 ? valuesClauseEnd : this.queryLength) - valuesClauseBegin; + if (valuesClauseBeginFound && valuesClauseEndFound) { + this.valuesClauseLength = valuesClauseEnd - valuesClauseBegin; } else { rewritableAsMultiValues = false; } @@ -698,7 +755,7 @@ private static String getContextForWithStatement(String sql, boolean noBackslash if (!asFound && section.equalsIgnoreCase(AS_CLAUSE)) { asFound = true; // Since the subquery part is skipped, this must be followed by a "," or the context statement. } else if (asFound) { - if (section.equalsIgnoreCase(",")) { + if (section.charAt(0) == ',') { asFound = false; // Another CTE is expected. } else { return section; diff --git a/src/main/core-api/java/com/mysql/cj/conf/PropertyDefinitions.java b/src/main/core-api/java/com/mysql/cj/conf/PropertyDefinitions.java index 5389bd326..d8a59b34d 100644 --- a/src/main/core-api/java/com/mysql/cj/conf/PropertyDefinitions.java +++ b/src/main/core-api/java/com/mysql/cj/conf/PropertyDefinitions.java @@ -210,8 +210,11 @@ public enum DatabaseTerm { new StringPropertyDefinition(PropertyKey.ociConfigFile, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE, Messages.getString("ConnectionProperties.ociConfigFile"), "8.0.27", CATEGORY_AUTH, Integer.MIN_VALUE + 7), + new StringPropertyDefinition(PropertyKey.ociConfigProfile, "DEFAULT", RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.ociConfigProfile"), "8.0.33", CATEGORY_AUTH, Integer.MIN_VALUE + 8), + new StringPropertyDefinition(PropertyKey.authenticationFidoCallbackHandler, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE, - Messages.getString("ConnectionProperties.authenticationFidoCallbackHandler"), "8.0.29", CATEGORY_AUTH, Integer.MIN_VALUE + 8), + Messages.getString("ConnectionProperties.authenticationFidoCallbackHandler"), "8.0.29", CATEGORY_AUTH, Integer.MIN_VALUE + 9), // // CATEGORY_CONNECTION diff --git a/src/main/core-api/java/com/mysql/cj/conf/PropertyKey.java b/src/main/core-api/java/com/mysql/cj/conf/PropertyKey.java index cc0ad23e4..1284beb86 100644 --- a/src/main/core-api/java/com/mysql/cj/conf/PropertyKey.java +++ b/src/main/core-api/java/com/mysql/cj/conf/PropertyKey.java @@ -168,6 +168,7 @@ public enum PropertyKey { noDatetimeStringSync("noDatetimeStringSync", true), // nullDatabaseMeansCurrent("nullDatabaseMeansCurrent", "nullCatalogMeansCurrent", true), // ociConfigFile("ociConfigFile", true), // + ociConfigProfile("ociConfigProfile", true), // overrideSupportsIntegrityEnhancementFacility("overrideSupportsIntegrityEnhancementFacility", true), // packetDebugBufferSize("packetDebugBufferSize", true), // padCharsWithSpace("padCharsWithSpace", true), // diff --git a/src/main/core-api/java/com/mysql/cj/protocol/ServerSession.java b/src/main/core-api/java/com/mysql/cj/protocol/ServerSession.java index 1fdea1840..5cf347c54 100644 --- a/src/main/core-api/java/com/mysql/cj/protocol/ServerSession.java +++ b/src/main/core-api/java/com/mysql/cj/protocol/ServerSession.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2021, Oracle and/or its affiliates. + * Copyright (c) 2015, 2023, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -87,12 +87,7 @@ public interface ServerSession { */ void setStatusFlags(int statusFlags, boolean saveOldStatusFlags); - int getOldStatusFlags(); - - void setOldStatusFlags(int statusFlags); - /** - * * @return TRANSACTION_NOT_STARTED, TRANSACTION_IN_PROGRESS, TRANSACTION_STARTED or TRANSACTION_COMPLETED */ int getTransactionState(); diff --git a/src/main/core-impl/java/com/mysql/cj/NativeSession.java b/src/main/core-impl/java/com/mysql/cj/NativeSession.java index 033d89d9b..68214e2a5 100644 --- a/src/main/core-impl/java/com/mysql/cj/NativeSession.java +++ b/src/main/core-impl/java/com/mysql/cj/NativeSession.java @@ -193,11 +193,13 @@ public void forceClose() { public void enableMultiQueries() { this.protocol.sendCommand(this.commandBuilder.buildComSetOption(((NativeProtocol) this.protocol).getSharedSendPacket(), 0), false, 0); + // OK_PACKET returned in previous sendCommand() was not processed so keep original transaction state. ((NativeServerSession) getServerSession()).preserveOldTransactionState(); } public void disableMultiQueries() { this.protocol.sendCommand(this.commandBuilder.buildComSetOption(((NativeProtocol) this.protocol).getSharedSendPacket(), 1), false, 0); + // OK_PACKET returned in previous sendCommand() was not processed so keep original transaction state. ((NativeServerSession) getServerSession()).preserveOldTransactionState(); } diff --git a/src/main/core-impl/java/com/mysql/cj/ServerPreparedQuery.java b/src/main/core-impl/java/com/mysql/cj/ServerPreparedQuery.java index 61241bedf..efaffdc1a 100644 --- a/src/main/core-impl/java/com/mysql/cj/ServerPreparedQuery.java +++ b/src/main/core-impl/java/com/mysql/cj/ServerPreparedQuery.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022, Oracle and/or its affiliates. + * Copyright (c) 2017, 2023, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -113,7 +113,6 @@ protected ServerPreparedQuery(NativeSession sess) { } /** - * * @param sql * query string * @throws IOException @@ -160,9 +159,9 @@ public void serverPrepare(String sql) throws IOException { // Read in the result set column information if (fieldCount > 0) { this.resultFields = this.session.getProtocol().read(ColumnDefinition.class, new ColumnDefinitionFactory(fieldCount, null)); - } - if (checkEOF && this.session.getProtocol().probeMessage(null).isEOFPacket()) { // Skip the following EOF packet. - this.session.getProtocol().skipPacket(); + if (checkEOF && this.session.getProtocol().probeMessage(null).isEOFPacket()) { // Skip the following EOF packet. + this.session.getProtocol().skipPacket(); + } } } } @@ -573,6 +572,8 @@ public void serverResetStatement() { this.session.getProtocol().sendCommand(this.commandBuilder.buildComStmtReset(this.session.getSharedSendPacket(), this.serverStatementId), false, 0); } finally { + // OK_PACKET returned in previous sendCommand() was not processed so keep original transaction state. + this.session.getProtocol().getServerSession().preserveOldTransactionState(); this.session.clearInputStream(); } } diff --git a/src/main/protocol-impl/java/com/mysql/cj/protocol/a/NativeProtocol.java b/src/main/protocol-impl/java/com/mysql/cj/protocol/a/NativeProtocol.java index d1d3106a5..94c5ab557 100644 --- a/src/main/protocol-impl/java/com/mysql/cj/protocol/a/NativeProtocol.java +++ b/src/main/protocol-impl/java/com/mysql/cj/protocol/a/NativeProtocol.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. + * Copyright (c) 2015, 2023, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -30,6 +30,7 @@ package com.mysql.cj.protocol.a; import java.io.BufferedInputStream; +import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; @@ -1881,7 +1882,7 @@ private BufferedInputStream getFileStream(String fileName) throws IOException { } } } - return new BufferedInputStream(new FileInputStream(fileName)); + return new BufferedInputStream(new FileInputStream(new File(fileName).getCanonicalFile())); } // Given the code paths above, allowLoadLocaInfileInPath.isExplicitlySet() must be true and restrictions to "LOAD DATA LOCAL INFILE" apply. diff --git a/src/main/protocol-impl/java/com/mysql/cj/protocol/a/NativeServerSession.java b/src/main/protocol-impl/java/com/mysql/cj/protocol/a/NativeServerSession.java index 1a53376df..38c6ba779 100644 --- a/src/main/protocol-impl/java/com/mysql/cj/protocol/a/NativeServerSession.java +++ b/src/main/protocol-impl/java/com/mysql/cj/protocol/a/NativeServerSession.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. + * Copyright (c) 2015, 2023, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -131,20 +131,12 @@ public void setStatusFlags(int statusFlags) { @Override public void setStatusFlags(int statusFlags, boolean saveOldStatus) { + int currentStatusFlags = this.statusFlags; + this.statusFlags = statusFlags; if (saveOldStatus) { - this.oldStatusFlags = this.statusFlags; + this.oldStatusFlags = currentStatusFlags; + preserveOldTransactionState(); } - this.statusFlags = statusFlags; - } - - @Override - public int getOldStatusFlags() { - return this.oldStatusFlags; - } - - @Override - public void setOldStatusFlags(int oldStatusFlags) { - this.oldStatusFlags = oldStatusFlags; } @Override diff --git a/src/main/protocol-impl/java/com/mysql/cj/protocol/a/ReaderValueEncoder.java b/src/main/protocol-impl/java/com/mysql/cj/protocol/a/ReaderValueEncoder.java index 79c4eda06..0af2afbd6 100644 --- a/src/main/protocol-impl/java/com/mysql/cj/protocol/a/ReaderValueEncoder.java +++ b/src/main/protocol-impl/java/com/mysql/cj/protocol/a/ReaderValueEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -66,20 +66,23 @@ protected byte[] readBytes(Reader reader, BindValue binding) { byte[] bytes; boolean useLength = this.propertySet.getBooleanProperty(PropertyKey.useStreamLengthsInPrepStmts).getValue(); - String forcedEncoding = binding.isNational() ? null : this.propertySet.getStringProperty(PropertyKey.clobCharacterEncoding).getStringValue(); + String clobEncoding = binding.isNational() ? null : this.propertySet.getStringProperty(PropertyKey.clobCharacterEncoding).getStringValue(); + if (clobEncoding == null) { + clobEncoding = this.charEncoding.getStringValue(); + } long scaleOrLength = binding.getScaleOrLength(); if (useLength && (scaleOrLength != -1)) { c = new char[(int) scaleOrLength]; int numCharsRead = Util.readFully(reader, c, (int) scaleOrLength); // blocks until all read - bytes = StringUtils.getBytes(new String(c, 0, numCharsRead), forcedEncoding); + bytes = StringUtils.getBytes(new String(c, 0, numCharsRead), clobEncoding); } else { c = new char[4096]; StringBuilder buf = new StringBuilder(); while ((len = reader.read(c)) != -1) { buf.append(c, 0, len); } - bytes = StringUtils.getBytes(buf.toString(), forcedEncoding); + bytes = StringUtils.getBytes(buf.toString(), clobEncoding); } return escapeBytesIfNeeded(bytes); diff --git a/src/main/protocol-impl/java/com/mysql/cj/protocol/a/authentication/AuthenticationOciClient.java b/src/main/protocol-impl/java/com/mysql/cj/protocol/a/authentication/AuthenticationOciClient.java index 473e532f7..4fe5d329f 100644 --- a/src/main/protocol-impl/java/com/mysql/cj/protocol/a/authentication/AuthenticationOciClient.java +++ b/src/main/protocol-impl/java/com/mysql/cj/protocol/a/authentication/AuthenticationOciClient.java @@ -32,6 +32,7 @@ import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.security.interfaces.RSAPrivateKey; import java.util.Base64; @@ -62,8 +63,11 @@ public class AuthenticationOciClient implements AuthenticationPlugin protocol = null; private MysqlCallbackHandler usernameCallbackHandler = null; - private String fingerprint = null; + private String configFingerprint = null; + private String configKeyFile = null; + private String configSecurityTokenFile = null; private RSAPrivateKey privateKey = null; + private byte[] token = null; @Override public void init(Protocol prot, MysqlCallbackHandler cbh) { @@ -73,7 +77,7 @@ public void init(Protocol prot, MysqlCallbackHandler cbh) { @Override public void reset() { - this.fingerprint = null; + this.configFingerprint = null; this.privateKey = null; } @@ -123,55 +127,92 @@ public boolean nextAuthenticationStep(NativePacketPayload fromServer, List 10240) { // Fail if above 10KB. + throw ExceptionFactory.createException(Messages.getString("AuthenticationOciClientPlugin.SecurityTokenTooBig")); + } + this.token = Files.readAllBytes(Paths.get(this.configSecurityTokenFile)); + } catch (IOException e) { + throw ExceptionFactory.createException(Messages.getString("AuthenticationOciClientPlugin.FailedReadingSecurityTokenFile"), e); + } + } } diff --git a/src/main/protocol-impl/java/com/mysql/cj/protocol/x/XServerSession.java b/src/main/protocol-impl/java/com/mysql/cj/protocol/x/XServerSession.java index 61461b63a..1f59fc103 100644 --- a/src/main/protocol-impl/java/com/mysql/cj/protocol/x/XServerSession.java +++ b/src/main/protocol-impl/java/com/mysql/cj/protocol/x/XServerSession.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. + * Copyright (c) 2018, 2023, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -70,16 +70,6 @@ public void setStatusFlags(int statusFlags, boolean saveOldStatusFlags) { throw ExceptionFactory.createException(CJOperationNotSupportedException.class, "Not supported"); } - @Override - public int getOldStatusFlags() { - throw ExceptionFactory.createException(CJOperationNotSupportedException.class, "Not supported"); - } - - @Override - public void setOldStatusFlags(int statusFlags) { - throw ExceptionFactory.createException(CJOperationNotSupportedException.class, "Not supported"); - } - @Override public int getTransactionState() { throw ExceptionFactory.createException(CJOperationNotSupportedException.class, "Not supported"); diff --git a/src/main/resources/com/mysql/cj/LocalizedErrorMessages.properties b/src/main/resources/com/mysql/cj/LocalizedErrorMessages.properties index 97543d459..9587111eb 100644 --- a/src/main/resources/com/mysql/cj/LocalizedErrorMessages.properties +++ b/src/main/resources/com/mysql/cj/LocalizedErrorMessages.properties @@ -51,11 +51,17 @@ AuthenticationLdapSaslClientPlugin.MissingLdapServerHostname=An LDAP Server host AuthenticationLdapSaslClientPlugin.FailCreateSaslClient=Failed creating a SASL client for the authentication mechanism ''{0}''. AuthenticationLdapSaslClientPlugin.ErrProcessingAuthIter=Error while processing an authentication iteration for the authentication mechanism ''{0}''. -AuthenticationOciClientPlugin.SdkNotFound=The OCI SDK could not be found or is not installed. -AuthenticationOciClientPlugin.OciConfigFileError=OCI configuration file could not be read. -AuthenticationOciClientPlugin.OciConfigFileMissingEntry=OCI configuration file does not contain a ''fingerprint'' or ''key_file'' entry. -AuthenticationOciClientPlugin.PrivateKeyNotFound=Private key could not be found at location given by OCI configuration entry ''key_file''. -AuthenticationOciClientPlugin.PrivateKeyNotValid=OCI configuration entry ''key_file'' does not reference a valid key file. +AuthenticationOciClientPlugin.ConfigFileNotFound=OCI configuration file not found. +AuthenticationOciClientPlugin.OciSdkNotFound=The OCI SDK cannot be found or it is not installed. +AuthenticationOciClientPlugin.OciConfigFileError=The OCI configuration file cannot be read. +AuthenticationOciClientPlugin.ProfileNotFound=The specified profile is not found in the OCI configuration file. +AuthenticationOciClientPlugin.OciConfigFileMissingEntry=The OCI configuration file does not contain a ''fingerprint'' or ''key_file'' entry. +AuthenticationOciClientPlugin.PrivateKeyNotFound=The private key cannot be found at the location referenced in the OCI configuration entry ''key_file''. +AuthenticationOciClientPlugin.FailedReadingPrivateKey=Failed reading the private key file referenced in the OCI configuration entry ''key_file''. +AuthenticationOciClientPlugin.PrivateKeyNotValid=The OCI configuration entry ''key_file'' does not reference a valid key file. +AuthenticationOciClientPlugin.SecurityTokenFileNotFound=The security token file cannot be found at the location referenced in the OCI configuration entry ''security_token_file''. +AuthenticationOciClientPlugin.SecurityTokenTooBig=Invalid security token file. File size above 10 KB. +AuthenticationOciClientPlugin.FailedReadingSecurityTokenFile=Failed reading the security token file referenced in the OCI configuration entry ''security_token_file''. AuthenticationProvider.BadDefaultAuthenticationPlugin=Improper value "{0}" for property ''defaultAuthenticationPlugin''. AuthenticationProvider.DefaultAuthenticationPluginIsNotListed=Default authentication plugin "{0}" is neither one of the built-in plugins nor one of the plugins listed in ''authenticationPlugins''. @@ -912,6 +918,7 @@ ConnectionProperties.noAccessToProcedureBodies=When determining procedure parame ConnectionProperties.noDatetimeStringSync=Don''t ensure that ''ResultSet.getTimestamp().toString().equals(ResultSet.getString())''. ConnectionProperties.nullCatalogMeansCurrent=In ''DatabaseMetaData'' methods that take a ''catalog'' or ''schema'' parameter, does the value "null" mean to use the current database? See also the property ''databaseTerm''. ConnectionProperties.ociConfigFile=The location of the OCI configuration file as required by the OCI SDK for Java. Default value is "~/.oci/config" for Unix-like systems and "%HOMEDRIVE%%HOMEPATH%.oci\\config" for Windows. +ConnectionProperties.ociConfigProfile=The profile in the OCI configuration file specified in ''ociConfigFile'', from where the configuration to use in the ''authentication_oci_client'' authentication plugin is to be read. ConnectionProperties.overrideSupportsIEF=Should the driver return "true" for ''DatabaseMetaData.supportsIntegrityEnhancementFacility()'' even if the database doesn''t support it to workaround applications that require this method to return "true" to signal support of foreign keys, even though the SQL specification states that this facility contains much more than just foreign key support (one such application being OpenOffice)? ConnectionProperties.packetDebugBufferSize=The maximum number of packets to retain when ''enablePacketDebug'' is "true". ConnectionProperties.padCharsWithSpace=If a result set column has the CHAR type and the value does not fill the amount of characters specified in the DDL for the column, should the driver pad the remaining characters with space (for ANSI compliance)? diff --git a/src/main/user-api/java/com/mysql/cj/xdevapi/Session.java b/src/main/user-api/java/com/mysql/cj/xdevapi/Session.java index fe579d772..d754901b7 100644 --- a/src/main/user-api/java/com/mysql/cj/xdevapi/Session.java +++ b/src/main/user-api/java/com/mysql/cj/xdevapi/Session.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. + * Copyright (c) 2015, 2023, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -93,16 +93,17 @@ public interface Session { Schema getSchema(String schemaName); /** - * Retrieve the default schema name which may be configured at connect time. + * Retrieve the default schema name, which might have been configured at the time of connection. Returns empty String if no default schema has been set. * - * @return default schema name + * @return default schema name or empty String if no default schema has been set */ String getDefaultSchemaName(); /** - * Retrieve the default schema which may be configured at connect time. + * Retrieve the default schema name, which might have been configured at the time of connection. Returns null if no default schema has been + * set. * - * @return default {@link Schema} + * @return default {@link Schema} or null if no default schema has been set */ Schema getDefaultSchema(); diff --git a/src/main/user-impl/java/com/mysql/cj/jdbc/DatabaseMetaData.java b/src/main/user-impl/java/com/mysql/cj/jdbc/DatabaseMetaData.java index bffbb53ab..a71aa206d 100644 --- a/src/main/user-impl/java/com/mysql/cj/jdbc/DatabaseMetaData.java +++ b/src/main/user-impl/java/com/mysql/cj/jdbc/DatabaseMetaData.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2022, Oracle and/or its affiliates. + * Copyright (c) 2002, 2023, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -2964,7 +2964,6 @@ protected Field[] getPrimaryKeysFields() { @Override public java.sql.ResultSet getPrimaryKeys(String catalog, String schema, final String table) throws SQLException { - if (table == null) { throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.2"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); @@ -2977,14 +2976,12 @@ public java.sql.ResultSet getPrimaryKeys(String catalog, String schema, final St final Statement stmt = this.conn.getMetadataSafeStatement(); try { - new IterateBlock(getDatabaseIterator(db)) { @Override void forEach(String dbStr) throws SQLException { ResultSet rs = null; try { - StringBuilder queryBuf = new StringBuilder("SHOW KEYS FROM "); queryBuf.append(StringUtils.quoteIdentifier(table, DatabaseMetaData.this.quotedId, DatabaseMetaData.this.pedantic)); queryBuf.append(" FROM "); @@ -3002,8 +2999,7 @@ void forEach(String dbStr) throws SQLException { } } - TreeMap sortMap = new TreeMap<>(); - + TreeMap sortMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); while (rs != null && rs.next()) { String keyType = rs.getString("Key_name"); @@ -3036,7 +3032,6 @@ void forEach(String dbStr) throws SQLException { rs.close(); } catch (Exception ex) { } - rs = null; } } @@ -4060,49 +4055,73 @@ public java.sql.ResultSet getTypeInfo() throws SQLException { ArrayList tuples = new ArrayList<>(); + // Ordered by DATA_TYPE and then by how closely the data type maps to the corresponding JDBC SQL type. + // java.sql.Types.BIT = -7 + tuples.add(new ByteArrayRow(getTypeInfo("BIT"), getExceptionInterceptor())); + // java.sql.Types.TINYINT = -6 + tuples.add(new ByteArrayRow(getTypeInfo("TINYINT"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("TINYINT UNSIGNED"), getExceptionInterceptor())); + // java.sql.Types.BIGINT = -5 tuples.add(new ByteArrayRow(getTypeInfo("BIGINT"), getExceptionInterceptor())); tuples.add(new ByteArrayRow(getTypeInfo("BIGINT UNSIGNED"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("BINARY"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("BIT"), getExceptionInterceptor())); + // java.sql.Types.LONGVARBINARY = -4 + tuples.add(new ByteArrayRow(getTypeInfo("LONG VARBINARY"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("MEDIUMBLOB"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("LONGBLOB"), getExceptionInterceptor())); tuples.add(new ByteArrayRow(getTypeInfo("BLOB"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("BOOL"), getExceptionInterceptor())); + // java.sql.Types.VARBINARY = -3 + tuples.add(new ByteArrayRow(getTypeInfo("VARBINARY"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("TINYBLOB"), getExceptionInterceptor())); + // java.sql.Types.BINARY = -2 + tuples.add(new ByteArrayRow(getTypeInfo("BINARY"), getExceptionInterceptor())); + // java.sql.Types.LONGVARCHAR = -1 + tuples.add(new ByteArrayRow(getTypeInfo("LONG VARCHAR"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("MEDIUMTEXT"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("LONGTEXT"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("TEXT"), getExceptionInterceptor())); + // java.sql.Types.CHAR = 1 tuples.add(new ByteArrayRow(getTypeInfo("CHAR"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("DATE"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("DATETIME"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("DECIMAL"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("DOUBLE PRECISION"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("DOUBLE PRECISION UNSIGNED"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("DOUBLE"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("DOUBLE UNSIGNED"), getExceptionInterceptor())); tuples.add(new ByteArrayRow(getTypeInfo("ENUM"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("FLOAT"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("INT"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("INT UNSIGNED"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("SET"), getExceptionInterceptor())); + // java.sql.Types.DECIMAL = 3 + tuples.add(new ByteArrayRow(getTypeInfo("DECIMAL"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("NUMERIC"), getExceptionInterceptor())); + // java.sql.Types.INTEGER = 4 tuples.add(new ByteArrayRow(getTypeInfo("INTEGER"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("INTEGER UNSIGNED"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("LONG VARBINARY"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("LONG VARCHAR"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("LONGBLOB"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("LONGTEXT"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("MEDIUMBLOB"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("INT"), getExceptionInterceptor())); tuples.add(new ByteArrayRow(getTypeInfo("MEDIUMINT"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("INTEGER UNSIGNED"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("INT UNSIGNED"), getExceptionInterceptor())); tuples.add(new ByteArrayRow(getTypeInfo("MEDIUMINT UNSIGNED"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("MEDIUMTEXT"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("NUMERIC"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("REAL"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("SET"), getExceptionInterceptor())); + // java.sql.Types.SMALLINT = 5 tuples.add(new ByteArrayRow(getTypeInfo("SMALLINT"), getExceptionInterceptor())); tuples.add(new ByteArrayRow(getTypeInfo("SMALLINT UNSIGNED"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("TEXT"), getExceptionInterceptor())); + if (!this.yearIsDateType) { + tuples.add(new ByteArrayRow(getTypeInfo("YEAR"), getExceptionInterceptor())); + } + // java.sql.Types.REAL = 7 + tuples.add(new ByteArrayRow(getTypeInfo("FLOAT"), getExceptionInterceptor())); + // java.sql.Types.DOUBLE = 8 + tuples.add(new ByteArrayRow(getTypeInfo("DOUBLE"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("DOUBLE PRECISION"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("REAL"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("DOUBLE UNSIGNED"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("DOUBLE PRECISION UNSIGNED"), getExceptionInterceptor())); + // java.sql.Types.VARCHAR = 12 + tuples.add(new ByteArrayRow(getTypeInfo("VARCHAR"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("TINYTEXT"), getExceptionInterceptor())); + // java.sql.Types.BOOLEAN = 16 + tuples.add(new ByteArrayRow(getTypeInfo("BOOL"), getExceptionInterceptor())); + // java.sql.Types.DATE = 91 + tuples.add(new ByteArrayRow(getTypeInfo("DATE"), getExceptionInterceptor())); + if (this.yearIsDateType) { + tuples.add(new ByteArrayRow(getTypeInfo("YEAR"), getExceptionInterceptor())); + } + // java.sql.Types.TIME = 92 tuples.add(new ByteArrayRow(getTypeInfo("TIME"), getExceptionInterceptor())); + // java.sql.Types.TIMESTAMP = 93 + tuples.add(new ByteArrayRow(getTypeInfo("DATETIME"), getExceptionInterceptor())); tuples.add(new ByteArrayRow(getTypeInfo("TIMESTAMP"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("TINYBLOB"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("TINYINT"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("TINYINT UNSIGNED"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("TINYTEXT"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("VARBINARY"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("VARCHAR"), getExceptionInterceptor())); - tuples.add(new ByteArrayRow(getTypeInfo("YEAR"), getExceptionInterceptor())); // TODO add missed types (aliases) diff --git a/src/main/user-impl/java/com/mysql/cj/jdbc/DatabaseMetaDataUsingInfoSchema.java b/src/main/user-impl/java/com/mysql/cj/jdbc/DatabaseMetaDataUsingInfoSchema.java index 77750a047..559a1dc53 100644 --- a/src/main/user-impl/java/com/mysql/cj/jdbc/DatabaseMetaDataUsingInfoSchema.java +++ b/src/main/user-impl/java/com/mysql/cj/jdbc/DatabaseMetaDataUsingInfoSchema.java @@ -1,7 +1,7 @@ /* * Modifications Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * - * Copyright (c) 2005, 2022, Oracle and/or its affiliates. + * Copyright (c) 2005, 2023, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -476,7 +476,7 @@ public ResultSet getIndexInfo(String catalog, String schema, String table, boole if (unique) { sqlBuf.append(" AND NON_UNIQUE=0 "); } - sqlBuf.append("ORDER BY NON_UNIQUE, INDEX_NAME, SEQ_IN_INDEX"); + sqlBuf.append(" ORDER BY NON_UNIQUE, INDEX_NAME, SEQ_IN_INDEX"); try (java.sql.PreparedStatement pStmt = prepareMetaDataSafeStatement(sqlBuf.toString())) { int nextId = 1; @@ -512,7 +512,7 @@ public java.sql.ResultSet getPrimaryKeys(String catalog, String schema, String t sqlBuf.append(" TABLE_SCHEMA = ? AND"); } sqlBuf.append(" TABLE_NAME = ?"); - sqlBuf.append(" AND INDEX_NAME='PRIMARY' ORDER BY TABLE_SCHEMA, TABLE_NAME, INDEX_NAME, SEQ_IN_INDEX"); + sqlBuf.append(" AND INDEX_NAME='PRIMARY' ORDER BY TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, SEQ_IN_INDEX"); try (java.sql.PreparedStatement pStmt = prepareMetaDataSafeStatement(sqlBuf.toString())) { int nextId = 1; diff --git a/src/test/java/testsuite/regression/MetaDataRegressionTest.java b/src/test/java/testsuite/regression/MetaDataRegressionTest.java index 6a6df2461..5d7c541ed 100644 --- a/src/test/java/testsuite/regression/MetaDataRegressionTest.java +++ b/src/test/java/testsuite/regression/MetaDataRegressionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2022, Oracle and/or its affiliates. + * Copyright (c) 2002, 2023, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -488,6 +488,16 @@ public void testTypes() throws Exception { this.rs = this.stmt.executeQuery("SELECT * from typesRegressTest"); + Map expectedTypes = new HashMap<>(); + expectedTypes.put("varcharField", "VARCHAR"); + expectedTypes.put("charField", "CHAR"); + expectedTypes.put("enumField", "CHAR"); + expectedTypes.put("setField", "CHAR"); + expectedTypes.put("tinyblobField", "TINYBLOB"); + expectedTypes.put("mediumBlobField", "MEDIUMBLOB"); + expectedTypes.put("longblobField", "LONGBLOB"); + expectedTypes.put("blobField", "BLOB"); + ResultSetMetaData rsmd = this.rs.getMetaData(); int numCols = rsmd.getColumnCount(); @@ -495,7 +505,7 @@ public void testTypes() throws Exception { for (int i = 0; i < numCols; i++) { String columnName = rsmd.getColumnName(i + 1); String columnTypeName = rsmd.getColumnTypeName(i + 1); - System.out.println(columnName + " -> " + columnTypeName); + assertEquals(expectedTypes.get(columnName), columnTypeName); } } finally { this.stmt.execute("DROP TABLE IF EXISTS typesRegressTest"); @@ -731,9 +741,6 @@ public void testBug7033() throws Exception { big5Conn = getConnectionWithProps(props); big5Stmt = big5Conn.createStatement(); - byte[] foobar = testString.getBytes("Big5"); - System.out.println(Arrays.toString(foobar)); - this.rs = big5Stmt.executeQuery("select 1 as '\u5957 \u9910'"); String retrString = this.rs.getMetaData().getColumnName(1); assertTrue(testString.equals(retrString)); @@ -1071,7 +1078,7 @@ public void testBug11781() throws Exception { props.setProperty(PropertyKey.useInformationSchema.getKeyName(), "" + useIS); props.setProperty(PropertyKey.databaseTerm.getKeyName(), databaseTerm); - System.out.println("useInformationSchema=" + useIS + ", databaseTerm=" + databaseTerm); + // System.out.println("useInformationSchema=" + useIS + ", databaseTerm=" + databaseTerm); Connection con = getConnectionWithProps(props); @@ -1544,19 +1551,21 @@ public void testRSMDToStringFromDBMD() throws Exception { @Test public void testCharacterSetForDBMD() throws Exception { - System.out.println("testCharacterSetForDBMD:"); String quoteChar = this.conn.getMetaData().getIdentifierQuoteString(); - String tableName = quoteChar + "\u00e9\u0074\u00e9" + quoteChar; - createTable(tableName, "(field1 int)"); + String tableName = "\u00e9\u0074\u00e9"; + String quotedTableName = quoteChar + tableName + quoteChar; + createTable(quotedTableName, "(field1 int)"); this.rs = this.conn.getMetaData().getTables(this.conn.getCatalog(), null, "%", new String[] { "TABLE" }); - while (this.rs.next()) { - System.out.println(this.rs.getString("TABLE_NAME") + " -> " + new String(this.rs.getBytes("TABLE_NAME"), "UTF-8")); + boolean tableFound = false; + while (this.rs.next() && !tableFound) { + tableFound = tableName.equals(this.rs.getString("TABLE_NAME")) && tableName.equals(new String(this.rs.getBytes("TABLE_NAME"), "UTF-8")); } - this.rs = this.conn.getMetaData().getTables(this.conn.getCatalog(), null, tableName, new String[] { "TABLE" }); + assertTrue(tableFound); + this.rs = this.conn.getMetaData().getTables(this.conn.getCatalog(), null, quotedTableName, new String[] { "TABLE" }); assertEquals(true, this.rs.next()); - System.out.println(this.rs.getString("TABLE_NAME")); - System.out.println(new String(this.rs.getBytes("TABLE_NAME"), "UTF-8")); + assertEquals(tableName, this.rs.getString("TABLE_NAME")); + assertEquals(tableName, new String(this.rs.getBytes("TABLE_NAME"), "UTF-8")); } /** @@ -1965,12 +1974,6 @@ public void testBug27916() throws Exception { @Test public void testBug20491() throws Exception { - this.rs = this.stmt.executeQuery("SHOW VARIABLES LIKE '%char%'"); - while (this.rs.next()) { - System.out.println(this.rs.getString(1) + " = " + this.rs.getString(2)); - } - this.rs.close(); - String[] fields = { "field1_ae_\u00e4", "field2_ue_\u00fc", "field3_oe_\u00f6", "field4_sz_\u00df" }; createTable("tst", "(`" + fields[0] + "` int(10) unsigned NOT NULL default '0', `" + fields[1] + "` varchar(45) default '', `" + fields[2] @@ -2147,7 +2150,7 @@ public void testNoSystemTablesReturned() throws Exception { props.setProperty(PropertyKey.useInformationSchema.getKeyName(), "" + useIS); props.setProperty(PropertyKey.databaseTerm.getKeyName(), dbMapsToSchema ? DatabaseTerm.SCHEMA.name() : DatabaseTerm.CATALOG.name()); - System.out.println("useIS=" + useIS + ", dbMapsToSchema=" + dbMapsToSchema); + // System.out.println("useIS=" + useIS + ", dbMapsToSchema=" + dbMapsToSchema); Connection conn1 = null; try { @@ -2197,6 +2200,23 @@ public void testABunchOfReturnTypes() throws Exception { } private void checkABunchOfReturnTypesForConnection(Connection mdConn) throws Exception { + // Setup. + final Map sqlTypesMap = new HashMap<>(); + Field[] typeFields = Types.class.getFields(); + + for (int i = 0; i < typeFields.length; i++) { + if (Modifier.isStatic(typeFields[i].getModifiers())) { + try { + sqlTypesMap.put(new Integer(typeFields[i].getInt(null)), "java.sql.Types." + typeFields[i].getName()); + } catch (IllegalArgumentException e) { + // ignore + } catch (IllegalAccessException e) { + // ignore + } + } + } + + // Test. DatabaseMetaData md = mdConn.getMetaData(); // Bug#44862 - getBestRowIdentifier does not return resultset as per JDBC API specifications @@ -2212,7 +2232,7 @@ private void checkABunchOfReturnTypesForConnection(Connection mdConn) throws Exc Types.SMALLINT, // 8. PSEUDO_COLUMN short => is this a pseudo column like an Oracle ROWID }; - checkTypes(this.rs, types); + checkTypes(this.rs, types, sqlTypesMap); // Bug#44683 - getVersionColumns does not return resultset as per JDBC API specifications this.rs = md.getVersionColumns(this.conn.getCatalog(), null, "returnTypesTest"); @@ -2227,7 +2247,7 @@ private void checkABunchOfReturnTypesForConnection(Connection mdConn) throws Exc Types.SMALLINT // PSEUDO_COLUMN short => whether this is pseudo column like an Oracle ROWID }; - checkTypes(this.rs, types); + checkTypes(this.rs, types, sqlTypesMap); // Bug#44865 - getColumns does not return resultset as per JDBC API specifications this.rs = md.getColumns(this.conn.getCatalog(), null, "returnTypesTest", "foo"); @@ -2261,7 +2281,7 @@ private void checkABunchOfReturnTypesForConnection(Connection mdConn) throws Exc Types.CHAR // 24. IS_GENERATEDCOLUMN String => Indicates whether this is a generated column }; - checkTypes(this.rs, types); + checkTypes(this.rs, types, sqlTypesMap); // Bug#44868 - getTypeInfo does not return resultset as per JDBC API specifications this.rs = md.getTypeInfo(); @@ -2286,7 +2306,7 @@ private void checkABunchOfReturnTypesForConnection(Connection mdConn) throws Exc Types.INTEGER // 18. NUM_PREC_RADIX int => usually 2 or 10 }; - checkTypes(this.rs, types); + checkTypes(this.rs, types, sqlTypesMap); // Bug#44869 - getIndexInfo does not return resultset as per JDBC API specifications this.rs = md.getIndexInfo(this.conn.getCatalog(), null, "returnTypesTest", false, false); @@ -2309,7 +2329,7 @@ private void checkABunchOfReturnTypesForConnection(Connection mdConn) throws Exc Types.CHAR // 13. FILTER_CONDITION String => Filter condition, if any. (may be null) }; - checkTypes(this.rs, types); + checkTypes(this.rs, types, sqlTypesMap); // Bug#44867 - getImportedKeys/exportedKeys/crossReference doesn't have correct type for DEFERRABILITY this.rs = md.getImportedKeys(this.conn.getCatalog(), null, "returnTypesTest"); @@ -2330,7 +2350,7 @@ private void checkABunchOfReturnTypesForConnection(Connection mdConn) throws Exc Types.SMALLINT // DEFERRABILITY short => can the evaluation of foreign key constraints be deferred until commit }; - checkTypes(this.rs, types); + checkTypes(this.rs, types, sqlTypesMap); this.rs = md.getExportedKeys(this.conn.getCatalog(), null, "returnTypesTest"); @@ -2350,7 +2370,7 @@ private void checkABunchOfReturnTypesForConnection(Connection mdConn) throws Exc Types.SMALLINT // DEFERRABILITY short => can the evaluation of foreign key constraints be deferred until commit }; - checkTypes(this.rs, types); + checkTypes(this.rs, types, sqlTypesMap); this.rs = md.getCrossReference(this.conn.getCatalog(), null, "returnTypesTest", this.conn.getCatalog(), null, "bar"); @@ -2370,35 +2390,15 @@ private void checkABunchOfReturnTypesForConnection(Connection mdConn) throws Exc Types.SMALLINT // DEFERRABILITY short => can the evaluation of foreign key constraints be deferred until commit }; - checkTypes(this.rs, types); + checkTypes(this.rs, types, sqlTypesMap); } - private final static Map TYPES_MAP = new HashMap<>(); - - static { - Field[] typeFields = Types.class.getFields(); - - for (int i = 0; i < typeFields.length; i++) { - System.out.println(typeFields[i].getName() + " -> " + typeFields[i].getType().getClass()); - - if (Modifier.isStatic(typeFields[i].getModifiers())) { - try { - TYPES_MAP.put(new Integer(typeFields[i].getInt(null)), "java.sql.Types." + typeFields[i].getName()); - } catch (IllegalArgumentException e) { - // ignore - } catch (IllegalAccessException e) { - // ignore - } - } - } - } - - private void checkTypes(ResultSet rsToCheck, int[] types) throws Exception { + private void checkTypes(ResultSet rsToCheck, int[] types, Map sqlTypesMap) throws Exception { ResultSetMetaData rsmd = rsToCheck.getMetaData(); assertEquals(types.length, rsmd.getColumnCount()); for (int i = 0; i < types.length; i++) { - String expectedType = TYPES_MAP.get(new Integer(types[i])); - String actualType = TYPES_MAP.get(new Integer(rsmd.getColumnType(i + 1))); + String expectedType = sqlTypesMap.get(new Integer(types[i])); + String actualType = sqlTypesMap.get(new Integer(rsmd.getColumnType(i + 1))); assertNotNull(expectedType); assertNotNull(actualType); assertEquals(expectedType, actualType, "Unexpected type in column " + (i + 1)); @@ -2929,8 +2929,6 @@ public void testBug63800() throws Exception { conn2.close(); } } - - System.out.println("useIS=" + useIS + ", dbMapsToSchema=" + dbMapsToSchema); } } } finally { @@ -3140,35 +3138,32 @@ public void testBug16436511() throws Exception { */ @Test public void testBug68098() throws Exception { - String[] testStepDescription = new String[] { "MySQL MetaData", "I__S MetaData" }; - Properties props = new Properties(); - props.setProperty(PropertyKey.sslMode.getKeyName(), SslMode.DISABLED.name()); - props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true"); - props.setProperty(PropertyKey.useInformationSchema.getKeyName(), "true"); - Connection connUseIS = getConnectionWithProps(props); - Connection[] testConnections = new Connection[] { this.conn, connUseIS }; String[] expectedIndexesOrder = new String[] { "index_1", "index_1", "index_3", "PRIMARY", "index_2", "index_2", "index_4" }; - this.stmt.execute("DROP TABLE IF EXISTS testBug68098"); - createTable("testBug68098", "(column_1 INT NOT NULL, column_2 INT NOT NULL, column_3 INT NOT NULL, PRIMARY KEY (column_1))"); - this.stmt.execute("CREATE INDEX index_4 ON testBug68098 (column_2)"); this.stmt.execute("CREATE UNIQUE INDEX index_3 ON testBug68098 (column_3)"); this.stmt.execute("CREATE INDEX index_2 ON testBug68098 (column_2, column_1)"); this.stmt.execute("CREATE UNIQUE INDEX index_1 ON testBug68098 (column_3, column_2)"); - for (int i = 0; i < testStepDescription.length; i++) { - DatabaseMetaData testDbMetaData = testConnections[i].getMetaData(); + boolean useIS = false; + do { + Properties props = new Properties(); + props.setProperty(PropertyKey.sslMode.getKeyName(), SslMode.DISABLED.name()); + props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true"); + props.setProperty(PropertyKey.useInformationSchema.getKeyName(), Boolean.toString(useIS)); + + Connection testConn = getConnectionWithProps(props); + DatabaseMetaData testDbMetaData = testConn.getMetaData(); this.rs = testDbMetaData.getIndexInfo(this.dbName, null, "testBug68098", false, false); int ind = 0; while (this.rs.next()) { - assertEquals(expectedIndexesOrder[ind++], this.rs.getString("INDEX_NAME"), testStepDescription[i] + ", sort order is wrong"); + assertEquals(expectedIndexesOrder[ind++], this.rs.getString("INDEX_NAME"), + (useIS ? "I_S MetaData" : "MySQL MetaData") + ", sort order is wrong"); } this.rs.close(); - } - - connUseIS.close(); + testConn.close(); + } while (useIS = !useIS); } /** @@ -3208,16 +3203,16 @@ public void testBug65871() throws Exception { props.setProperty(PropertyKey.useInformationSchema.getKeyName(), "false"); pedanticConn = getConnectionWithProps(props); - System.out.println("1. Non-pedantic, without I_S."); + // 1. Non-pedantic, without I_S. testBug65871_testCatalogs(nonPedanticConn); - System.out.println("2. Pedantic, without I_S."); + // 2. Pedantic, without I_S. testBug65871_testCatalogs(pedanticConn); - System.out.println("3. Non-pedantic, with I_S."); + // 3. Non-pedantic, with I_S. testBug65871_testCatalogs(nonPedanticConn_IS); - System.out.println("4. Pedantic, with I_S."); + // 4. Pedantic, with I_S. testBug65871_testCatalogs(pedanticConn_IS); } finally { @@ -3848,20 +3843,27 @@ public void testBug69290() throws Exception { props.setProperty(PropertyKey.sslMode.getKeyName(), SslMode.DISABLED.name()); props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true"); props.setProperty(PropertyKey.useInformationSchema.getKeyName(), "true"); - Connection connUseIS = getConnectionWithProps(props); + props.setProperty(PropertyKey.nullDatabaseMeansCurrent.getKeyName(), "true"); + Connection connUseISAndNullCurr = getConnectionWithProps(props); + props.setProperty(PropertyKey.useInformationSchema.getKeyName(), "true"); props.setProperty(PropertyKey.nullDatabaseMeansCurrent.getKeyName(), "false"); Connection connUseISAndNullAll = getConnectionWithProps(props); - props.remove(PropertyKey.useInformationSchema.getKeyName()); - Connection connNullAll = getConnectionWithProps(props); + props.setProperty(PropertyKey.useInformationSchema.getKeyName(), "false"); + props.setProperty(PropertyKey.nullDatabaseMeansCurrent.getKeyName(), "true"); + Connection connUseMySqlAndNullCurr = getConnectionWithProps(props); + + props.setProperty(PropertyKey.useInformationSchema.getKeyName(), "false"); + props.setProperty(PropertyKey.nullDatabaseMeansCurrent.getKeyName(), "false"); + Connection connUseMySqlAndNullAll = getConnectionWithProps(props); boolean dbMapsToSchema = ((JdbcConnection) this.conn).getPropertySet().getEnumProperty(PropertyKey.databaseTerm) .getValue() == DatabaseTerm.SCHEMA; final String testDb = dbMapsToSchema ? this.conn.getSchema() : this.conn.getCatalog(); - Connection[] testConnections = new Connection[] { this.conn, connUseIS }; + Connection[] testConnections = new Connection[] { connUseMySqlAndNullCurr, connUseISAndNullCurr }; // check table types returned in getTableTypes() final List tableTypes = Arrays.asList(new String[] { "LOCAL TEMPORARY", "SYSTEM TABLE", "SYSTEM VIEW", "TABLE", "VIEW" }); @@ -3933,7 +3935,7 @@ public void testBug69290() throws Exception { assertTrue(countResults[0][2] == countResults[1][2], "The number of results from getTables() MySQl(" + countResults[0][2] + ") and I__S(" + countResults[1][2] + ") should be the same for 'performance_schema' catalog/schema."); - testConnections = new Connection[] { connNullAll, connUseISAndNullAll }; + testConnections = new Connection[] { connUseMySqlAndNullAll, connUseISAndNullAll }; countResults = new int[][] { { 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0 } }; // check table types returned in getTables() for all catalogs/schemas and filter by table type (tested with property nullCatalogMeansCurrent=false) @@ -4086,35 +4088,29 @@ private void checkProcedureColumnTypeForBug68307(String testAgainst, DatabaseMet */ @Test public void testBug44451() throws Exception { - String methodName; - List expectedFields; - String[] testStepDescription = new String[] { "MySQL MetaData", "I__S MetaData" }; - Properties props = new Properties(); - props.setProperty(PropertyKey.sslMode.getKeyName(), SslMode.DISABLED.name()); - props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true"); - props.setProperty(PropertyKey.useInformationSchema.getKeyName(), "true"); - Connection connUseIS = getConnectionWithProps(props); - Connection[] testConnections = new Connection[] { this.conn, connUseIS }; + boolean useIS = false; + do { + Properties props = new Properties(); + props.setProperty(PropertyKey.sslMode.getKeyName(), SslMode.DISABLED.name()); + props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true"); + props.setProperty(PropertyKey.useInformationSchema.getKeyName(), Boolean.toString(useIS)); + Connection testConn = getConnectionWithProps(props); + DatabaseMetaData testDbMetaData = testConn.getMetaData(); - methodName = "getClientInfoProperties()"; - expectedFields = Arrays.asList("NAME", "MAX_LEN", "DEFAULT_VALUE", "DESCRIPTION"); - for (int i = 0; i < testStepDescription.length; i++) { - DatabaseMetaData testDbMetaData = testConnections[i].getMetaData(); + String methodName = "getClientInfoProperties()"; + List expectedFields = Arrays.asList("NAME", "MAX_LEN", "DEFAULT_VALUE", "DESCRIPTION"); this.rs = testDbMetaData.getClientInfoProperties(); - checkReturnedColumnsForBug44451(testStepDescription[i], methodName, expectedFields, this.rs); + checkReturnedColumnsForBug44451(useIS ? "I_S MetaData" : "MySQL MetaData", methodName, expectedFields, this.rs); this.rs.close(); - } - methodName = "getFunctions()"; - expectedFields = Arrays.asList("FUNCTION_CAT", "FUNCTION_SCHEM", "FUNCTION_NAME", "REMARKS", "FUNCTION_TYPE", "SPECIFIC_NAME"); - for (int i = 0; i < testStepDescription.length; i++) { - DatabaseMetaData testDbMetaData = testConnections[i].getMetaData(); + methodName = "getFunctions()"; + expectedFields = Arrays.asList("FUNCTION_CAT", "FUNCTION_SCHEM", "FUNCTION_NAME", "REMARKS", "FUNCTION_TYPE", "SPECIFIC_NAME"); this.rs = testDbMetaData.getFunctions(null, null, "%"); - checkReturnedColumnsForBug44451(testStepDescription[i], methodName, expectedFields, this.rs); + checkReturnedColumnsForBug44451(useIS ? "I_S MetaData" : "MySQL MetaData", methodName, expectedFields, this.rs); this.rs.close(); - } - connUseIS.close(); + testConn.close(); + } while (useIS = !useIS); } private void checkReturnedColumnsForBug44451(String stepDescription, String methodName, List expectedFields, ResultSet resultSetToCheck) @@ -4128,7 +4124,6 @@ private void checkReturnedColumnsForBug44451(String stepDescription, String meth assertEquals(expectedFields.get(i), rsMetaData.getColumnName(position), stepDescription + ", wrong column at position '" + position + "' in method '" + methodName + "'."); } - this.rs.close(); } /** @@ -4150,7 +4145,7 @@ public void testBug20504139() throws Exception { String connProps = String.format("pedantic=%s,useInformationSchema=%s,getProceduresReturnsFunctions=%s", usePedantic, useInformationSchema, useFuncsInProcs); - System.out.printf("testBug20504139_%d: %s%n", testCase, connProps); + String testDesc = String.format("testBug20504139_%d: %s%n", testCase, connProps); Properties props = new Properties(); props.setProperty(PropertyKey.sslMode.getKeyName(), SslMode.DISABLED.name()); @@ -4176,24 +4171,25 @@ public void testBug20504139() throws Exception { testRs = dbMapsToSchema ? dbmd.getProcedureColumns("", this.dbName, name, "%") : dbmd.getProcedureColumns(this.dbName, "", name, "%"); if (useFuncsInProcs) { - assertTrue(testRs.next()); - assertEquals("", testRs.getString(4), testCase + "." + i + ". expected function column name (empty)"); + assertTrue(testRs.next(), testDesc); + assertEquals("", testRs.getString(4), testDesc + "; " + testCase + "." + i + ". expected function column name (empty)"); assertEquals(DatabaseMetaData.procedureColumnReturn, testRs.getInt(5), - testCase + "." + i + ". expected function column type (empty)"); - assertTrue(testRs.next()); - assertEquals("namef", testRs.getString(4), testCase + "." + i + ". expected function column name"); - assertEquals(DatabaseMetaData.procedureColumnIn, testRs.getInt(5), testCase + "." + i + ". expected function column type (empty)"); - assertFalse(testRs.next()); + testDesc + "; " + testCase + "." + i + ". expected function column type (empty)"); + assertTrue(testRs.next(), testDesc); + assertEquals("namef", testRs.getString(4), testDesc + "; " + testCase + "." + i + ". expected function column name"); + assertEquals(DatabaseMetaData.procedureColumnIn, testRs.getInt(5), + testDesc + "; " + testCase + "." + i + ". expected function column type (empty)"); + assertFalse(testRs.next(), testDesc); } else { - assertFalse(testRs.next()); + assertFalse(testRs.next(), testDesc); } testRs.close(); i++; } } catch (SQLException e) { - assertFalse(e.getMessage().matches("FUNCTION `testBug20504139(:?`{2})?[fp]` does not exist"), - testCase + "." + i + ". failed to retrieve function columns, with getProcedureColumns(), from database meta data."); + assertFalse(e.getMessage().matches("FUNCTION `testBug20504139(:?`{2})?[fp]` does not exist"), testDesc + "; " + testCase + "." + i + + ". failed to retrieve function columns, with getProcedureColumns(), from database meta data."); throw e; } @@ -4288,12 +4284,12 @@ public void testBug21215151() throws Exception { DatabaseMetaData dbmd = this.conn.getMetaData(); this.rs = dbmd.getCatalogs(); - System.out.println("Catalogs:"); - System.out.println("--------------------------------------------------"); - while (this.rs.next()) { - System.out.println("\t" + this.rs.getString(1)); - } - this.rs.beforeFirst(); + // System.out.println("Catalogs:"); + // System.out.println("--------------------------------------------------"); + // while (this.rs.next()) { + // System.out.println("\t" + this.rs.getString(1)); + // } + // this.rs.beforeFirst(); // check the relative position of each element in the result set compared to the previous element. String previousDb = ""; @@ -4326,7 +4322,7 @@ public void testBug19803348() throws Exception { props.setProperty(PropertyKey.useInformationSchema.getKeyName(), "" + useIS); props.setProperty(PropertyKey.databaseTerm.getKeyName(), dbMapsToSchema ? DatabaseTerm.SCHEMA.name() : DatabaseTerm.CATALOG.name()); - System.out.println("useIS=" + useIS + ", dbMapsToSchema=" + dbMapsToSchema); + String testCase = "useIS=" + useIS + ", dbMapsToSchema=" + dbMapsToSchema; Connection conn1 = null; try { @@ -4349,34 +4345,34 @@ public void testBug19803348() throws Exception { createProcedure(testDb1 + ".testBug19803348_p", "(d int) BEGIN SELECT d; END"); this.rs = dbmd.getFunctions(null, null, "testBug19803348_%"); - assertTrue(this.rs.next()); - assertEquals(testDb1, this.rs.getString(dbMapsToSchema ? 2 : 1)); - assertEquals("testBug19803348_f", this.rs.getString(3)); - assertFalse(this.rs.next()); + assertTrue(this.rs.next(), testCase); + assertEquals(testDb1, this.rs.getString(dbMapsToSchema ? 2 : 1), testCase); + assertEquals("testBug19803348_f", this.rs.getString(3), testCase); + assertFalse(this.rs.next(), testCase); this.rs = dbmd.getFunctionColumns(null, null, "testBug19803348_%", "%"); - assertTrue(this.rs.next()); - assertEquals(testDb1, this.rs.getString(dbMapsToSchema ? 2 : 1)); - assertEquals("testBug19803348_f", this.rs.getString(3)); - assertEquals("", this.rs.getString(4)); - assertTrue(this.rs.next()); - assertEquals(testDb1, this.rs.getString(dbMapsToSchema ? 2 : 1)); - assertEquals("testBug19803348_f", this.rs.getString(3)); - assertEquals("d", this.rs.getString(4)); - assertFalse(this.rs.next()); + assertTrue(this.rs.next(), testCase); + assertEquals(testDb1, this.rs.getString(dbMapsToSchema ? 2 : 1), testCase); + assertEquals("testBug19803348_f", this.rs.getString(3), testCase); + assertEquals("", this.rs.getString(4), testCase); + assertTrue(this.rs.next(), testCase); + assertEquals(testDb1, this.rs.getString(dbMapsToSchema ? 2 : 1), testCase); + assertEquals("testBug19803348_f", this.rs.getString(3), testCase); + assertEquals("d", this.rs.getString(4), testCase); + assertFalse(this.rs.next(), testCase); this.rs = dbmd.getProcedures(null, null, "testBug19803348_%"); - assertTrue(this.rs.next()); - assertEquals(testDb1, this.rs.getString(dbMapsToSchema ? 2 : 1)); - assertEquals("testBug19803348_p", this.rs.getString(3)); - assertFalse(this.rs.next()); + assertTrue(this.rs.next(), testCase); + assertEquals(testDb1, this.rs.getString(dbMapsToSchema ? 2 : 1), testCase); + assertEquals("testBug19803348_p", this.rs.getString(3), testCase); + assertFalse(this.rs.next(), testCase); this.rs = dbmd.getProcedureColumns(null, null, "testBug19803348_%", "%"); - assertTrue(this.rs.next()); - assertEquals(testDb1, this.rs.getString(dbMapsToSchema ? 2 : 1)); - assertEquals("testBug19803348_p", this.rs.getString(3)); - assertEquals("d", this.rs.getString(4)); - assertFalse(this.rs.next()); + assertTrue(this.rs.next(), testCase); + assertEquals(testDb1, this.rs.getString(dbMapsToSchema ? 2 : 1), testCase); + assertEquals("testBug19803348_p", this.rs.getString(3), testCase); + assertEquals("d", this.rs.getString(4), testCase); + assertFalse(this.rs.next(), testCase); dropFunction(testDb1 + ".testBug19803348_f"); dropProcedure(testDb1 + ".testBug19803348_p"); @@ -4388,52 +4384,52 @@ public void testBug19803348() throws Exception { createProcedure(testDb2 + ".testBug19803348_A_p", "(d int) BEGIN SELECT d; END"); this.rs = dbmd.getFunctions(null, null, "testBug19803348_%"); - assertTrue(this.rs.next()); - assertEquals(testDb1, this.rs.getString(dbMapsToSchema ? 2 : 1)); - assertEquals("testBug19803348_B_f", this.rs.getString(3)); - assertTrue(this.rs.next()); - assertEquals(testDb2, this.rs.getString(dbMapsToSchema ? 2 : 1)); - assertEquals("testBug19803348_A_f", this.rs.getString(3)); - assertFalse(this.rs.next()); + assertTrue(this.rs.next(), testCase); + assertEquals(testDb1, this.rs.getString(dbMapsToSchema ? 2 : 1), testCase); + assertEquals("testBug19803348_B_f", this.rs.getString(3), testCase); + assertTrue(this.rs.next(), testCase); + assertEquals(testDb2, this.rs.getString(dbMapsToSchema ? 2 : 1), testCase); + assertEquals("testBug19803348_A_f", this.rs.getString(3), testCase); + assertFalse(this.rs.next(), testCase); this.rs = dbmd.getFunctionColumns(null, null, "testBug19803348_%", "%"); - assertTrue(this.rs.next()); - assertEquals(testDb1, this.rs.getString(dbMapsToSchema ? 2 : 1)); - assertEquals("testBug19803348_B_f", this.rs.getString(3)); - assertEquals("", this.rs.getString(4)); - assertTrue(this.rs.next()); - assertEquals(testDb1, this.rs.getString(dbMapsToSchema ? 2 : 1)); - assertEquals("testBug19803348_B_f", this.rs.getString(3)); - assertEquals("d", this.rs.getString(4)); - assertTrue(this.rs.next()); - assertEquals(testDb2, this.rs.getString(dbMapsToSchema ? 2 : 1)); - assertEquals("testBug19803348_A_f", this.rs.getString(3)); - assertEquals("", this.rs.getString(4)); - assertTrue(this.rs.next()); - assertEquals(testDb2, this.rs.getString(dbMapsToSchema ? 2 : 1)); - assertEquals("testBug19803348_A_f", this.rs.getString(3)); - assertEquals("d", this.rs.getString(4)); - assertFalse(this.rs.next()); + assertTrue(this.rs.next(), testCase); + assertEquals(testDb1, this.rs.getString(dbMapsToSchema ? 2 : 1), testCase); + assertEquals("testBug19803348_B_f", this.rs.getString(3), testCase); + assertEquals("", this.rs.getString(4), testCase); + assertTrue(this.rs.next(), testCase); + assertEquals(testDb1, this.rs.getString(dbMapsToSchema ? 2 : 1), testCase); + assertEquals("testBug19803348_B_f", this.rs.getString(3), testCase); + assertEquals("d", this.rs.getString(4), testCase); + assertTrue(this.rs.next(), testCase); + assertEquals(testDb2, this.rs.getString(dbMapsToSchema ? 2 : 1), testCase); + assertEquals("testBug19803348_A_f", this.rs.getString(3), testCase); + assertEquals("", this.rs.getString(4), testCase); + assertTrue(this.rs.next(), testCase); + assertEquals(testDb2, this.rs.getString(dbMapsToSchema ? 2 : 1), testCase); + assertEquals("testBug19803348_A_f", this.rs.getString(3), testCase); + assertEquals("d", this.rs.getString(4), testCase); + assertFalse(this.rs.next(), testCase); this.rs = dbmd.getProcedures(null, null, "testBug19803348_%"); - assertTrue(this.rs.next()); - assertEquals(testDb1, this.rs.getString(dbMapsToSchema ? 2 : 1)); - assertEquals("testBug19803348_B_p", this.rs.getString(3)); - assertTrue(this.rs.next()); - assertEquals(testDb2, this.rs.getString(dbMapsToSchema ? 2 : 1)); - assertEquals("testBug19803348_A_p", this.rs.getString(3)); - assertFalse(this.rs.next()); + assertTrue(this.rs.next(), testCase); + assertEquals(testDb1, this.rs.getString(dbMapsToSchema ? 2 : 1), testCase); + assertEquals("testBug19803348_B_p", this.rs.getString(3), testCase); + assertTrue(this.rs.next(), testCase); + assertEquals(testDb2, this.rs.getString(dbMapsToSchema ? 2 : 1), testCase); + assertEquals("testBug19803348_A_p", this.rs.getString(3), testCase); + assertFalse(this.rs.next(), testCase); this.rs = dbmd.getProcedureColumns(null, null, "testBug19803348_%", "%"); - assertTrue(this.rs.next()); - assertEquals(testDb1, this.rs.getString(dbMapsToSchema ? 2 : 1)); - assertEquals("testBug19803348_B_p", this.rs.getString(3)); - assertEquals("d", this.rs.getString(4)); - assertTrue(this.rs.next()); - assertEquals(testDb2, this.rs.getString(dbMapsToSchema ? 2 : 1)); - assertEquals("testBug19803348_A_p", this.rs.getString(3)); - assertEquals("d", this.rs.getString(4)); - assertFalse(this.rs.next()); + assertTrue(this.rs.next(), testCase); + assertEquals(testDb1, this.rs.getString(dbMapsToSchema ? 2 : 1), testCase); + assertEquals("testBug19803348_B_p", this.rs.getString(3), testCase); + assertEquals("d", this.rs.getString(4), testCase); + assertTrue(this.rs.next(), testCase); + assertEquals(testDb2, this.rs.getString(dbMapsToSchema ? 2 : 1), testCase); + assertEquals("testBug19803348_A_p", this.rs.getString(3), testCase); + assertEquals("d", this.rs.getString(4), testCase); + assertFalse(this.rs.next(), testCase); } finally { if (conn1 != null) { @@ -5009,7 +5005,7 @@ public void testBug97413() throws Exception { } else if (warn.getMessage().startsWith("UNSIGNED for decimal and floating point data types is deprecated")) { cnt4++; } else { - System.out.println(warn.getMessage()); + fail("Not expected warning: " + warn.getMessage()); } warn = warn.getNextWarning(); } @@ -5507,4 +5503,55 @@ public void testBug106758() throws Exception { } } } + + /** + * Tests fix for Bug#109807 (Bug#35021014), DatabaseMetaData#getTypeInfo should ordered by DATA_TYPE. + * + * @throws Exception + */ + @Test + void testBug109807() throws Exception { + boolean useIS = false; + boolean yearDT = false; + do { + Properties props = new Properties(); + props.setProperty(PropertyKey.useInformationSchema.getKeyName(), Boolean.toString(useIS)); + props.setProperty(PropertyKey.yearIsDateType.getKeyName(), Boolean.toString(yearDT)); + Connection testConn = getConnectionWithProps(props); + DatabaseMetaData testDbMD = testConn.getMetaData(); + this.rs = testDbMD.getTypeInfo(); + int prevDataType = Integer.MIN_VALUE; + while (this.rs.next()) { + assertTrue(prevDataType <= this.rs.getInt("DATA_TYPE"), "DatabaseMetaData#getTypeInfo must be ordered ascending by DATA_TYPE"); + prevDataType = this.rs.getInt("DATA_TYPE"); + } + testConn.close(); + } while ((useIS = !useIS) || (yearDT = !yearDT)); + } + + /** + * Tests fix for Bug#109808 (Bug#35021038), DatabaseMetaData#getPrimaryKeys should ordered by COLUMN_NAME. + * + * @throws Exception + */ + @Test + void testBug109808() throws Exception { + createTable("testBug109808", + "(col_A INT NOT NULL, col_b INT NOT NULL, col_C VARCHAR(100) NOT NULL, col_D TIMESTAMP, PRIMARY KEY(col_b, col_C, col_A))"); + boolean useIS = false; + do { + Properties props = new Properties(); + props.setProperty(PropertyKey.useInformationSchema.getKeyName(), Boolean.toString(useIS)); + Connection testConn = getConnectionWithProps(props); + DatabaseMetaData dbmd = testConn.getMetaData(); + this.rs = dbmd.getPrimaryKeys(null, null, "testBug109808"); + assertTrue(this.rs.next()); + assertEquals("col_A", this.rs.getString("COLUMN_NAME")); + assertTrue(this.rs.next()); + assertEquals("col_b", this.rs.getString("COLUMN_NAME")); + assertTrue(this.rs.next()); + assertEquals("col_C", this.rs.getString("COLUMN_NAME")); + assertFalse(this.rs.next()); + } while (useIS = !useIS); + } } diff --git a/src/test/java/testsuite/regression/StatementRegressionTest.java b/src/test/java/testsuite/regression/StatementRegressionTest.java index e4979b062..f5fd1baf4 100644 --- a/src/test/java/testsuite/regression/StatementRegressionTest.java +++ b/src/test/java/testsuite/regression/StatementRegressionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2022, Oracle and/or its affiliates. + * Copyright (c) 2002, 2023, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -47,6 +47,7 @@ import java.io.ByteArrayOutputStream; import java.io.CharArrayReader; import java.io.File; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; @@ -59,6 +60,8 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Paths; import java.sql.Array; import java.sql.BatchUpdateException; import java.sql.Blob; @@ -5315,8 +5318,7 @@ public void testBug41566() throws Exception { } /* - * Bug #40439 - Error rewriting batched statement if table name ends with - * "values". + * Bug #40439 - Error rewriting batched statement if table name ends with "values". */ @Test public void testBug40439() throws Exception { @@ -8362,14 +8364,14 @@ public void testBug71672() throws SQLException { // A. Test Statement.execute() results createTable("testBug71672", tableDDL); for (int i = 0; i < queries.length; i++) { - testBug71672Statement(testStep, testConn, queries[i], -1, expectedGenKeys[i]); + checkStatementForTestBug71672(testStep, testConn, queries[i], -1, expectedGenKeys[i]); } dropTable("testBug71672"); // B. Test Statement.executeUpdate() results createTable("testBug71672", tableDDL); for (int i = 0; i < queries.length; i++) { - testBug71672Statement(testStep, testConn, queries[i], expectedUpdCount[i], expectedGenKeys[i]); + checkStatementForTestBug71672(testStep, testConn, queries[i], expectedUpdCount[i], expectedGenKeys[i]); } dropTable("testBug71672"); @@ -8399,14 +8401,14 @@ public void testBug71672() throws SQLException { // D. Test PreparedStatement.execute() results createTable("testBug71672", tableDDL); for (int i = 0; i < queries.length; i++) { - testBug71672PreparedStatement(testStep, testConn, queries[i], -1, expectedGenKeys[i]); + checkPreparedStatementForTestBug71672(testStep, testConn, queries[i], -1, expectedGenKeys[i]); } dropTable("testBug71672"); // E. Test PreparedStatement.executeUpdate() results createTable("testBug71672", tableDDL); for (int i = 0; i < queries.length; i++) { - testBug71672PreparedStatement(testStep, testConn, queries[i], expectedUpdCount[i], expectedGenKeys[i]); + checkPreparedStatementForTestBug71672(testStep, testConn, queries[i], expectedUpdCount[i], expectedGenKeys[i]); } dropTable("testBug71672"); @@ -8464,22 +8466,22 @@ public void testBug71672() throws SQLException { // A. Test Statement.execute() results createTable("testBug71672", tableDDL); - testBug71672Statement(testStep, testConn, allQueries, -1, expectedGenKeysMultiQueries); + checkStatementForTestBug71672(testStep, testConn, allQueries, -1, expectedGenKeysMultiQueries); dropTable("testBug71672"); // B. Test Statement.executeUpdate() results createTable("testBug71672", tableDDL); - testBug71672Statement(testStep, testConn, allQueries, 3, expectedGenKeysMultiQueries); + checkStatementForTestBug71672(testStep, testConn, allQueries, 3, expectedGenKeysMultiQueries); dropTable("testBug71672"); // C. Test PreparedStatement.execute() results createTable("testBug71672", tableDDL); - testBug71672PreparedStatement(testStep, testConn, allQueries, -1, expectedGenKeysMultiQueries); + checkPreparedStatementForTestBug71672(testStep, testConn, allQueries, -1, expectedGenKeysMultiQueries); dropTable("testBug71672"); // D. Test PreparedStatement.executeUpdate() results createTable("testBug71672", tableDDL); - testBug71672PreparedStatement(testStep, testConn, allQueries, 3, expectedGenKeysMultiQueries); + checkPreparedStatementForTestBug71672(testStep, testConn, allQueries, 3, expectedGenKeysMultiQueries); dropTable("testBug71672"); testConn.close(); @@ -8497,7 +8499,8 @@ public void testBug71672() throws SQLException { * @param expectedKeys * @throws SQLException */ - public void testBug71672Statement(int testStep, Connection testConn, String query, int expectedUpdateCount, int[] expectedKeys) throws SQLException { + private void checkStatementForTestBug71672(int testStep, Connection testConn, String query, int expectedUpdateCount, int[] expectedKeys) + throws SQLException { Statement testStmt = testConn.createStatement(); if (expectedUpdateCount < 0) { @@ -8527,7 +8530,7 @@ public void testBug71672Statement(int testStep, Connection testConn, String quer * @param expectedKeys * @throws SQLException */ - public void testBug71672PreparedStatement(int testStep, Connection testConn, String query, int expectedUpdateCount, int[] expectedKeys) + private void checkPreparedStatementForTestBug71672(int testStep, Connection testConn, String query, int expectedUpdateCount, int[] expectedKeys) throws SQLException { PreparedStatement testPStmt = testConn.prepareStatement(query); @@ -12969,7 +12972,7 @@ public void testBug108195() throws Exception { } /** - * Tests for Bug#99604 (Bug#31612628), Add support to row alias on INSERT... ON DUPLICATE KEY UPDATE on batch mode. + * Tests fix for Bug#99604 (Bug#31612628), Add support to row alias on INSERT... ON DUPLICATE KEY UPDATE on batch mode. * Resolved by fix for Bug#106240 (33781440), StringIndexOutOfBoundsException when VALUE is at the end of the query. * * @throws Exception @@ -13067,4 +13070,306 @@ void testBug102520() throws Exception { testConn.close(); } while ((useSPS = !useSPS) || (cachePS = !cachePS)); } + + /** + * Tests fix for Bug#109243 (Bug#34852047), Judge whether the returned result set of the sql statement is incorrect. + * + * @throws Exception + */ + @Test + public void testBug109243() throws Exception { + assumeTrue(versionMeetsMinimum(8, 0), "MySQL 8.0+ is required to run this test."); + + this.rs = this.stmt.executeQuery("WITH q1 AS (SELECT 1 v1),q2 AS (SELECT 2 v2) SELECT v1,v2 FROM q1,q2"); + assertTrue(this.rs.next()); + assertEquals(1, this.rs.getInt(1)); + assertEquals(2, this.rs.getInt(2)); + assertFalse(this.rs.next()); + } + + /** + * Tests fix for Bug#77368 (Bug#21321849), "LOAD DATA LOCAL INFILE" doesn't work properly with relative paths. + * + * Testing this would require changing the value of "user.dir" which could cause other tests to fail. As such, only basic testing to verify that the default + * relative paths work fine is done here. + * + * @throws Exception + */ + @Test + public void testBug77368() throws Exception { + createTable("testBug77368", "(id INT, txt VARCHAR(100))"); + + final String data = "1\tMySQL\n2\tConnector/J"; + final String fileName = "TestBug77368.tsv"; + + Files.write(Paths.get(fileName), data.getBytes()); + + try { + Properties props = new Properties(); + props.setProperty(PropertyKey.allowLoadLocalInfile.getKeyName(), "true"); + Connection testConn = getConnectionWithProps(props); + Statement testStmt = testConn.createStatement(); + testStmt.execute("LOAD DATA LOCAL INFILE './" + fileName + "' INTO TABLE testBug77368"); + + this.rs = this.stmt.executeQuery("SELECT * FROM testBug77368"); + assertTrue(this.rs.next()); + assertEquals(1, this.rs.getInt(1)); + assertEquals("MySQL", this.rs.getString(2)); + assertTrue(this.rs.next()); + assertEquals(2, this.rs.getInt(1)); + assertEquals("Connector/J", this.rs.getString(2)); + assertFalse(this.rs.next()); + testConn.close(); + } finally { + try { + Files.delete(Paths.get(fileName)); + } catch (FileNotFoundException e) { + } + } + } + + /** + * Tests fix for Bug#109864 (Bug#35034666), Connector/J 8.0.32 hangs on MySQL 5.5 with prepared statements. + * + * @throws Exception + */ + @Test + public void testBug109864() throws Exception { + createTable("testBug109864", "(id INT)"); + + boolean useSPS = false; + + do { + Properties props = new Properties(); + props.setProperty(PropertyKey.sslMode.getKeyName(), SslMode.DISABLED.name()); + props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true"); + props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), Boolean.toString(useSPS)); + props.setProperty(PropertyKey.socketTimeout.getKeyName(), "5000"); + + Connection testConn = getConnectionWithProps(props); + + this.pstmt = testConn.prepareStatement("SELECT ?"); // Both column and parameter definition blocks in Prepare response. + this.pstmt.setInt(1, 1); + this.rs = this.pstmt.executeQuery(); + assertTrue(this.rs.next()); + assertFalse(this.rs.next()); + this.pstmt.close(); + + this.pstmt = testConn.prepareStatement("SELECT 1"); // No parameter definition block in Stmt Prepare response. + this.rs = this.pstmt.executeQuery(); + assertTrue(this.rs.next()); + assertFalse(this.rs.next()); + + // Prior to this fix preparing this statement would hang indefinitely with server-prepared statements. + this.pstmt = testConn.prepareStatement("INSERT INTO testBug109864 VALUES (?)"); // No column definition block in Prepare response. + this.pstmt.setInt(1, 1); + assertEquals(1, this.pstmt.executeUpdate()); + this.pstmt.close(); + + this.pstmt = testConn.prepareStatement("INSERT INTO testBug109864 VALUES (2)"); // No column or parameter definition blocks in Prepare response. + assertEquals(1, this.pstmt.executeUpdate()); + this.pstmt.close(); + + testConn.close(); + } while (useSPS = !useSPS); + } + + /** + * Tests fix for Bug#109377 (Bug#34900156), rewriteBatchedStatements doesn't work when parenthesis are found in values. + * + * @throws Exception + */ + @Test + void testBug109377() throws Exception { + createTable("testBug109377", "(d DOUBLE, i INT)"); + + boolean useSPS = false; + do { + final String testCase = String.format("Case [useSPS: %s]", useSPS ? "Y" : "N"); + + Properties props = new Properties(); + props.setProperty(PropertyKey.sslMode.getKeyName(), SslMode.DISABLED.name()); + props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true"); + props.setProperty(PropertyKey.queryInterceptors.getKeyName(), QueryInfoQueryInterceptor.class.getName()); + props.setProperty(PropertyKey.rewriteBatchedStatements.getKeyName(), "true"); + props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), Boolean.toString(useSPS)); + + try (Connection testConn = getConnectionWithProps(props)) { + QueryInfoQueryInterceptor.startCapturing(); + this.pstmt = testConn.prepareStatement("INSERT INTO testBug109377 (d, i) VALUES (RAND(?), ?)"); + this.pstmt.setInt(1, 10); + this.pstmt.setInt(2, 1); + this.pstmt.addBatch(); + this.pstmt.setInt(1, 10); + this.pstmt.setInt(2, 2); + this.pstmt.addBatch(); + this.pstmt.executeBatch(); + if (useSPS) { + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO testBug109377 (d, i) VALUES (RAND(?), ?),(RAND(?), ?)"); + } else { + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO testBug109377 (d, i) VALUES (RAND(10), 1),(RAND(10), 2)"); + } + } + } while (useSPS = !useSPS); + } + + /** + * Tests for Bug#107577 (Bug#34325361), rewriteBatchedStatements not work when table column name contains 'value' string. + * + * Duplicate of Bug#40439 (Bug#11750096), Error rewriting batched statement if table name ends with "values", that re-surfaced while fixing Bug#109377 + * (Bug#34900156). + * + * @throws Exception + */ + @Test + void testBug107577() throws Exception { + createTable("VALUES_testBug_VALUE_107577_VALUES", "(value INT, VALUE_foo_VALUES_bar_VALUE INT)"); + + boolean useSPS = false; + do { + final String testCase = String.format("Case [useSPS: %s]", useSPS ? "Y" : "N"); + + Properties props = new Properties(); + props.setProperty(PropertyKey.sslMode.getKeyName(), SslMode.DISABLED.name()); + props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true"); + props.setProperty(PropertyKey.queryInterceptors.getKeyName(), QueryInfoQueryInterceptor.class.getName()); + props.setProperty(PropertyKey.rewriteBatchedStatements.getKeyName(), "true"); + props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), Boolean.toString(useSPS)); + + try (Connection testConn = getConnectionWithProps(props)) { + QueryInfoQueryInterceptor.startCapturing(); + this.pstmt = testConn.prepareStatement("INSERT INTO VALUES_testBug_VALUE_107577_VALUES (value, VALUE_foo_VALUES_bar_VALUE) VALUES (?, ?)"); + this.pstmt.setInt(1, 1); + this.pstmt.setInt(2, 1); + this.pstmt.addBatch(); + this.pstmt.setInt(1, 2); + this.pstmt.setInt(2, 2); + this.pstmt.addBatch(); + this.pstmt.executeBatch(); + if (useSPS) { + QueryInfoQueryInterceptor.assertCapturedSql(testCase, + "INSERT INTO VALUES_testBug_VALUE_107577_VALUES (value, VALUE_foo_VALUES_bar_VALUE) VALUES (?, ?),(?, ?)"); + } else { + QueryInfoQueryInterceptor.assertCapturedSql(testCase, + "INSERT INTO VALUES_testBug_VALUE_107577_VALUES (value, VALUE_foo_VALUES_bar_VALUE) VALUES (1, 1),(2, 2)"); + } + } + } while (useSPS = !useSPS); + } + + public static class QueryInfoQueryInterceptor extends BaseQueryInterceptor { + private static boolean enabled = false; + private static List capturedSql = new ArrayList<>(); + + public static void startCapturing() { + enabled = true; + capturedSql.clear(); + } + + public static void assertCapturedSql(String testCase, String... expectedSql) { + enabled = false; + assertEquals(expectedSql.length, capturedSql.size(), testCase); + for (int i = 0; i < expectedSql.length; i++) { + assertEquals(expectedSql[i], capturedSql.get(i), testCase); + } + } + + @Override + public T preProcess(Supplier sql, Query interceptedQuery) { + if (enabled && interceptedQuery != null) { + capturedSql.add(sql.get()); + } + return super.preProcess(sql, interceptedQuery); + } + } + + /** + * Tests fix for Bug#34558945, PS using setCharacterStream() fails with "Incorrect string value" if the Java program encoding is not UTF-8 after 8.0.29. + * + * @throws Exception + */ + @Test + void testBug34558945() throws Exception { + createTable("testBug34558945Utf8", "(txt VARCHAR(100) COLLATE utf8mb4_general_ci)"); + createTable("testBug34558945SJis", "(txt VARCHAR(100) COLLATE sjis_japanese_ci)"); + + Properties props = new Properties(); + props.setProperty(PropertyKey.sslMode.getKeyName(), SslMode.DISABLED.name()); + props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true"); + + final String utf8 = "UTF-8"; + final String sjis = "Shift_JIS"; + final String[] charEncs = { null, utf8, sjis }; + final String value = "\u7ADC"; + + for (String ce : charEncs) { + for (String cce : charEncs) { + + if (ce == null) { + props.remove(PropertyKey.characterEncoding.getKeyName()); + } else { + props.setProperty(PropertyKey.characterEncoding.getKeyName(), ce); + } + if (cce == null) { + props.remove(PropertyKey.clobCharacterEncoding.getKeyName()); + } else { + props.setProperty(PropertyKey.clobCharacterEncoding.getKeyName(), cce); + } + + boolean useSPS = false; + do { + props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), Boolean.toString(useSPS)); + try (Connection testConn = getConnectionWithProps(props)) { + int records; + PreparedStatement testPStmt; + Statement testStmt; + + // Table with UTF-8 column. + records = 1; + testPStmt = testConn.prepareStatement("INSERT INTO testBug34558945Utf8 VALUES (?)"); + testPStmt.setString(1, value); + assertEquals(1, testPStmt.executeUpdate()); + testPStmt.setCharacterStream(1, new StringReader(value), value.length()); + if (sjis.equals(cce) || cce == null && sjis.equals(ce)) { + assertThrows(SQLException.class, ".*Incorrect string value:.*", testPStmt::executeUpdate); + } else { + assertEquals(1, testPStmt.executeUpdate()); + records = 2; + } + + testStmt = testConn.createStatement(); + this.rs = testStmt.executeQuery("SELECT * FROM testBug34558945Utf8"); + while (records-- > 0) { + assertTrue(this.rs.next()); + assertEquals(value, this.rs.getString(1)); + } + assertFalse(this.rs.next()); + testStmt.executeUpdate("TRUNCATE TABLE testBug34558945Utf8"); + + // Table with Shift_JIS column. + records = 1; + testPStmt = testConn.prepareStatement("INSERT INTO testBug34558945SJis VALUES (?)"); + testPStmt.setString(1, value); + assertEquals(1, testPStmt.executeUpdate()); + testPStmt.setCharacterStream(1, new StringReader(value), value.length()); + if (sjis.equals(cce) || cce == null && sjis.equals(ce)) { + assertEquals(1, testPStmt.executeUpdate()); + records = 2; + } else { + assertThrows(SQLException.class, ".*Incorrect string value:.*", testPStmt::executeUpdate); + } + + testStmt = testConn.createStatement(); + this.rs = testStmt.executeQuery("SELECT * FROM testBug34558945SJis"); + while (records-- > 0) { + assertTrue(this.rs.next()); + assertEquals(value, this.rs.getString(1)); + } + assertFalse(this.rs.next()); + testStmt.executeUpdate("TRUNCATE TABLE testBug34558945SJis"); + } + } while (useSPS = !useSPS); + } + } + } } diff --git a/src/test/java/testsuite/simple/StatementsTest.java b/src/test/java/testsuite/simple/StatementsTest.java index 506ff942d..a91cd0a1c 100644 --- a/src/test/java/testsuite/simple/StatementsTest.java +++ b/src/test/java/testsuite/simple/StatementsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2022, Oracle and/or its affiliates. + * Copyright (c) 2002, 2023, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -5258,6 +5258,7 @@ public void testQueryInfoParsingAndRewrittingSpecialCases() throws Exception { /* * Special Case 3: Table and column names. */ + // INSERT ... tbl (c1, c2) ... VALUES (?, ?) AS ... --> rewritable. QueryInfoQueryInterceptor.startCapturing(); this.pstmt = testConn.prepareStatement("INSERT INTO testQueryInfo (c1, c2) VALUES (?, ?) AS new(v1, v2)"); @@ -5280,7 +5281,7 @@ public void testQueryInfoParsingAndRewrittingSpecialCases() throws Exception { "INSERT INTO testQueryInfo (c1, c2) VALUES (3, 4) AS new(v1, v2)"); } - // INSERT ... tbl (c1, c2) ... VALUES(?, ? )ON DUPLICATE KEY UPDATE ... --> rewritable. + // INSERT ... tbl (c1, c2) ... VALUES (?, ?) ON DUPLICATE KEY UPDATE ... --> rewritable. QueryInfoQueryInterceptor.startCapturing(); this.pstmt = testConn.prepareStatement("INSERT INTO testQueryInfo (c1, c2) VALUES (?, ?) ON DUPLICATE KEY UPDATE c1 = VALUES(c2)"); this.pstmt.setInt(1, 1); @@ -5372,6 +5373,206 @@ public void testQueryInfoParsingAndRewrittingSpecialCases() throws Exception { QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO testQueryInfo VALUES (12, 3)", "INSERT INTO testQueryInfo VALUES (45, 6)"); } + /* + * Special Case 6: Multiple VALUES lists. + */ + + // INSERT ... VALUES (?, n), (?, m) --> rewritable. + QueryInfoQueryInterceptor.startCapturing(); + this.pstmt = testConn.prepareStatement("INSERT INTO testQueryInfo VALUES (?, 0), (?, 1)"); + this.pstmt.setInt(1, 1); + this.pstmt.setInt(2, 2); + this.pstmt.addBatch(); + this.pstmt.setInt(1, 3); + this.pstmt.setInt(2, 4); + this.pstmt.addBatch(); + this.pstmt.executeBatch(); + if (rwBS && useSPS) { + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO testQueryInfo VALUES (?, 0), (?, 1),(?, 0), (?, 1)"); + } else if (!rwBS && useSPS) { + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO testQueryInfo VALUES (?, 0), (?, 1)", + "INSERT INTO testQueryInfo VALUES (?, 0), (?, 1)"); + } else if (rwBS) { // && !useSPS + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO testQueryInfo VALUES (1, 0), (2, 1),(3, 0), (4, 1)"); + } else { // !rwBS && !useSPS + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO testQueryInfo VALUES (1, 0), (2, 1)", + "INSERT INTO testQueryInfo VALUES (3, 0), (4, 1)"); + } + + // INSERT ... VALUES (?, n), (?, m) AS ... --> rewritable. + QueryInfoQueryInterceptor.startCapturing(); + this.pstmt = testConn.prepareStatement("INSERT INTO testQueryInfo VALUES (?, 0), (?, 1) AS new(v1, v2)"); + this.pstmt.setInt(1, 1); + this.pstmt.setInt(2, 2); + this.pstmt.addBatch(); + this.pstmt.setInt(1, 3); + this.pstmt.setInt(2, 4); + this.pstmt.addBatch(); + this.pstmt.executeBatch(); + if (rwBS && useSPS) { + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO testQueryInfo VALUES (?, 0), (?, 1),(?, 0), (?, 1) AS new(v1, v2)"); + } else if (!rwBS && useSPS) { + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO testQueryInfo VALUES (?, 0), (?, 1) AS new(v1, v2)", + "INSERT INTO testQueryInfo VALUES (?, 0), (?, 1) AS new(v1, v2)"); + } else if (rwBS) { // && !useSPS + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO testQueryInfo VALUES (1, 0), (2, 1),(3, 0), (4, 1) AS new(v1, v2)"); + } else { // !rwBS && !useSPS + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO testQueryInfo VALUES (1, 0), (2, 1) AS new(v1, v2)", + "INSERT INTO testQueryInfo VALUES (3, 0), (4, 1) AS new(v1, v2)"); + } + + // INSERT ... VALUES (?, n), (?, m) ON DUPLICATE KEY UPDATE ... --> rewritable. + QueryInfoQueryInterceptor.startCapturing(); + this.pstmt = testConn.prepareStatement("INSERT INTO testQueryInfo VALUES (?, 0), (?, 1) ON DUPLICATE KEY UPDATE c1 = VALUES(c2)"); + this.pstmt.setInt(1, 1); + this.pstmt.setInt(2, 2); + this.pstmt.addBatch(); + this.pstmt.setInt(1, 3); + this.pstmt.setInt(2, 4); + this.pstmt.addBatch(); + this.pstmt.executeBatch(); + if (rwBS && useSPS) { + QueryInfoQueryInterceptor.assertCapturedSql(testCase, + "INSERT INTO testQueryInfo VALUES (?, 0), (?, 1),(?, 0), (?, 1) ON DUPLICATE KEY UPDATE c1 = VALUES(c2)"); + } else if (!rwBS && useSPS) { + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO testQueryInfo VALUES (?, 0), (?, 1) ON DUPLICATE KEY UPDATE c1 = VALUES(c2)", + "INSERT INTO testQueryInfo VALUES (?, 0), (?, 1) ON DUPLICATE KEY UPDATE c1 = VALUES(c2)"); + } else if (rwBS) { // && !useSPS + QueryInfoQueryInterceptor.assertCapturedSql(testCase, + "INSERT INTO testQueryInfo VALUES (1, 0), (2, 1),(3, 0), (4, 1) ON DUPLICATE KEY UPDATE c1 = VALUES(c2)"); + } else { // !rwBS && !useSPS + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO testQueryInfo VALUES (1, 0), (2, 1) ON DUPLICATE KEY UPDATE c1 = VALUES(c2)", + "INSERT INTO testQueryInfo VALUES (3, 0), (4, 1) ON DUPLICATE KEY UPDATE c1 = VALUES(c2)"); + } + + /* + * Special Case 7: "VALUE" as table and column name. + */ + + createTable("value", "(value INT)"); + + // INSERT ... VALUE (?) --> rewritable. + QueryInfoQueryInterceptor.startCapturing(); + this.pstmt = testConn.prepareStatement("INSERT INTO value (value) VALUE (?)"); + this.pstmt.setInt(1, 1); + this.pstmt.addBatch(); + this.pstmt.setInt(1, 2); + this.pstmt.addBatch(); + this.pstmt.executeBatch(); + if (rwBS && useSPS) { + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO value (value) VALUE (?),(?)"); + } else if (!rwBS && useSPS) { + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO value (value) VALUE (?)", "INSERT INTO value (value) VALUE (?)"); + } else if (rwBS) { // && !useSPS + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO value (value) VALUE (1),(2)"); + } else { // !rwBS && !useSPS + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO value (value) VALUE (1)", "INSERT INTO value (value) VALUE (2)"); + } + + // INSERT ... VALUE (?) AS ... --> rewritable. + QueryInfoQueryInterceptor.startCapturing(); + this.pstmt = testConn.prepareStatement("INSERT INTO value (value) VALUE (?) AS new(v)"); + this.pstmt.setInt(1, 1); + this.pstmt.addBatch(); + this.pstmt.setInt(1, 2); + this.pstmt.addBatch(); + this.pstmt.executeBatch(); + if (rwBS && useSPS) { + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO value (value) VALUE (?),(?) AS new(v)"); + } else if (!rwBS && useSPS) { + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO value (value) VALUE (?) AS new(v)", + "INSERT INTO value (value) VALUE (?) AS new(v)"); + } else if (rwBS) { // && !useSPS + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO value (value) VALUE (1),(2) AS new(v)"); + } else { // !rwBS && !useSPS + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO value (value) VALUE (1) AS new(v)", + "INSERT INTO value (value) VALUE (2) AS new(v)"); + } + + // INSERT ... VALUE (?) ON DUPLICATE KEY UPDATE ... --> rewritable. + QueryInfoQueryInterceptor.startCapturing(); + this.pstmt = testConn.prepareStatement("INSERT INTO value (value) VALUE (?) ON DUPLICATE KEY UPDATE value = VALUES(value)"); + this.pstmt.setInt(1, 1); + this.pstmt.addBatch(); + this.pstmt.setInt(1, 2); + this.pstmt.addBatch(); + this.pstmt.executeBatch(); + if (rwBS && useSPS) { + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO value (value) VALUE (?),(?) ON DUPLICATE KEY UPDATE value = VALUES(value)"); + } else if (!rwBS && useSPS) { + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO value (value) VALUE (?) ON DUPLICATE KEY UPDATE value = VALUES(value)", + "INSERT INTO value (value) VALUE (?) ON DUPLICATE KEY UPDATE value = VALUES(value)"); + } else if (rwBS) { // && !useSPS + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO value (value) VALUE (1),(2) ON DUPLICATE KEY UPDATE value = VALUES(value)"); + } else { // !rwBS && !useSPS + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO value (value) VALUE (1) ON DUPLICATE KEY UPDATE value = VALUES(value)", + "INSERT INTO value (value) VALUE (2) ON DUPLICATE KEY UPDATE value = VALUES(value)"); + } + + /* + * Special Case 8: "VALUE" as table name and "?" as column name. + */ + + createTable("value", "(`?` INT)"); + + // INSERT ... VALUE (?) --> rewritable. + QueryInfoQueryInterceptor.startCapturing(); + this.pstmt = testConn.prepareStatement("INSERT INTO value (`?`) VALUE (?)"); + this.pstmt.setInt(1, 1); + this.pstmt.addBatch(); + this.pstmt.setInt(1, 2); + this.pstmt.addBatch(); + this.pstmt.executeBatch(); + if (rwBS && useSPS) { + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO value (`?`) VALUE (?),(?)"); + } else if (!rwBS && useSPS) { + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO value (`?`) VALUE (?)", "INSERT INTO value (`?`) VALUE (?)"); + } else if (rwBS) { // && !useSPS + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO value (`?`) VALUE (1),(2)"); + } else { // !rwBS && !useSPS + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO value (`?`) VALUE (1)", "INSERT INTO value (`?`) VALUE (2)"); + } + + // INSERT ... VALUE (?) AS ... --> rewritable. + QueryInfoQueryInterceptor.startCapturing(); + this.pstmt = testConn.prepareStatement("INSERT INTO value (`?`) VALUE (?) AS `values`(`?`)"); + this.pstmt.setInt(1, 1); + this.pstmt.addBatch(); + this.pstmt.setInt(1, 2); + this.pstmt.addBatch(); + this.pstmt.executeBatch(); + if (rwBS && useSPS) { + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO value (`?`) VALUE (?),(?) AS `values`(`?`)"); + } else if (!rwBS && useSPS) { + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO value (`?`) VALUE (?) AS `values`(`?`)", + "INSERT INTO value (`?`) VALUE (?) AS `values`(`?`)"); + } else if (rwBS) { // && !useSPS + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO value (`?`) VALUE (1),(2) AS `values`(`?`)"); + } else { // !rwBS && !useSPS + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO value (`?`) VALUE (1) AS `values`(`?`)", + "INSERT INTO value (`?`) VALUE (2) AS `values`(`?`)"); + } + + // INSERT ... VALUE (?) ON DUPLICATE KEY UPDATE ... --> rewritable. + QueryInfoQueryInterceptor.startCapturing(); + this.pstmt = testConn.prepareStatement("INSERT INTO value (`?`) VALUE (?) ON DUPLICATE KEY UPDATE `?` = VALUES(`?`)"); + this.pstmt.setInt(1, 1); + this.pstmt.addBatch(); + this.pstmt.setInt(1, 2); + this.pstmt.addBatch(); + this.pstmt.executeBatch(); + if (rwBS && useSPS) { + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO value (`?`) VALUE (?),(?) ON DUPLICATE KEY UPDATE `?` = VALUES(`?`)"); + } else if (!rwBS && useSPS) { + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO value (`?`) VALUE (?) ON DUPLICATE KEY UPDATE `?` = VALUES(`?`)", + "INSERT INTO value (`?`) VALUE (?) ON DUPLICATE KEY UPDATE `?` = VALUES(`?`)"); + } else if (rwBS) { // && !useSPS + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO value (`?`) VALUE (1),(2) ON DUPLICATE KEY UPDATE `?` = VALUES(`?`)"); + } else { // !rwBS && !useSPS + QueryInfoQueryInterceptor.assertCapturedSql(testCase, "INSERT INTO value (`?`) VALUE (1) ON DUPLICATE KEY UPDATE `?` = VALUES(`?`)", + "INSERT INTO value (`?`) VALUE (2) ON DUPLICATE KEY UPDATE `?` = VALUES(`?`)"); + } + testConn.close(); } while ((useSPS = !useSPS) || (rwBS = !rwBS)); }