From 9a2685a722cb63e83fa2a640ff2ffe802813c6a7 Mon Sep 17 00:00:00 2001 From: Lance Andersen Date: Wed, 8 Oct 2025 10:05:49 -0400 Subject: [PATCH 01/10] JDBC 4.5 MR initial push --- .../share/classes/java/sql/Array.java | 38 ++- src/java.sql/share/classes/java/sql/Blob.java | 42 ++- src/java.sql/share/classes/java/sql/Clob.java | 40 ++- .../share/classes/java/sql/Connection.java | 277 +++++++++++++++++- .../classes/java/sql/DriverPropertyInfo.java | 11 +- .../share/classes/java/sql/JDBCType.java | 14 +- .../share/classes/java/sql/NClob.java | 11 +- .../share/classes/java/sql/SQLPermission.java | 26 +- .../share/classes/java/sql/SQLXML.java | 46 ++- .../share/classes/java/sql/Timestamp.java | 16 +- .../share/classes/java/sql/Types.java | 23 +- .../share/classes/java/sql/package-info.java | 40 ++- .../sql/testng/test/sql/ConnectionTests.java | 230 +++++++++++++++ .../sql/testng/test/sql/TimestampTests.java | 40 ++- test/jdk/java/sql/testng/util/BaseTest.java | 7 - 15 files changed, 764 insertions(+), 97 deletions(-) create mode 100644 test/jdk/java/sql/testng/test/sql/ConnectionTests.java diff --git a/src/java.sql/share/classes/java/sql/Array.java b/src/java.sql/share/classes/java/sql/Array.java index 1c42d3e5b7923..f3ca8fc16c882 100644 --- a/src/java.sql/share/classes/java/sql/Array.java +++ b/src/java.sql/share/classes/java/sql/Array.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -61,13 +61,19 @@ * If the connection's type map or a type map supplied to a method has no entry * for the base type, the elements are mapped according to the standard mapping. *

+ * To release resources used by the {@code Array} object, applications must call + * either the {@link #free()} or the {@link #close()} method. Any attempt to + * invoke a method other than {@link #free()} or {@link #close()} after the + * {@code Array} object has been closed, will result in a {@link SQLException} + * being thrown. + *

* All methods on the {@code Array} interface must be fully implemented if the * JDBC driver supports the data type. * * @since 1.2 */ -public interface Array { +public interface Array extends AutoCloseable { /** * Retrieves the SQL type name of the elements in @@ -345,21 +351,35 @@ ResultSet getResultSet (long index, int count, java.util.Map> map) throws SQLException; /** - * This method frees the {@code Array} object and releases the resources that - * it holds. The object is invalid once the {@code free} - * method is called. + * Closes and releases the resources held by this {@code Array} object. *

- * After {@code free} has been called, any attempt to invoke a - * method other than {@code free} will result in a {@code SQLException} - * being thrown. If {@code free} is called multiple times, the subsequent - * calls to {@code free} are treated as a no-op. + * If the {@code Array} object is already closed, then invoking this method + * has no effect. * * @throws SQLException if an error occurs releasing * the Array's resources * @throws SQLFeatureNotSupportedException if the JDBC driver does not support * this method * @since 1.6 + * @see #close() */ void free() throws SQLException; + /** + * Closes and releases the resources held by this {@code Array} object. + *

+ * If the {@code Array} object is already closed, then invoking this method + * has no effect. + * + * @throws SQLException if an error occurs releasing + * the Array's resources + * @throws SQLFeatureNotSupportedException if the JDBC driver + * does not support this method + * @implSpec The default implementation calls the {@link #free()} method. + * @see #free() + * @since 26 + */ + default void close() throws SQLException { + free(); + }; } diff --git a/src/java.sql/share/classes/java/sql/Blob.java b/src/java.sql/share/classes/java/sql/Blob.java index 5bf9cfcb7adf5..435ac692f3fc5 100644 --- a/src/java.sql/share/classes/java/sql/Blob.java +++ b/src/java.sql/share/classes/java/sql/Blob.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,7 +32,7 @@ * the Java programming language of an SQL * {@code BLOB} value. An SQL {@code BLOB} is a built-in type * that stores a Binary Large Object as a column value in a row of - * a database table. By default drivers implement {@code Blob} using + * a database table. By default, drivers implement {@code Blob} using * an SQL {@code locator(BLOB)}, which means that a * {@code Blob} object contains a logical pointer to the * SQL {@code BLOB} data rather than the data itself. @@ -50,13 +50,19 @@ * {@code BLOB} value. In addition, this interface has methods for updating * a {@code BLOB} value. *

+ * To release resources used by the {@code Blob} object, applications must call + * either the {@link #free()} or the {@link #close()} method. Any attempt to + * invoke a method other than {@link #free()} or {@link #close()} after the + * {@code Blob} object has been closed, will result in a {@link SQLException} + * being thrown. + *

* All methods on the {@code Blob} interface must be fully implemented if the * JDBC driver supports the data type. * * @since 1.2 */ -public interface Blob { +public interface Blob extends AutoCloseable { /** * Returns the number of bytes in the {@code BLOB} value @@ -266,20 +272,17 @@ public interface Blob { void truncate(long len) throws SQLException; /** - * This method frees the {@code Blob} object and releases the resources that - * it holds. The object is invalid once the {@code free} - * method is called. + * Closes and releases the resources held by this {@code Blob} object. *

- * After {@code free} has been called, any attempt to invoke a - * method other than {@code free} will result in an {@code SQLException} - * being thrown. If {@code free} is called multiple times, the subsequent - * calls to {@code free} are treated as a no-op. + * If the {@code Blob} object is already closed, then invoking this method + * has no effect. * * @throws SQLException if an error occurs releasing * the Blob's resources * @throws SQLFeatureNotSupportedException if the JDBC driver * does not support this method * @since 1.6 + * @see #close() */ void free() throws SQLException; @@ -303,4 +306,23 @@ public interface Blob { * @since 1.6 */ InputStream getBinaryStream(long pos, long length) throws SQLException; + + /** + * Closes and releases the resources held by this {@code Blob} object. + *

+ * If the {@code Blob} object is already closed, then invoking this method + * has no effect. + * + * @implSpec The default implementation calls the {@link #free()} method. + * + * @throws SQLException if an error occurs releasing + * the Blob's resources + * @throws SQLFeatureNotSupportedException if the JDBC driver + * does not support this method + * @since 26 + * @see #free() + */ + default void close() throws SQLException { + free(); + }; } diff --git a/src/java.sql/share/classes/java/sql/Clob.java b/src/java.sql/share/classes/java/sql/Clob.java index 15e6897f71112..b9becb5d9ece5 100644 --- a/src/java.sql/share/classes/java/sql/Clob.java +++ b/src/java.sql/share/classes/java/sql/Clob.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,7 +33,7 @@ * An SQL {@code CLOB} is a built-in type * that stores a Character Large Object as a column value in a row of * a database table. - * By default drivers implement a {@code Clob} object using an SQL + * By default, drivers implement a {@code Clob} object using an SQL * {@code locator(CLOB)}, which means that a {@code Clob} object * contains a logical pointer to the SQL {@code CLOB} data rather than * the data itself. A {@code Clob} object is valid for the duration @@ -49,13 +49,19 @@ * access an SQL {@code CLOB} value. In addition, this interface * has methods for updating a {@code CLOB} value. *

+ * To release resources used by the {@code Clob} object, applications must call + * either the {@link #free()} or the {@link #close()} method. Any attempt to + * invoke a method other than {@link #free()} or {@link #close()} after the + * {@code Clob} object has been closed, will result in a {@link SQLException} + * being thrown. + *

* All methods on the {@code Clob} interface must be * fully implemented if the JDBC driver supports the data type. * * @since 1.2 */ -public interface Clob { +public interface Clob extends AutoCloseable { /** * Retrieves the number of characters @@ -310,14 +316,10 @@ public interface Clob { void truncate(long len) throws SQLException; /** - * This method releases the resources that the {@code Clob} object - * holds. The object is invalid once the {@code free} method - * is called. + * Closes and releases the resources held by this {@code Clob} object. *

- * After {@code free} has been called, any attempt to invoke a - * method other than {@code free} will result in a {@code SQLException} - * being thrown. If {@code free} is called multiple times, the subsequent - * calls to {@code free} are treated as a no-op. + * If the {@code Clob} object is already closed, then invoking this method + * has no effect. * * @throws SQLException if an error occurs releasing * the Clob's resources @@ -325,6 +327,7 @@ public interface Clob { * @throws SQLFeatureNotSupportedException if the JDBC driver * does not support this method * @since 1.6 + * @see #close() */ void free() throws SQLException; @@ -350,4 +353,21 @@ public interface Clob { */ Reader getCharacterStream(long pos, long length) throws SQLException; + /** + * Closes and releases the resources held by this {@code Clob} object. + *

+ * If the {@code Clob} object is already closed, then invoking this method + * has no effect. + * + * @throws SQLException if an error occurs releasing + * the Clob's resources + * @throws SQLFeatureNotSupportedException if the JDBC driver + * does not support this method + * @implSpec The default implementation calls the {@link #free()} method. + * @see #free() + * @since 26 + */ + default void close() throws SQLException { + free(); + }; } diff --git a/src/java.sql/share/classes/java/sql/Connection.java b/src/java.sql/share/classes/java/sql/Connection.java index 19f283f576218..05f64e57d94e0 100644 --- a/src/java.sql/share/classes/java/sql/Connection.java +++ b/src/java.sql/share/classes/java/sql/Connection.java @@ -28,6 +28,7 @@ import java.util.Properties; import java.util.concurrent.Executor; import java.util.Map; +import java.util.regex.Pattern; /** *

A connection (session) with a specific @@ -43,8 +44,8 @@ * should use the appropriate {@code Connection} method such as * {@code setAutoCommit} or {@code setTransactionIsolation}. * Applications should not invoke SQL commands directly to change the connection's - * configuration when there is a JDBC method available. By default a {@code Connection} object is in - * auto-commit mode, which means that it automatically commits changes + * configuration when there is a JDBC method available. By default, a {@code Connection} + * object is in auto-commit mode, which means that it automatically commits changes * after executing each statement. If auto-commit mode has been * disabled, the method {@code commit} must be called explicitly in * order to commit changes; otherwise, database changes will not be saved. @@ -1676,5 +1677,277 @@ default void setShardingKey(ShardingKey shardingKey, ShardingKey superShardingKe default void setShardingKey(ShardingKey shardingKey) throws SQLException { throw new SQLFeatureNotSupportedException("setShardingKey not implemented"); } + // JDBC 4.5 + /** + * Returns a {@code String} enclosed in single quotes. Any occurrence of a + * single quote within the string will be replaced by two single quotes. + * + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Examples of the conversion:
ValueResult
Hello 'Hello'
G'Day 'G''Day'
'G''Day''''G''''Day'''
I'''M 'I''''''M'
+ *
+ * @implNote + * JDBC driver implementations may need to provide their own implementation + * of this method in order to meet the requirements of the underlying + * datasource. + * @param val a character string + * @return A string enclosed by single quotes with every single quote + * converted to two single quotes + * @throws NullPointerException if val is {@code null} + * @throws SQLException if a database access error occurs + * + * @since 26 + */ + default String enquoteLiteral(String val) throws SQLException { + return "'" + val.replace("'", "''") + "'"; + } + + /** + * Returns a SQL identifier. If {@code identifier} is a simple SQL identifier: + * + * + * If {@code identifier} is not a simple SQL identifier, {@code identifier} will be + * enclosed in double quotes if not already present. If the datasource does + * not support double quotes for delimited identifiers, the + * identifier should be enclosed by the string returned from + * {@link DatabaseMetaData#getIdentifierQuoteString}. If the datasource + * does not support delimited identifiers, a + * {@code SQLFeatureNotSupportedException} should be thrown. + *

+ * A {@code SQLException} will be thrown if {@code identifier} contains any + * characters invalid in a delimited identifier or the identifier length is + * invalid for the datasource. + * + * @implSpec + * The default implementation uses the following criteria to + * determine a valid simple SQL identifier: + *

+ * + * The default implementation will throw a {@code SQLException} if: + * + *
+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Examples of the conversion:
identifieralwaysQuoteResult
HellofalseHello
Hellotrue"Hello"
G'Dayfalse"G'Day"
"Bruce Wayne"false"Bruce Wayne"
"Bruce Wayne"true"Bruce Wayne"
GoodDay$false"GoodDay$"
Hello"WorldfalseSQLException
"Hello"World"falseSQLException
+ *
+ * @implNote + * JDBC driver implementations may need to provide their own implementation + * of this method in order to meet the requirements of the underlying + * datasource. + * @param identifier a SQL identifier + * @param alwaysQuote indicates if a simple SQL identifier should be + * returned as a quoted identifier + * @return A simple SQL identifier or a delimited identifier + * @throws SQLException if identifier is not a valid identifier + * @throws SQLFeatureNotSupportedException if the datasource does not support + * delimited identifiers + * @throws NullPointerException if identifier is {@code null} + * + * @since 26 + */ + default String enquoteIdentifier(String identifier, boolean alwaysQuote) throws SQLException { + int len = identifier.length(); + if (len < 1 || len > 128) { + throw new SQLException("Invalid name"); + } + if (Pattern.compile("[\\p{Alpha}][\\p{Alnum}_]*").matcher(identifier).matches()) { + return alwaysQuote ? "\"" + identifier + "\"" : identifier; + } + if (identifier.matches("^\".+\"$")) { + identifier = identifier.substring(1, len - 1); + } + if (Pattern.compile("[^\u0000\"]+").matcher(identifier).matches()) { + return "\"" + identifier + "\""; + } else { + throw new SQLException("Invalid name"); + } + } + + /** + * Retrieves whether {@code identifier} is a simple SQL identifier. + * + * @implSpec The default implementation uses the following criteria to + * determine a valid simple SQL identifier: + * + * + *
+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Examples of the conversion:
identifierSimple Identifier
Hellotrue
G'Dayfalse
"Bruce Wayne"false
GoodDay$false
Hello"Worldfalse
"Hello"World"false
+ *
+ * @implNote JDBC driver implementations may need to provide their own + * implementation of this method in order to meet the requirements of the + * underlying datasource. + * @param identifier a SQL identifier + * @return true if a simple SQL identifier, false otherwise + * @throws NullPointerException if identifier is {@code null} + * @throws SQLException if a database access error occurs + * + * @since 26 + */ + default boolean isSimpleIdentifier(String identifier) throws SQLException { + int len = identifier.length(); + return len >= 1 && len <= 128 + && Pattern.compile("[\\p{Alpha}][\\p{Alnum}_]*").matcher(identifier).matches(); + } + + /** + * Returns a {@code String} representing a National Character Set Literal + * enclosed in single quotes and prefixed with a upper case letter N. + * Any occurrence of a single quote within the string will be replaced + * by two single quotes. + * + *
+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Examples of the conversion:
ValueResult
Hello N'Hello'
G'Day N'G''Day'
'G''Day'N'''G''''Day'''
I'''M N'I''''''M'
N'Hello' N'N''Hello'''
+ *
+ * @implNote + * JDBC driver implementations may need to provide their own implementation + * of this method in order to meet the requirements of the underlying + * datasource. An implementation of enquoteNCharLiteral may accept a different + * set of characters than that accepted by the same drivers implementation of + * enquoteLiteral. + * @param val a character string + * @return the result of replacing every single quote character in the + * argument by two single quote characters where this entire result is + * then prefixed with 'N'. + * @throws NullPointerException if val is {@code null} + * @throws SQLException if a database access error occurs + * + * @since 26 + */ + default String enquoteNCharLiteral(String val) throws SQLException { + return "N'" + val.replace("'", "''") + "'"; + } } diff --git a/src/java.sql/share/classes/java/sql/DriverPropertyInfo.java b/src/java.sql/share/classes/java/sql/DriverPropertyInfo.java index c5f5fc30ee904..d7ea21bd30c25 100644 --- a/src/java.sql/share/classes/java/sql/DriverPropertyInfo.java +++ b/src/java.sql/share/classes/java/sql/DriverPropertyInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,12 +25,13 @@ package java.sql; +import java.util.Properties; + /** *

Driver properties for making a connection. The - * {@code DriverPropertyInfo} class is of interest only to advanced programmers - * who need to interact with a Driver via the method - * {@code getDriverProperties} to discover - * and supply properties for connections. + * {@code DriverPropertyInfo} class is of interest only to advanced programmers. + * The method {@linkplain Driver#getPropertyInfo(String, Properties)} may be used + * to discover Driver properties. * * @since 1.1 */ diff --git a/src/java.sql/share/classes/java/sql/JDBCType.java b/src/java.sql/share/classes/java/sql/JDBCType.java index 9c9d314b7f117..fb7fa73bbbcf3 100644 --- a/src/java.sql/share/classes/java/sql/JDBCType.java +++ b/src/java.sql/share/classes/java/sql/JDBCType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -200,7 +200,17 @@ public enum JDBCType implements SQLType { /** * Identifies the generic SQL type {@code TIMESTAMP_WITH_TIMEZONE}. */ - TIMESTAMP_WITH_TIMEZONE(Types.TIMESTAMP_WITH_TIMEZONE); + TIMESTAMP_WITH_TIMEZONE(Types.TIMESTAMP_WITH_TIMEZONE), + + /** + * Identifies the generic SQL type {@code DECFLOAT}. + */ + DECFLOAT(Types.DECFLOAT), + + /** + * Identifies the generic SQL type {@code DECFLOAT}. + */ + JSON(Types.JSON); /** * The Integer value for the JDBCType. It maps to a value in diff --git a/src/java.sql/share/classes/java/sql/NClob.java b/src/java.sql/share/classes/java/sql/NClob.java index 587f6e1f843c6..0639f8941460d 100644 --- a/src/java.sql/share/classes/java/sql/NClob.java +++ b/src/java.sql/share/classes/java/sql/NClob.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,7 +31,8 @@ * An SQL {@code NCLOB} is a built-in type * that stores a Character Large Object using the National Character Set * as a column value in a row of a database table. - *

The {@code NClob} interface extends the {@code Clob} interface + *

+ * The {@code NClob} interface extends the {@code Clob} interface * which provides methods for getting the * length of an SQL {@code NCLOB} value, * for materializing a {@code NCLOB} value on the client, and for @@ -44,6 +45,12 @@ * access an SQL {@code NCLOB} value. In addition, this interface * has methods for updating a {@code NCLOB} value. *

+ * To release resources used by the {@code NClob} object, applications must call + * either the {@link #free()} or the {@link #close()} method. Any attempt to + * invoke a method other than {@link #free()} or {@link #close()} after the + * {@code NClob} object has been closed, will result in a {@link SQLException} + * being thrown. + *

* All methods on the {@code NClob} interface must be fully implemented if the * JDBC driver supports the data type. * diff --git a/src/java.sql/share/classes/java/sql/SQLPermission.java b/src/java.sql/share/classes/java/sql/SQLPermission.java index 84e41c51a33a2..0507d440425bf 100644 --- a/src/java.sql/share/classes/java/sql/SQLPermission.java +++ b/src/java.sql/share/classes/java/sql/SQLPermission.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,30 +29,14 @@ import java.security.*; /** - * A {@code SQLPermission} object contains - * a name (also referred to as a "target name") but no actions - * list; there is either a named permission or there is not. - * The target name is the name of the permission. The - * naming convention follows the hierarchical property naming convention. - * In addition, an asterisk - * may appear at the end of the name, following a ".", or by itself, to - * signify a wildcard match. For example: {@code loadLibrary.*} - * and {@code *} signify a wildcard match, - * while {@code *loadLibrary} and {@code a*b} do not. - * - * @apiNote - * This permission cannot be used for controlling access to resources - * as the Security Manager is no longer supported. + *This class was only useful in conjunction with the {@link java.lang.SecurityManager}, + * which is no longer supported. There is no replacement for this class. * * @since 1.3 - * @see java.security.BasicPermission - * @see java.security.Permission - * @see java.security.Permissions - * @see java.security.PermissionCollection - * @see java.lang.SecurityManager * + * @deprecated There is no replacement for this class. */ - +@Deprecated(since="26", forRemoval=true) public final class SQLPermission extends BasicPermission { /** diff --git a/src/java.sql/share/classes/java/sql/SQLXML.java b/src/java.sql/share/classes/java/sql/SQLXML.java index 3edae8a950881..da8fe3ecf213c 100644 --- a/src/java.sql/share/classes/java/sql/SQLXML.java +++ b/src/java.sql/share/classes/java/sql/SQLXML.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -170,15 +170,20 @@ * The conceptual states of writable and not writable determine if one * of the writing APIs will set a value or throw an exception. *

- * The state moves from readable to not readable once free() or any of the + * The state moves from readable to not readable once close(), free() or any of the * reading APIs are called: getBinaryStream(), getCharacterStream(), getSource(), and getString(). * Implementations may also change the state to not writable when this occurs. *

- * The state moves from writable to not writable once free() or any of the + * The state moves from writable to not writable once close(), free() or any of the * writing APIs are called: setBinaryStream(), setCharacterStream(), setResult(), and setString(). * Implementations may also change the state to not readable when this occurs. - * - *

+ *

+ * To release resources used by the {@code SQLXML} object, applications must call + * either the {@link #free()} or the {@link #close()} method. Any attempt to + * invoke a method other than {@link #free()} or {@link #close()} after the + * {@code SQLXML} object has been closed, will result in a {@link SQLException} + * being thrown. + *

* All methods on the {@code SQLXML} interface must be fully implemented if the * JDBC driver supports the data type. * @@ -188,21 +193,19 @@ * @see javax.xml.xpath * @since 1.6 */ -public interface SQLXML +public interface SQLXML extends AutoCloseable { /** - * This method closes this object and releases the resources that it held. - * The SQL XML object becomes invalid and neither readable or writable - * when this method is called. + * Closes and releases the resources held by this {@code SQLXML} object. + *

+ * If the {@code SQLXML} object is already closed, then invoking this method + * has no effect. * - * After {@code free} has been called, any attempt to invoke a - * method other than {@code free} will result in a {@code SQLException} - * being thrown. If {@code free} is called multiple times, the subsequent - * calls to {@code free} are treated as a no-op. * @throws SQLException if there is an error freeing the XML value. * @throws SQLFeatureNotSupportedException if the JDBC driver does not support * this method * @since 1.6 + * @see #close() */ void free() throws SQLException; @@ -424,4 +427,21 @@ public interface SQLXML */ T setResult(Class resultClass) throws SQLException; + /** + * Closes and releases the resources held by this {@code SQLXML} object. + *

+ * If the {@code SQLXML} object is already closed, then invoking this method + * has no effect. + * + * @throws SQLException if an error occurs releasing + * the SQLXML's resources + * @throws SQLFeatureNotSupportedException if the JDBC driver + * does not support this method + * @implSpec The default implementation calls the {@link #free()} method. + * @see #free() + * @since 26 + */ + default void close() throws SQLException { + free(); + }; } diff --git a/src/java.sql/share/classes/java/sql/Timestamp.java b/src/java.sql/share/classes/java/sql/Timestamp.java index a91ab7210c58e..3d16335d71eba 100644 --- a/src/java.sql/share/classes/java/sql/Timestamp.java +++ b/src/java.sql/share/classes/java/sql/Timestamp.java @@ -54,10 +54,7 @@ * because the nanos component of a date is unknown. * As a result, the {@code Timestamp.equals(Object)} * method is not symmetric with respect to the - * {@code java.util.Date.equals(Object)} - * method. Also, the {@code hashCode} method uses the underlying - * {@code java.util.Date} - * implementation and therefore does not include nanos in its computation. + * {@code java.util.Date.equals(Object)} method. *

* Due to the differences between the {@code Timestamp} class * and the {@code java.util.Date} @@ -465,10 +462,15 @@ public int compareTo(java.util.Date o) { } /** - * {@inheritDoc} + * Returns a hash code value for this Timestamp. The result is the + * exclusive OR of the two halves of the primitive {@code long} + * value returned by the {@link #getTime} method. That is, + * the hash code is the value of the expression: + * {@snippet : + * (int)(this.getTime()^(this.getTime() >>> 32)) + * } * - * The {@code hashCode} method uses the underlying {@code java.util.Date} - * implementation and therefore does not include nanos in its computation. + * @return a hash code value for this Timestamp. * */ @Override diff --git a/src/java.sql/share/classes/java/sql/Types.java b/src/java.sql/share/classes/java/sql/Types.java index 2b3b571647b13..778d782ddaf85 100644 --- a/src/java.sql/share/classes/java/sql/Types.java +++ b/src/java.sql/share/classes/java/sql/Types.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -339,6 +339,27 @@ public class Types { */ public static final int TIMESTAMP_WITH_TIMEZONE = 2014; + + //--------------------------JDBC 4.5 ----------------------------- + + /** + * The constant in the Java programming language, sometimes referred to + * as a type code, that identifies the generic SQL type + * {@code DECFLOAT}. + * + * @since 26 + */ + public static final int DECFLOAT = 2015; + + /** + * The constant in the Java programming language, sometimes referred to + * as a type code, that identifies the generic SQL type + * {@code JSON}. + * + * @since 26 + */ + public static final int JSON = 2016; + // Prevent instantiation private Types() {} } diff --git a/src/java.sql/share/classes/java/sql/package-info.java b/src/java.sql/share/classes/java/sql/package-info.java index 495119effac96..927e0f746d5f9 100644 --- a/src/java.sql/share/classes/java/sql/package-info.java +++ b/src/java.sql/share/classes/java/sql/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,20 +38,22 @@ * use and update data from a spread sheet, flat file, or any other tabular * data source. * - *

What the JDBC 4.3 API Includes

- * The JDBC 4.3 API includes both + *

What the JDBC 4.5 API Includes

+ * The JDBC 4.5 API includes both * the {@code java.sql} package, referred to as the JDBC core API, * and the {@code javax.sql} package, referred to as the JDBC Optional * Package API. This complete JDBC API - * is included in the Java Standard Edition (Java SE), version 7. + * is included in the Java Standard Edition (Java SE). * The {@code javax.sql} package extends the functionality of the JDBC API * from a client-side API to a server-side API, and it is an essential part * of the Java Enterprise Edition * (Java EE) technology. * *

Versions

- * The JDBC 4.3 API incorporates all of the previous JDBC API versions: + * The JDBC 4.5 API incorporates all the previous JDBC API versions: * * + *

{@code java.sql} and {@code javax.sql} Features Introduced in the JDBC 4.5 API

+ * + *

{@code java.sql} and {@code javax.sql} Features Introduced in the JDBC 4.4 API

+ * *

{@code java.sql} and {@code javax.sql} Features Introduced in the JDBC 4.3 API

* * @since 1.1 */ diff --git a/test/jdk/java/sql/testng/test/sql/ConnectionTests.java b/test/jdk/java/sql/testng/test/sql/ConnectionTests.java new file mode 100644 index 0000000000000..a3b633a4f3021 --- /dev/null +++ b/test/jdk/java/sql/testng/test/sql/ConnectionTests.java @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package test.sql; + +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import util.BaseTest; +import util.StubConnection; + +import java.sql.SQLException; + +import static org.testng.Assert.assertEquals; + +public class ConnectionTests extends BaseTest { + + protected StubConnection conn; + protected static String maxIdentifier; + + @BeforeMethod + public void setUpMethod() throws Exception { + conn = new StubConnection(); + } + + @BeforeClass + public static void setUpClass() throws Exception { + int maxLen = 128; + StringBuilder s = new StringBuilder(maxLen); + for (int i = 0; i < maxLen; i++) { + s.append('a'); + } + maxIdentifier = s.toString(); + } + /* + * Verify that enquoteLiteral creates a valid literal and converts every + * single quote to two single quotes + */ + + @Test(dataProvider = "validEnquotedLiteralValues") + public void test00(String s, String expected) throws SQLException { + assertEquals(conn.enquoteLiteral(s), expected); + } + + /* + * Validate a NullPointerException is thrown if the string passed to + * enquoteLiteral is null + */ + @Test(expectedExceptions = NullPointerException.class) + public void test01() throws SQLException { + conn.enquoteLiteral(null); + + } + + /* + * Validate that enquoteIdentifier returns the expected value + */ + @Test(dataProvider = "validIdentifierValues") + public void test02(String s, boolean alwaysQuote, String expected) throws SQLException { + assertEquals(conn.enquoteIdentifier(s, alwaysQuote), expected); + + } + + /* + * Validate that a SQLException is thrown for values that are not valid + * for a SQL identifier + */ + @Test(dataProvider = "invalidIdentifierValues", + expectedExceptions = SQLException.class) + public void test03(String s, boolean alwaysQuote) throws SQLException { + conn.enquoteIdentifier(s, alwaysQuote); + + } + + /* + * Validate a NullPointerException is thrown is the string passed to + * enquoteIdentiifer is null + */ + @Test(dataProvider = "trueFalse", + expectedExceptions = NullPointerException.class) + public void test04(boolean alwaysQuote) throws SQLException { + conn.enquoteIdentifier(null, alwaysQuote); + } + + /* + * Validate that isSimpleIdentifier returns the expected value + */ + @Test(dataProvider = "simpleIdentifierValues") + public void test05(String s, boolean expected) throws SQLException { + assertEquals(conn.isSimpleIdentifier(s), expected); + } + + /* + * Validate a NullPointerException is thrown if the string passed to + * isSimpleIdentifier is null + */ + @Test(expectedExceptions = NullPointerException.class) + public void test06() throws SQLException { + conn.isSimpleIdentifier(null); + } + + /* + * Verify that enquoteLiteral creates a valid literal and converts every + * single quote to two single quotes + */ + @Test(dataProvider = "validEnquotedNCharLiteralValues") + public void test07(String s, String expected) throws SQLException { + assertEquals(conn.enquoteNCharLiteral(s), expected); + } + + /* + * Validate a NullPointerException is thrown if the string passed to + * enquoteNCharLiteral is null + */ + @Test(expectedExceptions = NullPointerException.class) + public void test08() throws SQLException { + conn.enquoteNCharLiteral(null); + } + + /* + * DataProvider used to provide strings that will be used to validate + * that enquoteLiteral converts a string to a literal and every instance of + * a single quote will be converted into two single quotes in the literal. + */ + @DataProvider(name = "validEnquotedLiteralValues") + protected Object[][] validEnquotedLiteralValues() { + return new Object[][]{ + {"Hello", "'Hello'"}, + {"G'Day", "'G''Day'"}, + {"'G''Day'", "'''G''''Day'''"}, + {"I'''M", "'I''''''M'"}, + {"The Dark Knight", "'The Dark Knight'"} + }; + } + + /* + * DataProvider used to provide strings that will be used to validate + * that enqouteIdentifier returns a simple SQL Identifier or a double + * quoted identifier + */ + @DataProvider(name = "validIdentifierValues") + protected Object[][] validEnquotedIdentifierValues() { + return new Object[][]{ + {"b", false, "b"}, + {"b", true, "\"b\""}, + {maxIdentifier, false, maxIdentifier}, + {maxIdentifier, true, "\"" + maxIdentifier + "\""}, + {"Hello", false, "Hello"}, + {"Hello", true, "\"Hello\""}, + {"G'Day", false, "\"G'Day\""}, + {"G'Day", true, "\"G'Day\""}, + {"Bruce Wayne", false, "\"Bruce Wayne\""}, + {"Bruce Wayne", true, "\"Bruce Wayne\""}, + {"GoodDay$", false, "\"GoodDay$\""}, + {"GoodDay$", true, "\"GoodDay$\""},}; + } + + /* + * DataProvider used to provide strings are invalid for enquoteIdentifier + * resulting in a SQLException being thrown + */ + @DataProvider(name = "invalidIdentifierValues") + protected Object[][] invalidEnquotedIdentifierValues() { + return new Object[][]{ + {"Hel\"lo", false}, + {"\"Hel\"lo\"", true}, + {"Hello" + '\0', false}, + {"", false}, + {maxIdentifier + 'a', false},}; + } + + /* + * DataProvider used to provide strings that will be used to validate + * that isSimpleIdentifier returns the correct value based on the + * identifier specified. + */ + @DataProvider(name = "simpleIdentifierValues") + protected Object[][] simpleIdentifierValues() { + return new Object[][]{ + {"b", true}, + {"Hello", true}, + {"\"Gotham\"", false}, + {"G'Day", false}, + {"Bruce Wayne", false}, + {"GoodDay$", false}, + {"Dick_Grayson", true}, + {"Batmobile1966", true}, + {maxIdentifier, true}, + {maxIdentifier + 'a', false}, + {"", false},}; + } + + /* + * DataProvider used to provide strings that will be used to validate + * that enquoteNCharLiteral converts a string to a National Character + * literal and every instance of + * a single quote will be converted into two single quotes in the literal. + */ + @DataProvider(name = "validEnquotedNCharLiteralValues") + protected Object[][] validEnquotedNCharLiteralValues() { + return new Object[][]{ + {"Hello", "N'Hello'"}, + {"G'Day", "N'G''Day'"}, + {"'G''Day'", "N'''G''''Day'''"}, + {"I'''M", "N'I''''''M'"}, + {"N'Hello'", "N'N''Hello'''"}, + {"The Dark Knight", "N'The Dark Knight'"} + }; + } +} diff --git a/test/jdk/java/sql/testng/test/sql/TimestampTests.java b/test/jdk/java/sql/testng/test/sql/TimestampTests.java index fefe3276d47a1..6baea9fa26f4b 100644 --- a/test/jdk/java/sql/testng/test/sql/TimestampTests.java +++ b/test/jdk/java/sql/testng/test/sql/TimestampTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -667,6 +667,44 @@ public void test53() { expectThrows(IllegalArgumentException.class, () -> Timestamp.from(Instant.MIN)); } + /* + * Validate that two Timestamp hashCode values are equal when + * the Timestamp values match, including the nanos. + */ + @Test + public void test54() { + long t = System.currentTimeMillis(); + Timestamp ts1 = new Timestamp(t); + Timestamp ts2 = new Timestamp(t); + ts1.setNanos(123456789); + ts2.setNanos(123456789); + assertTrue(ts1.equals(ts1)); + assertTrue(ts2.equals(ts2)); + assertTrue(ts1.equals(ts2)); + // As the Timestamp values, including the nanos are the same, the hashCode's + // should be equal + assertEquals(ts1.hashCode(), ts2.hashCode()); + } + + /* + * Validate that two Timestamp hashCode values are not equal when only + * the nanos value for the Timestamp differ. + */ + @Test + public void test55() { + long t = System.currentTimeMillis(); + Timestamp ts1 = new Timestamp(t); + Timestamp ts2 = new Timestamp(t); + // Modify the nanos so that the Timestamp values differ + ts1.setNanos(123456789); + ts2.setNanos(987654321); + assertTrue(ts1.equals(ts1)); + assertTrue(ts2.equals(ts2)); + assertFalse(ts1.equals(ts2)); + // As the nanos differ, the hashCode values should differ + assertNotEquals(ts1.hashCode(), ts2.hashCode()); + } + /* * DataProvider used to provide Timestamps which are not valid and are used * to validate that an IllegalArgumentException will be thrown from the diff --git a/test/jdk/java/sql/testng/util/BaseTest.java b/test/jdk/java/sql/testng/util/BaseTest.java index 6821940b7cea2..11e156f878bd6 100644 --- a/test/jdk/java/sql/testng/util/BaseTest.java +++ b/test/jdk/java/sql/testng/util/BaseTest.java @@ -92,13 +92,6 @@ protected T serializeDeserializeObject(T o) return o1; } - /* - * Utility Method used to set the current Policy - */ - protected static void setPolicy(Policy p) { - Policy.setPolicy(p); - } - /* * DataProvider used to specify the value to set and check for * methods using boolean values From 99495e80951b6204400fcb45eb59f5e6ed6b2323 Mon Sep 17 00:00:00 2001 From: Lance Andersen Date: Fri, 10 Oct 2025 11:59:34 -0400 Subject: [PATCH 02/10] Doc clean up based on feedback --- src/java.sql/share/classes/java/sql/Array.java | 2 +- src/java.sql/share/classes/java/sql/Connection.java | 2 +- src/java.sql/share/classes/java/sql/DriverPropertyInfo.java | 2 +- src/java.sql/share/classes/java/sql/JDBCType.java | 4 ++++ src/java.sql/share/classes/java/sql/SQLPermission.java | 2 +- src/java.sql/share/classes/java/sql/SQLXML.java | 2 +- 6 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/java.sql/share/classes/java/sql/Array.java b/src/java.sql/share/classes/java/sql/Array.java index f3ca8fc16c882..a027434a00a74 100644 --- a/src/java.sql/share/classes/java/sql/Array.java +++ b/src/java.sql/share/classes/java/sql/Array.java @@ -73,7 +73,7 @@ * @since 1.2 */ -public interface Array extends AutoCloseable { +public interface Array extends AutoCloseable { /** * Retrieves the SQL type name of the elements in diff --git a/src/java.sql/share/classes/java/sql/Connection.java b/src/java.sql/share/classes/java/sql/Connection.java index 05f64e57d94e0..8815de4d067d6 100644 --- a/src/java.sql/share/classes/java/sql/Connection.java +++ b/src/java.sql/share/classes/java/sql/Connection.java @@ -1894,7 +1894,7 @@ default String enquoteIdentifier(String identifier, boolean alwaysQuote) throws * implementation of this method in order to meet the requirements of the * underlying datasource. * @param identifier a SQL identifier - * @return true if a simple SQL identifier, false otherwise + * @return true if a simple SQL identifier, false otherwise * @throws NullPointerException if identifier is {@code null} * @throws SQLException if a database access error occurs * diff --git a/src/java.sql/share/classes/java/sql/DriverPropertyInfo.java b/src/java.sql/share/classes/java/sql/DriverPropertyInfo.java index d7ea21bd30c25..426a49b7ae025 100644 --- a/src/java.sql/share/classes/java/sql/DriverPropertyInfo.java +++ b/src/java.sql/share/classes/java/sql/DriverPropertyInfo.java @@ -30,7 +30,7 @@ /** *

Driver properties for making a connection. The * {@code DriverPropertyInfo} class is of interest only to advanced programmers. - * The method {@linkplain Driver#getPropertyInfo(String, Properties)} may be used + * The method {@link Driver#getPropertyInfo(String, Properties)} may be used * to discover Driver properties. * * @since 1.1 diff --git a/src/java.sql/share/classes/java/sql/JDBCType.java b/src/java.sql/share/classes/java/sql/JDBCType.java index fb7fa73bbbcf3..8ac05f523a1b0 100644 --- a/src/java.sql/share/classes/java/sql/JDBCType.java +++ b/src/java.sql/share/classes/java/sql/JDBCType.java @@ -202,13 +202,17 @@ public enum JDBCType implements SQLType { */ TIMESTAMP_WITH_TIMEZONE(Types.TIMESTAMP_WITH_TIMEZONE), + /* JDBC 4.5 Types */ + /** * Identifies the generic SQL type {@code DECFLOAT}. + * @since 26 */ DECFLOAT(Types.DECFLOAT), /** * Identifies the generic SQL type {@code DECFLOAT}. + * @since 26 */ JSON(Types.JSON); diff --git a/src/java.sql/share/classes/java/sql/SQLPermission.java b/src/java.sql/share/classes/java/sql/SQLPermission.java index 0507d440425bf..f9a0c7e2ed6e7 100644 --- a/src/java.sql/share/classes/java/sql/SQLPermission.java +++ b/src/java.sql/share/classes/java/sql/SQLPermission.java @@ -29,7 +29,7 @@ import java.security.*; /** - *This class was only useful in conjunction with the {@link java.lang.SecurityManager}, + * This class was only useful in conjunction with the {@link java.lang.SecurityManager}, * which is no longer supported. There is no replacement for this class. * * @since 1.3 diff --git a/src/java.sql/share/classes/java/sql/SQLXML.java b/src/java.sql/share/classes/java/sql/SQLXML.java index da8fe3ecf213c..1b7ca2bf670ef 100644 --- a/src/java.sql/share/classes/java/sql/SQLXML.java +++ b/src/java.sql/share/classes/java/sql/SQLXML.java @@ -177,7 +177,7 @@ * The state moves from writable to not writable once close(), free() or any of the * writing APIs are called: setBinaryStream(), setCharacterStream(), setResult(), and setString(). * Implementations may also change the state to not readable when this occurs. - *

+ *

* To release resources used by the {@code SQLXML} object, applications must call * either the {@link #free()} or the {@link #close()} method. Any attempt to * invoke a method other than {@link #free()} or {@link #close()} after the From 0f9876b2007c2694c10b7a99f6b2e1e74f615658 Mon Sep 17 00:00:00 2001 From: Lance Andersen Date: Fri, 10 Oct 2025 13:19:24 -0400 Subject: [PATCH 03/10] Spacing clean up in ConnectionTests --- test/jdk/java/sql/testng/test/sql/ConnectionTests.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/jdk/java/sql/testng/test/sql/ConnectionTests.java b/test/jdk/java/sql/testng/test/sql/ConnectionTests.java index a3b633a4f3021..f41d2dc553678 100644 --- a/test/jdk/java/sql/testng/test/sql/ConnectionTests.java +++ b/test/jdk/java/sql/testng/test/sql/ConnectionTests.java @@ -52,6 +52,7 @@ public static void setUpClass() throws Exception { } maxIdentifier = s.toString(); } + /* * Verify that enquoteLiteral creates a valid literal and converts every * single quote to two single quotes @@ -69,7 +70,6 @@ public void test00(String s, String expected) throws SQLException { @Test(expectedExceptions = NullPointerException.class) public void test01() throws SQLException { conn.enquoteLiteral(null); - } /* @@ -78,7 +78,6 @@ public void test01() throws SQLException { @Test(dataProvider = "validIdentifierValues") public void test02(String s, boolean alwaysQuote, String expected) throws SQLException { assertEquals(conn.enquoteIdentifier(s, alwaysQuote), expected); - } /* @@ -89,7 +88,6 @@ public void test02(String s, boolean alwaysQuote, String expected) throws SQLExc expectedExceptions = SQLException.class) public void test03(String s, boolean alwaysQuote) throws SQLException { conn.enquoteIdentifier(s, alwaysQuote); - } /* From 1c1c8047028d166dbc7e23ac641dd27518004079 Mon Sep 17 00:00:00 2001 From: Lance Andersen Date: Mon, 13 Oct 2025 14:22:56 -0400 Subject: [PATCH 04/10] Round 3 of formating/typo updates --- .../share/classes/java/sql/Array.java | 34 +++++++++--------- src/java.sql/share/classes/java/sql/Blob.java | 36 +++++++++---------- .../share/classes/java/sql/JDBCType.java | 2 +- .../share/classes/java/sql/SQLXML.java | 2 +- .../share/classes/java/sql/Timestamp.java | 2 +- 5 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/java.sql/share/classes/java/sql/Array.java b/src/java.sql/share/classes/java/sql/Array.java index a027434a00a74..8fe84f4f930a7 100644 --- a/src/java.sql/share/classes/java/sql/Array.java +++ b/src/java.sql/share/classes/java/sql/Array.java @@ -365,21 +365,21 @@ ResultSet getResultSet (long index, int count, */ void free() throws SQLException; - /** - * Closes and releases the resources held by this {@code Array} object. - *

- * If the {@code Array} object is already closed, then invoking this method - * has no effect. - * - * @throws SQLException if an error occurs releasing - * the Array's resources - * @throws SQLFeatureNotSupportedException if the JDBC driver - * does not support this method - * @implSpec The default implementation calls the {@link #free()} method. - * @see #free() - * @since 26 - */ - default void close() throws SQLException { - free(); - }; + /** + * Closes and releases the resources held by this {@code Array} object. + *

+ * If the {@code Array} object is already closed, then invoking this method + * has no effect. + * + * @throws SQLException if an error occurs releasing + * the Array's resources + * @throws SQLFeatureNotSupportedException if the JDBC driver + * does not support this method + * @implSpec The default implementation calls the {@link #free()} method. + * @see #free() + * @since 26 + */ + default void close() throws SQLException { + free(); + }; } diff --git a/src/java.sql/share/classes/java/sql/Blob.java b/src/java.sql/share/classes/java/sql/Blob.java index 435ac692f3fc5..4f5619616eb3d 100644 --- a/src/java.sql/share/classes/java/sql/Blob.java +++ b/src/java.sql/share/classes/java/sql/Blob.java @@ -307,22 +307,22 @@ public interface Blob extends AutoCloseable { */ InputStream getBinaryStream(long pos, long length) throws SQLException; - /** - * Closes and releases the resources held by this {@code Blob} object. - *

- * If the {@code Blob} object is already closed, then invoking this method - * has no effect. - * - * @implSpec The default implementation calls the {@link #free()} method. - * - * @throws SQLException if an error occurs releasing - * the Blob's resources - * @throws SQLFeatureNotSupportedException if the JDBC driver - * does not support this method - * @since 26 - * @see #free() - */ - default void close() throws SQLException { - free(); - }; + /** + * Closes and releases the resources held by this {@code Blob} object. + *

+ * If the {@code Blob} object is already closed, then invoking this method + * has no effect. + * + * @implSpec The default implementation calls the {@link #free()} method. + * + * @throws SQLException if an error occurs releasing + * the Blob's resources + * @throws SQLFeatureNotSupportedException if the JDBC driver + * does not support this method + * @since 26 + * @see #free() + */ + default void close() throws SQLException { + free(); + }; } diff --git a/src/java.sql/share/classes/java/sql/JDBCType.java b/src/java.sql/share/classes/java/sql/JDBCType.java index 8ac05f523a1b0..9da51c2792594 100644 --- a/src/java.sql/share/classes/java/sql/JDBCType.java +++ b/src/java.sql/share/classes/java/sql/JDBCType.java @@ -211,7 +211,7 @@ public enum JDBCType implements SQLType { DECFLOAT(Types.DECFLOAT), /** - * Identifies the generic SQL type {@code DECFLOAT}. + * Identifies the generic SQL type {@code JSON}. * @since 26 */ JSON(Types.JSON); diff --git a/src/java.sql/share/classes/java/sql/SQLXML.java b/src/java.sql/share/classes/java/sql/SQLXML.java index 1b7ca2bf670ef..70d481822e2d3 100644 --- a/src/java.sql/share/classes/java/sql/SQLXML.java +++ b/src/java.sql/share/classes/java/sql/SQLXML.java @@ -183,7 +183,7 @@ * invoke a method other than {@link #free()} or {@link #close()} after the * {@code SQLXML} object has been closed, will result in a {@link SQLException} * being thrown. - *

+ *

* All methods on the {@code SQLXML} interface must be fully implemented if the * JDBC driver supports the data type. * diff --git a/src/java.sql/share/classes/java/sql/Timestamp.java b/src/java.sql/share/classes/java/sql/Timestamp.java index 3d16335d71eba..c550291bb9593 100644 --- a/src/java.sql/share/classes/java/sql/Timestamp.java +++ b/src/java.sql/share/classes/java/sql/Timestamp.java @@ -470,7 +470,7 @@ public int compareTo(java.util.Date o) { * (int)(this.getTime()^(this.getTime() >>> 32)) * } * - * @return a hash code value for this Timestamp. + * @return a hash code value for this Timestamp. * */ @Override From 85db002102178b3ae2602644354a7454d3cb6782 Mon Sep 17 00:00:00 2001 From: Lance Andersen Date: Wed, 15 Oct 2025 08:15:51 -0400 Subject: [PATCH 05/10] Address additional spacing comments and add Util class for hosting shared default methods --- src/java.sql/share/classes/java/sql/Clob.java | 34 +- .../share/classes/java/sql/Connection.java | 35 +- .../share/classes/java/sql/SQLUtils.java | 304 ++++++++++++++++++ .../share/classes/java/sql/Statement.java | 32 +- .../sql/testng/test/sql/ConnectionTests.java | 9 +- 5 files changed, 342 insertions(+), 72 deletions(-) create mode 100644 src/java.sql/share/classes/java/sql/SQLUtils.java diff --git a/src/java.sql/share/classes/java/sql/Clob.java b/src/java.sql/share/classes/java/sql/Clob.java index b9becb5d9ece5..141f7de81af4b 100644 --- a/src/java.sql/share/classes/java/sql/Clob.java +++ b/src/java.sql/share/classes/java/sql/Clob.java @@ -353,21 +353,21 @@ public interface Clob extends AutoCloseable { */ Reader getCharacterStream(long pos, long length) throws SQLException; - /** - * Closes and releases the resources held by this {@code Clob} object. - *

- * If the {@code Clob} object is already closed, then invoking this method - * has no effect. - * - * @throws SQLException if an error occurs releasing - * the Clob's resources - * @throws SQLFeatureNotSupportedException if the JDBC driver - * does not support this method - * @implSpec The default implementation calls the {@link #free()} method. - * @see #free() - * @since 26 - */ - default void close() throws SQLException { - free(); - }; + /** + * Closes and releases the resources held by this {@code Clob} object. + *

+ * If the {@code Clob} object is already closed, then invoking this method + * has no effect. + * + * @throws SQLException if an error occurs releasing + * the Clob's resources + * @throws SQLFeatureNotSupportedException if the JDBC driver + * does not support this method + * @implSpec The default implementation calls the {@link #free()} method. + * @see #free() + * @since 26 + */ + default void close() throws SQLException { + free(); + }; } diff --git a/src/java.sql/share/classes/java/sql/Connection.java b/src/java.sql/share/classes/java/sql/Connection.java index 8815de4d067d6..29e2b82c27dc1 100644 --- a/src/java.sql/share/classes/java/sql/Connection.java +++ b/src/java.sql/share/classes/java/sql/Connection.java @@ -28,7 +28,6 @@ import java.util.Properties; import java.util.concurrent.Executor; import java.util.Map; -import java.util.regex.Pattern; /** *

A connection (session) with a specific @@ -78,7 +77,7 @@ * con.setTypeMap(map); * * - * @see DriverManager#getConnection + * @see DriverManager#getConnection(String) * @see Statement * @see ResultSet * @see DatabaseMetaData @@ -1506,7 +1505,7 @@ CallableStatement prepareCall(String sql, int resultSetType, * * @throws SQLException if an error occurs * @since 9 - * @see endRequest + * @see #endRequest() * @see javax.sql.PooledConnection */ default void beginRequest() throws SQLException { @@ -1549,7 +1548,7 @@ default void beginRequest() throws SQLException { * * @throws SQLException if an error occurs * @since 9 - * @see beginRequest + * @see #beginRequest() * @see javax.sql.PooledConnection */ default void endRequest() throws SQLException { @@ -1712,8 +1711,8 @@ default void setShardingKey(ShardingKey shardingKey) throws SQLException { * * @since 26 */ - default String enquoteLiteral(String val) throws SQLException { - return "'" + val.replace("'", "''") + "'"; + default String enquoteLiteral(String val) throws SQLException { + return SQLUtils.enquoteLiteral(val); } /** @@ -1822,21 +1821,7 @@ default String enquoteLiteral(String val) throws SQLException { * @since 26 */ default String enquoteIdentifier(String identifier, boolean alwaysQuote) throws SQLException { - int len = identifier.length(); - if (len < 1 || len > 128) { - throw new SQLException("Invalid name"); - } - if (Pattern.compile("[\\p{Alpha}][\\p{Alnum}_]*").matcher(identifier).matches()) { - return alwaysQuote ? "\"" + identifier + "\"" : identifier; - } - if (identifier.matches("^\".+\"$")) { - identifier = identifier.substring(1, len - 1); - } - if (Pattern.compile("[^\u0000\"]+").matcher(identifier).matches()) { - return "\"" + identifier + "\""; - } else { - throw new SQLException("Invalid name"); - } + return SQLUtils.enquoteIdentifier(identifier, alwaysQuote); } /** @@ -1901,9 +1886,7 @@ default String enquoteIdentifier(String identifier, boolean alwaysQuote) throws * @since 26 */ default boolean isSimpleIdentifier(String identifier) throws SQLException { - int len = identifier.length(); - return len >= 1 && len <= 128 - && Pattern.compile("[\\p{Alpha}][\\p{Alnum}_]*").matcher(identifier).matches(); + return SQLUtils.isSimpleIdentifier(identifier); } /** @@ -1947,7 +1930,7 @@ default boolean isSimpleIdentifier(String identifier) throws SQLException { * * @since 26 */ - default String enquoteNCharLiteral(String val) throws SQLException { - return "N'" + val.replace("'", "''") + "'"; + default String enquoteNCharLiteral(String val) throws SQLException { + return SQLUtils.enquoteNCharLiteral(val); } } diff --git a/src/java.sql/share/classes/java/sql/SQLUtils.java b/src/java.sql/share/classes/java/sql/SQLUtils.java new file mode 100644 index 0000000000000..b97539eb72689 --- /dev/null +++ b/src/java.sql/share/classes/java/sql/SQLUtils.java @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.sql; + +import java.util.regex.Pattern; + +/** + * Utility class used by the Connection & Statement interfaces for their + * shared default methods. + */ +class SQLUtils { + // Pattern used to verify if an identifier is a Simple SQL identifier + static final Pattern SIMPLE_IDENTIFIER_PATTERN + = Pattern.compile("[\\p{Alpha}][\\p{Alnum}_]*"); + // Pattern to check if an identifier contains a null character or a double quote + static final Pattern INVALID_IDENTIFIER_CHARACTERS_PATTERN + = Pattern.compile("[^\u0000\"]+"); + + /** + * Returns a {@code String} enclosed in single quotes. Any occurrence of a + * single quote within the string will be replaced by two single quotes. + * + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Examples of the conversion:
ValueResult
Hello 'Hello'
G'Day 'G''Day'
'G''Day''''G''''Day'''
I'''M 'I''''''M'
+ *
+ * + * @param val a character string + * @return A string enclosed by single quotes with every single quote + * converted to two single quotes + * @throws NullPointerException if val is {@code null} + * @throws SQLException if a database access error occurs + * @implNote JDBC driver implementations may need to provide their own implementation + * of this method in order to meet the requirements of the underlying + * datasource. + */ + static String enquoteLiteral(String val) throws SQLException { + return "'" + val.replace("'", "''") + "'"; + } + + /** + * Returns a SQL identifier. If {@code identifier} is a simple SQL identifier: + *
    + *
  • Return the original value if {@code alwaysQuote} is + * {@code false}
  • + *
  • Return a delimited identifier if {@code alwaysQuote} is + * {@code true}
  • + *
+ *

+ * If {@code identifier} is not a simple SQL identifier, {@code identifier} will be + * enclosed in double quotes if not already present. If the datasource does + * not support double quotes for delimited identifiers, the + * identifier should be enclosed by the string returned from + * {@link DatabaseMetaData#getIdentifierQuoteString}. If the datasource + * does not support delimited identifiers, a + * {@code SQLFeatureNotSupportedException} should be thrown. + *

+ * A {@code SQLException} will be thrown if {@code identifier} contains any + * characters invalid in a delimited identifier or the identifier length is + * invalid for the datasource. + * + * @param identifier a SQL identifier + * @param alwaysQuote indicates if a simple SQL identifier should be + * returned as a quoted identifier + * @return A simple SQL identifier or a delimited identifier + * @throws SQLException if identifier is not a valid identifier + * @throws SQLFeatureNotSupportedException if the datasource does not support + * delimited identifiers + * @throws NullPointerException if identifier is {@code null} + * @implSpec The default implementation uses the following criteria to + * determine a valid simple SQL identifier: + *

    + *
  • The string is not enclosed in double quotes
  • + *
  • The first character is an alphabetic character from a through z, or + * from A through Z
  • + *
  • The name only contains alphanumeric characters or the character "_"
  • + *
+ *

+ * The default implementation will throw a {@code SQLException} if: + *

    + *
  • {@code identifier} contains a {@code null} character or double quote and is not + * a simple SQL identifier.
  • + *
  • The length of {@code identifier} is less than 1 or greater than 128 characters + *
+ *
+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Examples of the conversion:
identifieralwaysQuoteResult
HellofalseHello
Hellotrue"Hello"
G'Dayfalse"G'Day"
"Bruce Wayne"false"Bruce Wayne"
"Bruce Wayne"true"Bruce Wayne"
GoodDay$false"GoodDay$"
Hello"WorldfalseSQLException
"Hello"World"falseSQLException
+ *
+ * @implNote JDBC driver implementations may need to provide their own implementation + * of this method in order to meet the requirements of the underlying + * datasource. + */ + static String enquoteIdentifier(String identifier, boolean alwaysQuote) throws SQLException { + int len = identifier.length(); + if (len < 1 || len > 128) { + throw new SQLException("Invalid name"); + } + if (SIMPLE_IDENTIFIER_PATTERN.matcher(identifier).matches()) { + return alwaysQuote ? "\"" + identifier + "\"" : identifier; + } + if (identifier.matches("^\".+\"$")) { + identifier = identifier.substring(1, len - 1); + } + // Enclose the identifier in double quotes. If the identifier + // contains a null character or a double quote, throw a SQLException + if (INVALID_IDENTIFIER_CHARACTERS_PATTERN.matcher(identifier).matches()) { + return "\"" + identifier + "\""; + } else { + throw new SQLException("Invalid name"); + } + } + + /** + * Retrieves whether {@code identifier} is a simple SQL identifier. + * + * @param identifier a SQL identifier + * @return true if a simple SQL identifier, false otherwise + * @throws NullPointerException if identifier is {@code null} + * @throws SQLException if a database access error occurs + * @implSpec The default implementation uses the following criteria to + * determine a valid simple SQL identifier: + *
    + *
  • The string is not enclosed in double quotes
  • + *
  • The first character is an alphabetic character from a through z, or + * from A through Z
  • + *
  • The string only contains alphanumeric characters or the character + * "_"
  • + *
  • The string is between 1 and 128 characters in length inclusive
  • + *
+ * + *
+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Examples of the conversion:
identifierSimple Identifier
Hellotrue
G'Dayfalse
"Bruce Wayne"false
GoodDay$false
Hello"Worldfalse
"Hello"World"false
+ *
+ * @implNote JDBC driver implementations may need to provide their own + * implementation of this method in order to meet the requirements of the + * underlying datasource. + */ + static boolean isSimpleIdentifier(String identifier) throws SQLException { + int len = identifier.length(); + return len >= 1 && len <= 128 + && SIMPLE_IDENTIFIER_PATTERN.matcher(identifier).matches(); + } + + /** + * Returns a {@code String} representing a National Character Set Literal + * enclosed in single quotes and prefixed with an upper case letter N. + * Any occurrence of a single quote within the string will be replaced + * by two single quotes. + * + *
+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Examples of the conversion:
ValueResult
Hello N'Hello'
G'Day N'G''Day'
'G''Day'N'''G''''Day'''
I'''M N'I''''''M'
N'Hello' N'N''Hello'''
+ *
+ * + * @param val a character string + * @return the result of replacing every single quote character in the + * argument by two single quote characters where this entire result is + * then prefixed with 'N'. + * @throws NullPointerException if val is {@code null} + * @throws SQLException if a database access error occurs + * @implNote JDBC driver implementations may need to provide their own implementation + * of this method in order to meet the requirements of the underlying + * datasource. An implementation of enquoteNCharLiteral may accept a different + * set of characters than that accepted by the same drivers implementation of + * enquoteLiteral. + */ + static String enquoteNCharLiteral(String val) throws SQLException { + return "N'" + val.replace("'", "''") + "'"; + } +} diff --git a/src/java.sql/share/classes/java/sql/Statement.java b/src/java.sql/share/classes/java/sql/Statement.java index bb5a5cfd64d96..3f4baf7395c10 100644 --- a/src/java.sql/share/classes/java/sql/Statement.java +++ b/src/java.sql/share/classes/java/sql/Statement.java @@ -25,9 +25,6 @@ package java.sql; -import java.util.regex.Pattern; -import static java.util.stream.Collectors.joining; - /** *

The object used for executing a static SQL statement * and returning the results it produces. @@ -1407,11 +1404,10 @@ default long executeLargeUpdate(String sql, String columnNames[]) * * @since 9 */ - default String enquoteLiteral(String val) throws SQLException { - return "'" + val.replace("'", "''") + "'"; + default String enquoteLiteral(String val) throws SQLException { + return SQLUtils.enquoteLiteral(val); } - /** * Returns a SQL identifier. If {@code identifier} is a simple SQL identifier: *

    @@ -1518,21 +1514,7 @@ default String enquoteLiteral(String val) throws SQLException { * @since 9 */ default String enquoteIdentifier(String identifier, boolean alwaysQuote) throws SQLException { - int len = identifier.length(); - if (len < 1 || len > 128) { - throw new SQLException("Invalid name"); - } - if (Pattern.compile("[\\p{Alpha}][\\p{Alnum}_]*").matcher(identifier).matches()) { - return alwaysQuote ? "\"" + identifier + "\"" : identifier; - } - if (identifier.matches("^\".+\"$")) { - identifier = identifier.substring(1, len - 1); - } - if (Pattern.compile("[^\u0000\"]+").matcher(identifier).matches()) { - return "\"" + identifier + "\""; - } else { - throw new SQLException("Invalid name"); - } + return SQLUtils.enquoteIdentifier(identifier, alwaysQuote); } /** @@ -1597,9 +1579,7 @@ default String enquoteIdentifier(String identifier, boolean alwaysQuote) throws * @since 9 */ default boolean isSimpleIdentifier(String identifier) throws SQLException { - int len = identifier.length(); - return len >= 1 && len <= 128 - && Pattern.compile("[\\p{Alpha}][\\p{Alnum}_]*").matcher(identifier).matches(); + return SQLUtils.isSimpleIdentifier(identifier); } /** @@ -1643,7 +1623,7 @@ default boolean isSimpleIdentifier(String identifier) throws SQLException { * * @since 9 */ - default String enquoteNCharLiteral(String val) throws SQLException { - return "N'" + val.replace("'", "''") + "'"; + default String enquoteNCharLiteral(String val) throws SQLException { + return SQLUtils.enquoteNCharLiteral(val); } } diff --git a/test/jdk/java/sql/testng/test/sql/ConnectionTests.java b/test/jdk/java/sql/testng/test/sql/ConnectionTests.java index f41d2dc553678..5152c74228c04 100644 --- a/test/jdk/java/sql/testng/test/sql/ConnectionTests.java +++ b/test/jdk/java/sql/testng/test/sql/ConnectionTests.java @@ -170,7 +170,8 @@ protected Object[][] validEnquotedIdentifierValues() { {"Bruce Wayne", false, "\"Bruce Wayne\""}, {"Bruce Wayne", true, "\"Bruce Wayne\""}, {"GoodDay$", false, "\"GoodDay$\""}, - {"GoodDay$", true, "\"GoodDay$\""},}; + {"GoodDay$", true, "\"GoodDay$\""}, + }; } /* @@ -184,7 +185,8 @@ protected Object[][] invalidEnquotedIdentifierValues() { {"\"Hel\"lo\"", true}, {"Hello" + '\0', false}, {"", false}, - {maxIdentifier + 'a', false},}; + {maxIdentifier + 'a', false}, + }; } /* @@ -205,7 +207,8 @@ protected Object[][] simpleIdentifierValues() { {"Batmobile1966", true}, {maxIdentifier, true}, {maxIdentifier + 'a', false}, - {"", false},}; + {"", false}, + }; } /* From 398b0f422195fcd80e274bd1a3729eb03d7ed24b Mon Sep 17 00:00:00 2001 From: Lance Andersen Date: Wed, 15 Oct 2025 15:59:22 -0400 Subject: [PATCH 06/10] Move fields from package-private to private --- src/java.sql/share/classes/java/sql/SQLUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/java.sql/share/classes/java/sql/SQLUtils.java b/src/java.sql/share/classes/java/sql/SQLUtils.java index b97539eb72689..1141443f8b7ff 100644 --- a/src/java.sql/share/classes/java/sql/SQLUtils.java +++ b/src/java.sql/share/classes/java/sql/SQLUtils.java @@ -32,10 +32,10 @@ */ class SQLUtils { // Pattern used to verify if an identifier is a Simple SQL identifier - static final Pattern SIMPLE_IDENTIFIER_PATTERN + private static final Pattern SIMPLE_IDENTIFIER_PATTERN = Pattern.compile("[\\p{Alpha}][\\p{Alnum}_]*"); // Pattern to check if an identifier contains a null character or a double quote - static final Pattern INVALID_IDENTIFIER_CHARACTERS_PATTERN + private static final Pattern INVALID_IDENTIFIER_CHARACTERS_PATTERN = Pattern.compile("[^\u0000\"]+"); /** From 6d77719eaaffb98d851a61a544905babcc62dd62 Mon Sep 17 00:00:00 2001 From: Lance Andersen Date: Thu, 16 Oct 2025 11:32:35 -0400 Subject: [PATCH 07/10] Minor wordsmithing --- .../share/classes/java/sql/Connection.java | 14 +++++++------- src/java.sql/share/classes/java/sql/SQLUtils.java | 12 ++++++------ src/java.sql/share/classes/java/sql/Statement.java | 14 +++++++------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/java.sql/share/classes/java/sql/Connection.java b/src/java.sql/share/classes/java/sql/Connection.java index 29e2b82c27dc1..68d2e94371250 100644 --- a/src/java.sql/share/classes/java/sql/Connection.java +++ b/src/java.sql/share/classes/java/sql/Connection.java @@ -1718,9 +1718,9 @@ default String enquoteLiteral(String val) throws SQLException { /** * Returns a SQL identifier. If {@code identifier} is a simple SQL identifier: *
      - *
    • Return the original value if {@code alwaysQuote} is + *
    • Return the original value if {@code alwaysDelimit} is * {@code false}
    • - *
    • Return a delimited identifier if {@code alwaysQuote} is + *
    • Return a delimited identifier if {@code alwaysDelimit} is * {@code true}
    • *
    * @@ -1758,7 +1758,7 @@ default String enquoteLiteral(String val) throws SQLException { * * * identifier - * alwaysQuote + * alwaysDelimit * Result * * @@ -1810,8 +1810,8 @@ default String enquoteLiteral(String val) throws SQLException { * of this method in order to meet the requirements of the underlying * datasource. * @param identifier a SQL identifier - * @param alwaysQuote indicates if a simple SQL identifier should be - * returned as a quoted identifier + * @param alwaysDelimit indicates if a simple SQL identifier should be + * returned as a delmited identifier * @return A simple SQL identifier or a delimited identifier * @throws SQLException if identifier is not a valid identifier * @throws SQLFeatureNotSupportedException if the datasource does not support @@ -1820,8 +1820,8 @@ default String enquoteLiteral(String val) throws SQLException { * * @since 26 */ - default String enquoteIdentifier(String identifier, boolean alwaysQuote) throws SQLException { - return SQLUtils.enquoteIdentifier(identifier, alwaysQuote); + default String enquoteIdentifier(String identifier, boolean alwaysDelimit) throws SQLException { + return SQLUtils.enquoteIdentifier(identifier, alwaysDelimit); } /** diff --git a/src/java.sql/share/classes/java/sql/SQLUtils.java b/src/java.sql/share/classes/java/sql/SQLUtils.java index 1141443f8b7ff..dd2762cb4fbd7 100644 --- a/src/java.sql/share/classes/java/sql/SQLUtils.java +++ b/src/java.sql/share/classes/java/sql/SQLUtils.java @@ -76,9 +76,9 @@ static String enquoteLiteral(String val) throws SQLException { /** * Returns a SQL identifier. If {@code identifier} is a simple SQL identifier: *
      - *
    • Return the original value if {@code alwaysQuote} is + *
    • Return the original value if {@code alwaysDelimit} is * {@code false}
    • - *
    • Return a delimited identifier if {@code alwaysQuote} is + *
    • Return a delimited identifier if {@code alwaysDelimit} is * {@code true}
    • *
    *

    @@ -95,7 +95,7 @@ static String enquoteLiteral(String val) throws SQLException { * invalid for the datasource. * * @param identifier a SQL identifier - * @param alwaysQuote indicates if a simple SQL identifier should be + * @param alwaysDelimit indicates if a simple SQL identifier should be * returned as a quoted identifier * @return A simple SQL identifier or a delimited identifier * @throws SQLException if identifier is not a valid identifier @@ -123,7 +123,7 @@ static String enquoteLiteral(String val) throws SQLException { * * * identifier - * alwaysQuote + * alwaysDelimit * Result * * @@ -174,13 +174,13 @@ static String enquoteLiteral(String val) throws SQLException { * of this method in order to meet the requirements of the underlying * datasource. */ - static String enquoteIdentifier(String identifier, boolean alwaysQuote) throws SQLException { + static String enquoteIdentifier(String identifier, boolean alwaysDelimit) throws SQLException { int len = identifier.length(); if (len < 1 || len > 128) { throw new SQLException("Invalid name"); } if (SIMPLE_IDENTIFIER_PATTERN.matcher(identifier).matches()) { - return alwaysQuote ? "\"" + identifier + "\"" : identifier; + return alwaysDelimit ? "\"" + identifier + "\"" : identifier; } if (identifier.matches("^\".+\"$")) { identifier = identifier.substring(1, len - 1); diff --git a/src/java.sql/share/classes/java/sql/Statement.java b/src/java.sql/share/classes/java/sql/Statement.java index 3f4baf7395c10..14eb860ef976e 100644 --- a/src/java.sql/share/classes/java/sql/Statement.java +++ b/src/java.sql/share/classes/java/sql/Statement.java @@ -1411,9 +1411,9 @@ default String enquoteLiteral(String val) throws SQLException { /** * Returns a SQL identifier. If {@code identifier} is a simple SQL identifier: *

      - *
    • Return the original value if {@code alwaysQuote} is + *
    • Return the original value if {@code alwaysDelimit} is * {@code false}
    • - *
    • Return a delimited identifier if {@code alwaysQuote} is + *
    • Return a delimited identifier if {@code alwaysDelimit} is * {@code true}
    • *
    * @@ -1451,7 +1451,7 @@ default String enquoteLiteral(String val) throws SQLException { * * * identifier - * alwaysQuote + * alwaysDelimit * Result * * @@ -1503,8 +1503,8 @@ default String enquoteLiteral(String val) throws SQLException { * of this method in order to meet the requirements of the underlying * datasource. * @param identifier a SQL identifier - * @param alwaysQuote indicates if a simple SQL identifier should be - * returned as a quoted identifier + * @param alwaysDelimit indicates if a simple SQL identifier should be + * returned as a delimited identifier * @return A simple SQL identifier or a delimited identifier * @throws SQLException if identifier is not a valid identifier * @throws SQLFeatureNotSupportedException if the datasource does not support @@ -1513,8 +1513,8 @@ default String enquoteLiteral(String val) throws SQLException { * * @since 9 */ - default String enquoteIdentifier(String identifier, boolean alwaysQuote) throws SQLException { - return SQLUtils.enquoteIdentifier(identifier, alwaysQuote); + default String enquoteIdentifier(String identifier, boolean alwaysDelimit) throws SQLException { + return SQLUtils.enquoteIdentifier(identifier, alwaysDelimit); } /** From 385dc0168881731b6c8a5321aa3434908c34fe94 Mon Sep 17 00:00:00 2001 From: Lance Andersen Date: Fri, 17 Oct 2025 18:07:33 -0400 Subject: [PATCH 08/10] Minor wordsmithing based on approved CSR feedback --- src/java.sql/share/classes/java/sql/Connection.java | 11 +++++++++-- src/java.sql/share/classes/java/sql/Statement.java | 13 ++++++++++--- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/java.sql/share/classes/java/sql/Connection.java b/src/java.sql/share/classes/java/sql/Connection.java index 68d2e94371250..3e051bd5e71f5 100644 --- a/src/java.sql/share/classes/java/sql/Connection.java +++ b/src/java.sql/share/classes/java/sql/Connection.java @@ -1699,6 +1699,9 @@ default void setShardingKey(ShardingKey shardingKey) throws SQLException { * * * + * @implSpec + * The default implementation creates the literal as: + * {@code "'" + val.replace("'", "''") + "'"}. * @implNote * JDBC driver implementations may need to provide their own implementation * of this method in order to meet the requirements of the underlying @@ -1831,8 +1834,9 @@ default String enquoteIdentifier(String identifier, boolean alwaysDelimit) throw * determine a valid simple SQL identifier: *
      *
    • The string is not enclosed in double quotes
    • - *
    • The first character is an alphabetic character from a through z, or - * from A through Z
    • + *
    • The first character is an alphabetic character from a ({@code '\u005Cu0061'}) + * through z ({@code '\u005Cu007A'}), or from A ({@code '\u005Cu0041'}) + * through Z ({@code '\u005Cu005A'})
    • *
    • The string only contains alphanumeric characters or the character * "_"
    • *
    • The string is between 1 and 128 characters in length inclusive
    • @@ -1915,6 +1919,9 @@ default boolean isSimpleIdentifier(String identifier) throws SQLException { * * * + * @implSpec + * The default implementation creates the literal as: + * {@code "N'" + val.replace("'", "''") + "'"}. * @implNote * JDBC driver implementations may need to provide their own implementation * of this method in order to meet the requirements of the underlying diff --git a/src/java.sql/share/classes/java/sql/Statement.java b/src/java.sql/share/classes/java/sql/Statement.java index 14eb860ef976e..c9200e5a3c577 100644 --- a/src/java.sql/share/classes/java/sql/Statement.java +++ b/src/java.sql/share/classes/java/sql/Statement.java @@ -1392,6 +1392,9 @@ default long executeLargeUpdate(String sql, String columnNames[]) * * * + * @implSpec + * The default implementation creates the literal as: + * {@code "'" + val.replace("'", "''") + "'"}. * @implNote * JDBC driver implementations may need to provide their own implementation * of this method in order to meet the requirements of the underlying @@ -1524,8 +1527,9 @@ default String enquoteIdentifier(String identifier, boolean alwaysDelimit) throw * determine a valid simple SQL identifier: *
        *
      • The string is not enclosed in double quotes
      • - *
      • The first character is an alphabetic character from a through z, or - * from A through Z
      • + *
      • The first character is an alphabetic character from a ({@code '\u005Cu0061'}) + * through z ({@code '\u005Cu007A'}), or from A ({@code '\u005Cu0041'}) + * through Z ({@code '\u005Cu005A'})
      • *
      • The string only contains alphanumeric characters or the character * "_"
      • *
      • The string is between 1 and 128 characters in length inclusive
      • @@ -1608,7 +1612,10 @@ default boolean isSimpleIdentifier(String identifier) throws SQLException { * * * - * @implNote + * @implSpec + * The default implementation creates the literal as: + * {@code "N'" + val.replace("'", "''") + "'"}. + * @implNote * JDBC driver implementations may need to provide their own implementation * of this method in order to meet the requirements of the underlying * datasource. An implementation of enquoteNCharLiteral may accept a different From ee8e6bd43d1d9d532111316f97c97dec0f56fc65 Mon Sep 17 00:00:00 2001 From: Lance Andersen Date: Thu, 6 Nov 2025 09:01:01 -0500 Subject: [PATCH 09/10] Updates based on internal discussions and CSR suggested wordsmithing --- .../share/classes/java/sql/Connection.java | 103 +- .../share/classes/java/sql/SQLUtils.java | 211 +++- .../share/classes/java/sql/Statement.java | 102 +- .../share/classes/java/sql/package-info.java | 4 +- .../test/sql/CallableStatementTests.java | 101 +- .../sql/testng/test/sql/ConnectionTests.java | 110 +-- .../test/sql/PreparedStatementTests.java | 103 +- .../sql/testng/test/sql/StatementTests.java | 127 +-- test/jdk/java/sql/testng/util/BaseTest.java | 120 ++- .../testng/util/StubCallableStatement.java | 20 +- .../java/sql/testng/util/StubConnection.java | 19 +- .../sql/testng/util/StubDatabaseMetaData.java | 907 ++++++++++++++++++ .../testng/util/StubPreparedStatement.java | 23 +- .../java/sql/testng/util/StubStatement.java | 17 +- .../test/rowset/serial/SQLInputImplTests.java | 6 +- .../rowset/serial/SQLOutputImplTests.java | 3 +- 16 files changed, 1563 insertions(+), 413 deletions(-) create mode 100644 test/jdk/java/sql/testng/util/StubDatabaseMetaData.java diff --git a/src/java.sql/share/classes/java/sql/Connection.java b/src/java.sql/share/classes/java/sql/Connection.java index 3e051bd5e71f5..946459adf7ba4 100644 --- a/src/java.sql/share/classes/java/sql/Connection.java +++ b/src/java.sql/share/classes/java/sql/Connection.java @@ -1719,40 +1719,45 @@ default String enquoteLiteral(String val) throws SQLException { } /** - * Returns a SQL identifier. If {@code identifier} is a simple SQL identifier: + * Returns a {@link #isSimpleIdentifier(String) simple SQL identifier} or a + * delimited identifier. A delimited identifier represents the name of a + * database object such as a table, column, or view that is enclosed by a + * delimiter, which is typically a double quote as defined by the SQL standard. + *

        + * If {@code identifier} is a simple SQL identifier: *

          - *
        • Return the original value if {@code alwaysDelimit} is - * {@code false}
        • - *
        • Return a delimited identifier if {@code alwaysDelimit} is - * {@code true}
        • + *
        • If {@code alwaysDelimit} is {@code false}, return the original value
        • + *
        • if {@code alwaysDelimit} is {@code true}, enquote the original value + * and return as a delimited identifier
        • *
        * - * If {@code identifier} is not a simple SQL identifier, {@code identifier} will be - * enclosed in double quotes if not already present. If the datasource does - * not support double quotes for delimited identifiers, the - * identifier should be enclosed by the string returned from - * {@link DatabaseMetaData#getIdentifierQuoteString}. If the datasource - * does not support delimited identifiers, a - * {@code SQLFeatureNotSupportedException} should be thrown. + * If {@code identifier} is not a simple SQL identifier, the delimited + * {@code identifier} to be returned must be enclosed by the delimiter + * returned from {@link DatabaseMetaData#getIdentifierQuoteString}. If + * the datasource does not support delimited identifiers, a + * {@code SQLFeatureNotSupportedException} is thrown. *

        * A {@code SQLException} will be thrown if {@code identifier} contains any - * characters invalid in a delimited identifier or the identifier length is - * invalid for the datasource. + * invalid characters within a delimited identifier or the identifier length + * is invalid for the datasource. * * @implSpec * The default implementation uses the following criteria to * determine a valid simple SQL identifier: *

          *
        • The string is not enclosed in double quotes
        • - *
        • The first character is an alphabetic character from a through z, or - * from A through Z
        • - *
        • The name only contains alphanumeric characters or the character "_"
        • + *
        • The first character is an alphabetic character from a ({@code '\u005C0061'}) + * through z ({@code '\u005Cu007A'}), or from A ({@code '\u005Cu0041'}) + * through Z ({@code '\u005Cu005A'})
        • + *
        • The name only contains alphanumeric characters([0-9A-Za-z]) + * or the character "_"
        • *
        * * The default implementation will throw a {@code SQLException} if: *
          - *
        • {@code identifier} contains a {@code null} character or double quote and is not - * a simple SQL identifier.
        • + *
        • {@link DatabaseMetaData#getIdentifierQuoteString} does not return a + * double quote
        • + *
        • {@code identifier} contains a {@code null} character or double quote
        • *
        • The length of {@code identifier} is less than 1 or greater than 128 characters *
        *
        @@ -1791,6 +1796,16 @@ default String enquoteLiteral(String val) throws SQLException { * "Bruce Wayne" * * + * "select" + * false + * "select" + * + * + * "select" + * true + * "select" + * + * * GoodDay$ * false * "GoodDay$" @@ -1814,7 +1829,7 @@ default String enquoteLiteral(String val) throws SQLException { * datasource. * @param identifier a SQL identifier * @param alwaysDelimit indicates if a simple SQL identifier should be - * returned as a delmited identifier + * returned as a delimited identifier * @return A simple SQL identifier or a delimited identifier * @throws SQLException if identifier is not a valid identifier * @throws SQLFeatureNotSupportedException if the datasource does not support @@ -1824,22 +1839,43 @@ default String enquoteLiteral(String val) throws SQLException { * @since 26 */ default String enquoteIdentifier(String identifier, boolean alwaysDelimit) throws SQLException { - return SQLUtils.enquoteIdentifier(identifier, alwaysDelimit); + String delimiter = this.getMetaData().getIdentifierQuoteString(); + return SQLUtils.enquoteIdentifier(delimiter, identifier, alwaysDelimit); } /** - * Retrieves whether {@code identifier} is a simple SQL identifier. - * - * @implSpec The default implementation uses the following criteria to - * determine a valid simple SQL identifier: + * Returns whether {@code identifier} is a simple SQL identifier. + * A simple SQL identifier is referred to as regular (or ordinary) identifier + * within the SQL standard. A regular identifier represents the name of a database + * object such as a table, column, or view. + *

        + * The rules for a regular Identifier are: *

          - *
        • The string is not enclosed in double quotes
        • *
        • The first character is an alphabetic character from a ({@code '\u005Cu0061'}) * through z ({@code '\u005Cu007A'}), or from A ({@code '\u005Cu0041'}) * through Z ({@code '\u005Cu005A'})
        • - *
        • The string only contains alphanumeric characters or the character - * "_"
        • - *
        • The string is between 1 and 128 characters in length inclusive
        • + *
        • The name only contains alphanumeric characters([0-9A-Za-z]) + * or the character "_"
        • + *
        • It cannot be a SQL reserved word
        • + *
        + *

        + * A datasource may have additional rules for a regular identifier such as: + *

          + *
        • Supports additional characters within the name based on + * the locale being used
        • + *
        • Supports a different maximum length for the identifier
        • + *
        + * + * @implSpec The default implementation uses the following criteria to + * determine a valid simple SQL identifier: + *
          + *
        • The identifier is not enclosed in double quotes
        • + *
        • The first character is an alphabetic character from a through z, or + * from A through Z
        • + *
        • The identifier only contains alphanumeric characters([0-9A-Za-z]) or + * the character "_"
        • + *
        • The identifier is not a SQL reserved word
        • + *
        • The identifier is between 1 and 128 characters in length inclusive
        • *
        * *
        @@ -1876,6 +1912,13 @@ default String enquoteIdentifier(String identifier, boolean alwaysDelimit) throw * "Hello"World" * false * + * + * "select" + * false + * + * "from" + * false + * * * *
        @@ -1883,7 +1926,7 @@ default String enquoteIdentifier(String identifier, boolean alwaysDelimit) throw * implementation of this method in order to meet the requirements of the * underlying datasource. * @param identifier a SQL identifier - * @return true if a simple SQL identifier, false otherwise + * @return true if a simple SQL identifier, false otherwise * @throws NullPointerException if identifier is {@code null} * @throws SQLException if a database access error occurs * diff --git a/src/java.sql/share/classes/java/sql/SQLUtils.java b/src/java.sql/share/classes/java/sql/SQLUtils.java index dd2762cb4fbd7..49757d7e8ad6d 100644 --- a/src/java.sql/share/classes/java/sql/SQLUtils.java +++ b/src/java.sql/share/classes/java/sql/SQLUtils.java @@ -24,6 +24,9 @@ */ package java.sql; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; import java.util.regex.Pattern; /** @@ -37,6 +40,83 @@ class SQLUtils { // Pattern to check if an identifier contains a null character or a double quote private static final Pattern INVALID_IDENTIFIER_CHARACTERS_PATTERN = Pattern.compile("[^\u0000\"]+"); + // SQL 2023 reserved words + private static final String[] SQL2023_RESERVED_WORDS = { + "ABS", "ABSENT", "ACOS", "ALL", "ALLOCATE", "ALTER", "AND", "ANY", + "ANY_VALUE", "ARE", "ARRAY", "ARRAY_AGG", "ARRAY_MAX_CARDINALITY", + "AS", "ASENSITIVE", "ASIN", "ASYMMETRIC", "AT", "ATAN", + "ATOMIC", "AUTHORIZATION", "AVG", + "BEGIN", "BEGIN_FRAME", "BEGIN_PARTITION", "BETWEEN", "BIGINT", + "BINARY", "BLOB", "BOOLEAN", "BOTH", "BTRIM", "BY", + "CALL", "CALLED", "CARDINALITY", "CASCADED", "CASE", "CAST", "CEIL", + "CEILING", "CHAR", "CHAR_LENGTH", + "CHARACTER", "CHARACTER_LENGTH", "CHECK", "CLASSIFIER", "CLOB", + "CLOSE", "COALESCE", "COLLATE", "COLLECT", "COLUMN", "COMMIT", "CONDITION", + "CONNECT", "CONSTRAINT", "CONTAINS", "CONVERT", "COPY", "CORR", "CORRESPONDING", + "COS", "COSH", "COUNT", "COVAR_POP", "COVAR_SAMP", "CREATE", "CROSS", "CUBE", + "CUME_DIST", "CURRENT", + "CURRENT_CATALOG", "CURRENT_DATE", "CURRENT_DEFAULT_TRANSFORM_GROUP", "CURRENT_PATH", + "CURRENT_ROLE", "CURRENT_SCHEMA", "CURRENT_TIME", "CURRENT_TIMESTAMP", + "CURRENT_TRANSFORM_GROUP_FOR_TYPE", "CURRENT_USER", "CURSOR", "CYCLE", + "DATE", "DAY", "DEALLOCATE", "DEC", "DECFLOAT", "DECIMAL", "DECLARE", "DEFAULT", + "DEFINE", "DELETE", "DENSE_RANK", "DEREF", "DESCRIBE", "DETERMINISTIC", + "DISCONNECT", "DISTINCT", "DOUBLE", "DROP", "DYNAMIC", + "EACH", "ELEMENT", "ELSE", "EMPTY", "END", "END_FRAME", "END_PARTITION", + "END-EXEC", "EQUALS", "ESCAPE", "EVERY", "EXCEPT", "EXEC", "EXECUTE", + "EXISTS", "EXP", "EXTERNAL", "EXTRACT", + "FALSE", "FETCH", "FILTER", "FIRST_VALUE", "FLOAT", "FLOOR", "FOR", "FOREIGN", "FRAME_ROW", + "FREE", "FROM", "FULL", "FUNCTION", "FUSION", + "GET", "GLOBAL", "GRANT", "GREATEST", "GROUP", "GROUPING", "GROUPS", + "HAVING", "HOLD", "HOUR", + "IDENTITY", "IN", "INDICATOR", "INITIAL", "INNER", "INOUT", "INSENSITIVE", + "INSERT", "INT", "INTEGER", + "INTERSECT", "INTERSECTION", "INTERVAL", "INTO", "IS", + "JOIN", "JSON", "JSON_ARRAY", "JSON_ARRAYAGG", "JSON_EXISTS", + "JSON_OBJECT", "JSON_OBJECTAGG", "JSON_QUERY", "JSON_SCALAR", + "JSON_SERIALIZE", "JSON_TABLE", "JSON_TABLE_PRIMITIVE", "JSON_VALUE", + "LAG", "LANGUAGE", "LARGE", " LAST_VALUE", "LATERAL", "LEAD", + "LEADING", "LEAST", "LEFT", "LIKE", "LIKE_REGEX", "LISTAGG", + "LN", "LOCAL", "LOCALTIME", "LOCALTIMESTAMP", "LOG", "LOG10", + "LOWER", "LPAD", "LTRIM", + "MATCH", "MATCH_NUMBER", "MATCH_RECOGNIZE", "MATCHES", "MAX", + "MEMBER", "MERGE", "METHOD", "MIN", "MINUTE", "MOD", "MODIFIES", + "MODULE", "MONTH", "MULTISET", + "NATIONAL", "NATURAL", "NCHAR", "NCLOB", "NEW", "NO", "NONE", + "NORMALIZE", "NOT", "NTH_VALUE", "NTILE", "NULL", "NULLIF", "NUMERIC", + "OCCURRENCES_REGEX", "OCTET_LENGTH", "OF", "OFFSET", "OLD", "OMIT", + "ON", "ONE", "ONLY", "OPEN", "OR", "ORDER", "OUT", "OUTER", "OUTPUT", + "OVER", "OVERLAPS", "OVERLAY", + "PARAMETER", "PARTITION", "PATTERN", "PER", "PERCENT", "PERCENT_RANK", + "PERCENTILE_CONT", "PERCENTILE_DISC", "PERIOD", "PORTION", "POSITION", + "POSITION_REGEX", "POWER", "PRECEDES", + "PRECISION", "PREPARE", "PRIMARY", "PROCEDURE", "PTF", + "RANGE", "RANK", "READS", "REAL", "RECURSIVE", "REF", "REFERENCES", + "REFERENCING", "REGR_AVGX", "REGR_AVGY", "REGR_COUNT", "REGR_INTERCEPT", + "REGR_R2", "REGR_SLOPE", "REGR_SXX", "REGR_SXY", "REGR_SYY", + "RELEASE", "RESULT", "RETURN", "RETURNS", "REVOKE", "RIGHT", + "ROLLBACK", "ROLLUP", "ROW", "ROW_NUMBER", "ROWS", "RPAD", "RTRIM", + "RUNNING", + "SAVEPOINT", "SCOPE", "SCROLL", "SEARCH", "SECOND", "SEEK", + "SELECT", "SENSITIVE", "SESSION_USER", "SET", "SHOW", "SIMILAR", + "SIN", "SINH", "SKIP", "SMALLINT", + "SOME", "SPECIFIC", "SPECIFICTYPE", "SQL", "SQLEXCEPTION", "SQLSTATE", + "SQLWARNING", "SQRT", "START", "STATIC", "STDDEV_POP", "STDDEV_SAMP", + "SUBMULTISET", "SUBSET", "SUBSTRING", "SUBSTRING_REGEX", "SUCCEEDS", + "SUM", "SYMMETRIC", "SYSTEM", "SYSTEM_TIME", "SYSTEM_USER", + "TABLE", "TABLESAMPLE", "TAN", "TANH", "THEN", "TIME", "TIMESTAMP", + "TIMEZONE_HOUR", "TIMEZONE_MINUTE", "TO", "TRAILING", "TRANSLATE", + "TRANSLATE_REGEX", "TRANSLATION", "TREAT", "TRIGGER", "TRIM", + "TRIM_ARRAY", "TRUE", "TRUNCATE", + "UESCAPE", "UNION", "UNIQUE", "UNKNOWN", "UNNEST", "UPDATE", "UPPER", + "USER", "USING", + "VALUE", "VALUES", "VALUE_OF", "VAR_POP", "VAR_SAMP", "VARBINARY", + "VARCHAR", "VARYING", "VERSIONING", + "WHEN", "WHENEVER", "WHERE", "WHILE", "WIDTH_BUCKET", "WINDOW", + "WITH", "WITHIN", "WITHOUT", + "YEAR" + }; + private static final Set SQL_RESERVED_WORDS = + new HashSet<>(Arrays.asList(SQL2023_RESERVED_WORDS)); /** * Returns a {@code String} enclosed in single quotes. Any occurrence of a @@ -74,47 +154,44 @@ static String enquoteLiteral(String val) throws SQLException { } /** - * Returns a SQL identifier. If {@code identifier} is a simple SQL identifier: + * Returns a {@link #isSimpleIdentifier(String) simple SQL identifier} or a + * delimited identifier. A delimited identifier represents the name of a + * database object such as a table, column, or view that is enclosed by a + * delimiter, which is typically a double quote as defined by the SQL standard. + *

        + * If {@code identifier} is a simple SQL identifier: *

          - *
        • Return the original value if {@code alwaysDelimit} is - * {@code false}
        • - *
        • Return a delimited identifier if {@code alwaysDelimit} is - * {@code true}
        • + *
        • If {@code alwaysDelimit} is {@code false}, return the original value
        • + *
        • if {@code alwaysDelimit} is {@code true}, enquote the original value + * and return as a delimited identifier
        • *
        - *

        - * If {@code identifier} is not a simple SQL identifier, {@code identifier} will be - * enclosed in double quotes if not already present. If the datasource does - * not support double quotes for delimited identifiers, the - * identifier should be enclosed by the string returned from - * {@link DatabaseMetaData#getIdentifierQuoteString}. If the datasource - * does not support delimited identifiers, a - * {@code SQLFeatureNotSupportedException} should be thrown. + * + * If {@code identifier} is not a simple SQL identifier, the delimited + * {@code identifier} to be returned must be enclosed by the delimiter + * returned from {@link DatabaseMetaData#getIdentifierQuoteString}. If + * the datasource does not support delimited identifiers, a + * {@code SQLFeatureNotSupportedException} is thrown. *

        * A {@code SQLException} will be thrown if {@code identifier} contains any - * characters invalid in a delimited identifier or the identifier length is - * invalid for the datasource. + * invalid characters within a delimited identifier or the identifier length + * is invalid for the datasource. * - * @param identifier a SQL identifier - * @param alwaysDelimit indicates if a simple SQL identifier should be - * returned as a quoted identifier - * @return A simple SQL identifier or a delimited identifier - * @throws SQLException if identifier is not a valid identifier - * @throws SQLFeatureNotSupportedException if the datasource does not support - * delimited identifiers - * @throws NullPointerException if identifier is {@code null} - * @implSpec The default implementation uses the following criteria to + * @implSpec + * The default implementation uses the following criteria to * determine a valid simple SQL identifier: *

          *
        • The string is not enclosed in double quotes
        • - *
        • The first character is an alphabetic character from a through z, or - * from A through Z
        • + *
        • The first character is an alphabetic character from a ({@code '\u005C0061'}) + * through z ({@code '\u005Cu007A'}), or from A ({@code '\u005Cu0041'}) + * through Z ({@code '\u005Cu005A'})
        • *
        • The name only contains alphanumeric characters or the character "_"
        • *
        - *

        + * * The default implementation will throw a {@code SQLException} if: *

          - *
        • {@code identifier} contains a {@code null} character or double quote and is not - * a simple SQL identifier.
        • + *
        • {@link DatabaseMetaData#getIdentifierQuoteString} does not return a + * double quote
        • + *
        • {@code identifier} contains a {@code null} character or double quote
        • *
        • The length of {@code identifier} is less than 1 or greater than 128 characters *
        *
        @@ -153,6 +230,16 @@ static String enquoteLiteral(String val) throws SQLException { * "Bruce Wayne" * * + * "select" + * false + * "select" + * + * + * "select" + * true + * "select" + * + * * GoodDay$ * false * "GoodDay$" @@ -170,16 +257,28 @@ static String enquoteLiteral(String val) throws SQLException { * * *
        - * @implNote JDBC driver implementations may need to provide their own implementation + * @implNote + * JDBC driver implementations may need to provide their own implementation * of this method in order to meet the requirements of the underlying * datasource. + * @param identifier a SQL identifier + * @param alwaysDelimit indicates if a simple SQL identifier should be + * returned as a delimited identifier + * @return A simple SQL identifier or a delimited identifier + * @throws SQLException if identifier is not a valid identifier + * @throws SQLFeatureNotSupportedException if the datasource does not support + * delimited identifiers + * @throws NullPointerException if identifier is {@code null} */ - static String enquoteIdentifier(String identifier, boolean alwaysDelimit) throws SQLException { + static String enquoteIdentifier(String delimiter, String identifier, boolean alwaysDelimit) throws SQLException { int len = identifier.length(); if (len < 1 || len > 128) { - throw new SQLException("Invalid name"); + throw new SQLException("Invalid identifier length"); + } + if (!delimiter.equals("\"")) { + throw new SQLException("Unsupported delimiter"); } - if (SIMPLE_IDENTIFIER_PATTERN.matcher(identifier).matches()) { + if (isSimpleIdentifier(identifier)) { return alwaysDelimit ? "\"" + identifier + "\"" : identifier; } if (identifier.matches("^\".+\"$")) { @@ -195,21 +294,37 @@ static String enquoteIdentifier(String identifier, boolean alwaysDelimit) throws } /** - * Retrieves whether {@code identifier} is a simple SQL identifier. + * Returns whether {@code identifier} is a simple SQL identifier. + * A simple SQL identifier is referred to as regular (or ordinary) identifier + * within the SQL standard. A regular identifier represents the name of a database + * object such as a table, column, or view. + *

        + * The rules for a regular Identifier are: + *

          + *
        • The first character is an alphabetic character from a ({@code '\u005Cu0061'}) + * through z ({@code '\u005Cu007A'}), or from A ({@code '\u005Cu0041'}) + * through Z ({@code '\u005Cu005A'})
        • + *
        • The name only contains alphanumeric characters or the character "_"
        • + *
        • It cannot be a SQL reserved word
        • + *
        + *

        + * A datasource may have additional rules for a regular identifier such as: + *

          + *
        • Supports additional characters within the name based on + * the locale being used
        • + *
        • Supports a different maximum length for the identifier
        • + *
        * - * @param identifier a SQL identifier - * @return true if a simple SQL identifier, false otherwise - * @throws NullPointerException if identifier is {@code null} - * @throws SQLException if a database access error occurs * @implSpec The default implementation uses the following criteria to * determine a valid simple SQL identifier: *
          - *
        • The string is not enclosed in double quotes
        • + *
        • The identifier is not enclosed in double quotes
        • *
        • The first character is an alphabetic character from a through z, or * from A through Z
        • - *
        • The string only contains alphanumeric characters or the character + *
        • The identifier only contains alphanumeric characters or the character * "_"
        • - *
        • The string is between 1 and 128 characters in length inclusive
        • + *
        • The identifier is not a SQL reserved word
        • + *
        • The identifier is between 1 and 128 characters in length inclusive
        • *
        * *
        @@ -246,16 +361,28 @@ static String enquoteIdentifier(String identifier, boolean alwaysDelimit) throws * "Hello"World" * false * + * + * "select" + * false + * + * "from" + * false + * * * *
        * @implNote JDBC driver implementations may need to provide their own * implementation of this method in order to meet the requirements of the * underlying datasource. + * @param identifier a SQL identifier + * @return true if a simple SQL identifier, false otherwise + * @throws NullPointerException if identifier is {@code null} + * @throws SQLException if a database access error occurs */ static boolean isSimpleIdentifier(String identifier) throws SQLException { int len = identifier.length(); - return len >= 1 && len <= 128 + return !SQL_RESERVED_WORDS.contains(identifier.toUpperCase()) && + len >= 1 && len <= 128 && SIMPLE_IDENTIFIER_PATTERN.matcher(identifier).matches(); } diff --git a/src/java.sql/share/classes/java/sql/Statement.java b/src/java.sql/share/classes/java/sql/Statement.java index c9200e5a3c577..4da510f6749e8 100644 --- a/src/java.sql/share/classes/java/sql/Statement.java +++ b/src/java.sql/share/classes/java/sql/Statement.java @@ -1411,41 +1411,46 @@ default String enquoteLiteral(String val) throws SQLException { return SQLUtils.enquoteLiteral(val); } - /** - * Returns a SQL identifier. If {@code identifier} is a simple SQL identifier: + /** + * Returns a {@link #isSimpleIdentifier(String) simple SQL identifier} or a + * delimited identifier. A delimited identifier represents the name of a + * database object such as a table, column, or view that is enclosed by a + * delimiter, which is typically a double quote as defined by the SQL standard. + *

        + * If {@code identifier} is a simple SQL identifier: *

          - *
        • Return the original value if {@code alwaysDelimit} is - * {@code false}
        • - *
        • Return a delimited identifier if {@code alwaysDelimit} is - * {@code true}
        • + *
        • If {@code alwaysDelimit} is {@code false}, return the original value
        • + *
        • if {@code alwaysDelimit} is {@code true}, enquote the original value + * and return as a delimited identifier
        • *
        * - * If {@code identifier} is not a simple SQL identifier, {@code identifier} will be - * enclosed in double quotes if not already present. If the datasource does - * not support double quotes for delimited identifiers, the - * identifier should be enclosed by the string returned from - * {@link DatabaseMetaData#getIdentifierQuoteString}. If the datasource - * does not support delimited identifiers, a - * {@code SQLFeatureNotSupportedException} should be thrown. + * If {@code identifier} is not a simple SQL identifier, the delimited + * {@code identifier} to be returned must be enclosed by the delimiter + * returned from {@link DatabaseMetaData#getIdentifierQuoteString}. If + * the datasource does not support delimited identifiers, a + * {@code SQLFeatureNotSupportedException} is thrown. *

        * A {@code SQLException} will be thrown if {@code identifier} contains any - * characters invalid in a delimited identifier or the identifier length is - * invalid for the datasource. + * invalid characters within a delimited identifier or the identifier length + * is invalid for the datasource. * * @implSpec * The default implementation uses the following criteria to * determine a valid simple SQL identifier: *

          *
        • The string is not enclosed in double quotes
        • - *
        • The first character is an alphabetic character from a through z, or - * from A through Z
        • - *
        • The name only contains alphanumeric characters or the character "_"
        • + *
        • The first character is an alphabetic character from a ({@code '\u005C0061'}) + * through z ({@code '\u005Cu007A'}), or from A ({@code '\u005Cu0041'}) + * through Z ({@code '\u005Cu005A'})
        • + *
        • The name only contains alphanumeric characters([0-9A-Za-z]) + * or the character "_"
        • *
        * * The default implementation will throw a {@code SQLException} if: *
          - *
        • {@code identifier} contains a {@code null} character or double quote and is not - * a simple SQL identifier.
        • + *
        • {@link DatabaseMetaData#getIdentifierQuoteString} does not return a + * double quote
        • + *
        • {@code identifier} contains a {@code null} character or double quote
        • *
        • The length of {@code identifier} is less than 1 or greater than 128 characters *
        *
        @@ -1484,6 +1489,16 @@ default String enquoteLiteral(String val) throws SQLException { * "Bruce Wayne" * * + * "select" + * false + * "select" + * + * + * "select" + * true + * "select" + * + * * GoodDay$ * false * "GoodDay$" @@ -1517,22 +1532,42 @@ default String enquoteLiteral(String val) throws SQLException { * @since 9 */ default String enquoteIdentifier(String identifier, boolean alwaysDelimit) throws SQLException { - return SQLUtils.enquoteIdentifier(identifier, alwaysDelimit); + return getConnection().enquoteIdentifier(identifier,alwaysDelimit); } /** - * Retrieves whether {@code identifier} is a simple SQL identifier. - * - * @implSpec The default implementation uses the following criteria to - * determine a valid simple SQL identifier: + * Returns whether {@code identifier} is a simple SQL identifier. + * A simple SQL identifier is referred to as regular (or ordinary) identifier + * within the SQL standard. A regular identifier represents the name of a database + * object such as a table, column, or view. + *

        + * The rules for a regular Identifier are: *

          - *
        • The string is not enclosed in double quotes
        • *
        • The first character is an alphabetic character from a ({@code '\u005Cu0061'}) * through z ({@code '\u005Cu007A'}), or from A ({@code '\u005Cu0041'}) * through Z ({@code '\u005Cu005A'})
        • - *
        • The string only contains alphanumeric characters or the character - * "_"
        • - *
        • The string is between 1 and 128 characters in length inclusive
        • + *
        • The name only contains alphanumeric characters([0-9A-Za-z]) or the + * character "_"
        • + *
        • It cannot be a SQL reserved word
        • + *
        + *

        + * A datasource may have additional rules for a regular identifier such as: + *

          + *
        • Supports additional characters within the name based on + * the locale being used
        • + *
        • Supports a different maximum length for the identifier
        • + *
        + * + * @implSpec The default implementation uses the following criteria to + * determine a valid simple SQL identifier: + *
          + *
        • The identifier is not enclosed in double quotes
        • + *
        • The first character is an alphabetic character from a through z, or + * from A through Z
        • + *
        • The identifier only contains alphanumeric characters([0-9A-Za-z]) + * or the character "_"
        • + *
        • The identifier is not a SQL reserved word
        • + *
        • The identifier is between 1 and 128 characters in length inclusive
        • *
        * *
        @@ -1569,6 +1604,13 @@ default String enquoteIdentifier(String identifier, boolean alwaysDelimit) throw * "Hello"World" * false * + * + * "select" + * false + * + * "from" + * false + * * * *
        @@ -1576,7 +1618,7 @@ default String enquoteIdentifier(String identifier, boolean alwaysDelimit) throw * implementation of this method in order to meet the requirements of the * underlying datasource. * @param identifier a SQL identifier - * @return true if a simple SQL identifier, false otherwise + * @return true if a simple SQL identifier, false otherwise * @throws NullPointerException if identifier is {@code null} * @throws SQLException if a database access error occurs * diff --git a/src/java.sql/share/classes/java/sql/package-info.java b/src/java.sql/share/classes/java/sql/package-info.java index 927e0f746d5f9..64b23dba76f1a 100644 --- a/src/java.sql/share/classes/java/sql/package-info.java +++ b/src/java.sql/share/classes/java/sql/package-info.java @@ -177,7 +177,7 @@ *
          *
        • The interfaces {@code Array}, {@code Blob}, {@code Clob}, {@code NClob} * and {@code SQLXML} now extend the {@code AutoCloseable} interface and - * include a default {@code close} method implentation
        • + * include a default {@code close} method implementation *
        • Added support to {@code Connection} for enquoting literals * and simple identifiers
        • *
        • {@code SQLPermissions} has been deprecated for removal
        • @@ -259,7 +259,6 @@ * *
        * - * *

        {@code java.sql} and {@code javax.sql} Features Introduced in the JDBC 3.0 API

        *
          *
        • Pooled statements -- reuse of statements associated with a pooled @@ -315,7 +314,6 @@ * handling and passing data *
        * - * *

        Custom Mapping of UDTs

        * A user-defined type (UDT) defined in SQL can be mapped to a class in the Java * programming language. An SQL structured type or an SQL {@code DISTINCT} diff --git a/test/jdk/java/sql/testng/test/sql/CallableStatementTests.java b/test/jdk/java/sql/testng/test/sql/CallableStatementTests.java index f917b6af4dcc4..7a4fe15ecac1c 100644 --- a/test/jdk/java/sql/testng/test/sql/CallableStatementTests.java +++ b/test/jdk/java/sql/testng/test/sql/CallableStatementTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,15 +22,108 @@ */ package test.sql; +import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; -import util.StubCallableStatement; +import org.testng.annotations.Test; +import util.BaseTest; +import util.StubConnection; -public class CallableStatementTests extends PreparedStatementTests { +import java.sql.CallableStatement; +import java.sql.SQLException; + +import static org.testng.Assert.assertEquals; + +public class CallableStatementTests extends BaseTest { + private CallableStatement cstmt; @BeforeMethod public void setUpMethod() throws Exception { - stmt = new StubCallableStatement(); + cstmt = new StubConnection().prepareCall("{call SuperHero_Proc(?)}"); + } + + @AfterMethod + public void tearDownMethod() throws Exception { + cstmt.close(); + } + + /* + * Verify that enquoteLiteral creates a valid literal and converts every + * single quote to two single quotes + */ + @Test(dataProvider = "validEnquotedLiteralValues") + public void test00(String s, String expected) throws SQLException { + assertEquals(cstmt.enquoteLiteral(s), expected); + } + + /* + * Validate a NullPointerException is thrown if the string passed to + * enquoteLiteral is null + */ + @Test(expectedExceptions = NullPointerException.class) + public void test01() throws SQLException { + cstmt.enquoteLiteral(null); + } + + /* + * Validate that enquoteIdentifier returns the expected value + */ + @Test(dataProvider = "validIdentifierValues") + public void test02(String s, boolean alwaysQuote, String expected) throws SQLException { + assertEquals(cstmt.enquoteIdentifier(s, alwaysQuote), expected); } + /* + * Validate that a SQLException is thrown for values that are not valid + * for a SQL identifier + */ + @Test(dataProvider = "invalidIdentifierValues", + expectedExceptions = SQLException.class) + public void test03(String s, boolean alwaysQuote) throws SQLException { + cstmt.enquoteIdentifier(s, alwaysQuote); + } + + /* + * Validate a NullPointerException is thrown is the string passed to + * enquoteIdentiifer is null + */ + @Test(dataProvider = "trueFalse", + expectedExceptions = NullPointerException.class) + public void test04(boolean alwaysQuote) throws SQLException { + cstmt.enquoteIdentifier(null, alwaysQuote); + } + + /* + * Validate that isSimpleIdentifier returns the expected value + */ + @Test(dataProvider = "simpleIdentifierValues") + public void test05(String s, boolean expected) throws SQLException { + assertEquals(cstmt.isSimpleIdentifier(s), expected); + } + /* + * Validate a NullPointerException is thrown if the string passed to + * isSimpleIdentifier is null + */ + @Test(expectedExceptions = NullPointerException.class) + public void test06() throws SQLException { + cstmt.isSimpleIdentifier(null); + } + + /* + * Verify that enquoteLiteral creates a valid literal and converts every + * single quote to two single quotes + */ + @Test(dataProvider = "validEnquotedNCharLiteralValues") + public void test07(String s, String expected) throws SQLException { + assertEquals(cstmt.enquoteNCharLiteral(s), expected); + } + + /* + * Validate a NullPointerException is thrown if the string passed to + * enquoteNCharLiteral is null + */ + @Test(expectedExceptions = NullPointerException.class) + public void test08() throws SQLException { + cstmt.enquoteNCharLiteral(null); + } } diff --git a/test/jdk/java/sql/testng/test/sql/ConnectionTests.java b/test/jdk/java/sql/testng/test/sql/ConnectionTests.java index 5152c74228c04..f40c2784e4a74 100644 --- a/test/jdk/java/sql/testng/test/sql/ConnectionTests.java +++ b/test/jdk/java/sql/testng/test/sql/ConnectionTests.java @@ -22,42 +22,28 @@ */ package test.sql; -import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; -import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import util.BaseTest; import util.StubConnection; import java.sql.SQLException; -import static org.testng.Assert.assertEquals; +import static org.testng.Assert.*; public class ConnectionTests extends BaseTest { protected StubConnection conn; - protected static String maxIdentifier; @BeforeMethod public void setUpMethod() throws Exception { conn = new StubConnection(); } - @BeforeClass - public static void setUpClass() throws Exception { - int maxLen = 128; - StringBuilder s = new StringBuilder(maxLen); - for (int i = 0; i < maxLen; i++) { - s.append('a'); - } - maxIdentifier = s.toString(); - } - /* * Verify that enquoteLiteral creates a valid literal and converts every * single quote to two single quotes */ - @Test(dataProvider = "validEnquotedLiteralValues") public void test00(String s, String expected) throws SQLException { assertEquals(conn.enquoteLiteral(s), expected); @@ -134,98 +120,4 @@ public void test07(String s, String expected) throws SQLException { public void test08() throws SQLException { conn.enquoteNCharLiteral(null); } - - /* - * DataProvider used to provide strings that will be used to validate - * that enquoteLiteral converts a string to a literal and every instance of - * a single quote will be converted into two single quotes in the literal. - */ - @DataProvider(name = "validEnquotedLiteralValues") - protected Object[][] validEnquotedLiteralValues() { - return new Object[][]{ - {"Hello", "'Hello'"}, - {"G'Day", "'G''Day'"}, - {"'G''Day'", "'''G''''Day'''"}, - {"I'''M", "'I''''''M'"}, - {"The Dark Knight", "'The Dark Knight'"} - }; - } - - /* - * DataProvider used to provide strings that will be used to validate - * that enqouteIdentifier returns a simple SQL Identifier or a double - * quoted identifier - */ - @DataProvider(name = "validIdentifierValues") - protected Object[][] validEnquotedIdentifierValues() { - return new Object[][]{ - {"b", false, "b"}, - {"b", true, "\"b\""}, - {maxIdentifier, false, maxIdentifier}, - {maxIdentifier, true, "\"" + maxIdentifier + "\""}, - {"Hello", false, "Hello"}, - {"Hello", true, "\"Hello\""}, - {"G'Day", false, "\"G'Day\""}, - {"G'Day", true, "\"G'Day\""}, - {"Bruce Wayne", false, "\"Bruce Wayne\""}, - {"Bruce Wayne", true, "\"Bruce Wayne\""}, - {"GoodDay$", false, "\"GoodDay$\""}, - {"GoodDay$", true, "\"GoodDay$\""}, - }; - } - - /* - * DataProvider used to provide strings are invalid for enquoteIdentifier - * resulting in a SQLException being thrown - */ - @DataProvider(name = "invalidIdentifierValues") - protected Object[][] invalidEnquotedIdentifierValues() { - return new Object[][]{ - {"Hel\"lo", false}, - {"\"Hel\"lo\"", true}, - {"Hello" + '\0', false}, - {"", false}, - {maxIdentifier + 'a', false}, - }; - } - - /* - * DataProvider used to provide strings that will be used to validate - * that isSimpleIdentifier returns the correct value based on the - * identifier specified. - */ - @DataProvider(name = "simpleIdentifierValues") - protected Object[][] simpleIdentifierValues() { - return new Object[][]{ - {"b", true}, - {"Hello", true}, - {"\"Gotham\"", false}, - {"G'Day", false}, - {"Bruce Wayne", false}, - {"GoodDay$", false}, - {"Dick_Grayson", true}, - {"Batmobile1966", true}, - {maxIdentifier, true}, - {maxIdentifier + 'a', false}, - {"", false}, - }; - } - - /* - * DataProvider used to provide strings that will be used to validate - * that enquoteNCharLiteral converts a string to a National Character - * literal and every instance of - * a single quote will be converted into two single quotes in the literal. - */ - @DataProvider(name = "validEnquotedNCharLiteralValues") - protected Object[][] validEnquotedNCharLiteralValues() { - return new Object[][]{ - {"Hello", "N'Hello'"}, - {"G'Day", "N'G''Day'"}, - {"'G''Day'", "N'''G''''Day'''"}, - {"I'''M", "N'I''''''M'"}, - {"N'Hello'", "N'N''Hello'''"}, - {"The Dark Knight", "N'The Dark Knight'"} - }; - } } diff --git a/test/jdk/java/sql/testng/test/sql/PreparedStatementTests.java b/test/jdk/java/sql/testng/test/sql/PreparedStatementTests.java index d54763189c528..7813038361deb 100644 --- a/test/jdk/java/sql/testng/test/sql/PreparedStatementTests.java +++ b/test/jdk/java/sql/testng/test/sql/PreparedStatementTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,14 +22,109 @@ */ package test.sql; +import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; -import util.StubPreparedStatement; +import org.testng.annotations.Test; +import util.BaseTest; +import util.StubConnection; -public class PreparedStatementTests extends StatementTests { +import java.sql.PreparedStatement; +import java.sql.SQLException; + +import static org.testng.Assert.assertEquals; + +public class PreparedStatementTests extends BaseTest { + + private PreparedStatement pstmt; @BeforeMethod public void setUpMethod() throws Exception { - stmt = new StubPreparedStatement(); + pstmt = new StubConnection().prepareStatement("Select * from foo were bar = ?"); + } + + @AfterMethod + public void tearDownMethod() throws Exception { + pstmt.close(); + } + + /* + * Verify that enquoteLiteral creates a valid literal and converts every + * single quote to two single quotes + */ + @Test(dataProvider = "validEnquotedLiteralValues") + public void test00(String s, String expected) throws SQLException { + assertEquals(pstmt.enquoteLiteral(s), expected); + } + + /* + * Validate a NullPointerException is thrown if the string passed to + * enquoteLiteral is null + */ + @Test(expectedExceptions = NullPointerException.class) + public void test01() throws SQLException { + pstmt.enquoteLiteral(null); + } + + /* + * Validate that enquoteIdentifier returns the expected value + */ + @Test(dataProvider = "validIdentifierValues") + public void test02(String s, boolean alwaysQuote, String expected) throws SQLException { + assertEquals(pstmt.enquoteIdentifier(s, alwaysQuote), expected); } + /* + * Validate that a SQLException is thrown for values that are not valid + * for a SQL identifier + */ + @Test(dataProvider = "invalidIdentifierValues", + expectedExceptions = SQLException.class) + public void test03(String s, boolean alwaysQuote) throws SQLException { + pstmt.enquoteIdentifier(s, alwaysQuote); + } + + /* + * Validate a NullPointerException is thrown is the string passed to + * enquoteIdentiifer is null + */ + @Test(dataProvider = "trueFalse", + expectedExceptions = NullPointerException.class) + public void test04(boolean alwaysQuote) throws SQLException { + pstmt.enquoteIdentifier(null, alwaysQuote); + } + + /* + * Validate that isSimpleIdentifier returns the expected value + */ + @Test(dataProvider = "simpleIdentifierValues") + public void test05(String s, boolean expected) throws SQLException { + assertEquals(pstmt.isSimpleIdentifier(s), expected); + } + + /* + * Validate a NullPointerException is thrown if the string passed to + * isSimpleIdentifier is null + */ + @Test(expectedExceptions = NullPointerException.class) + public void test06() throws SQLException { + pstmt.isSimpleIdentifier(null); + } + + /* + * Verify that enquoteLiteral creates a valid literal and converts every + * single quote to two single quotes + */ + @Test(dataProvider = "validEnquotedNCharLiteralValues") + public void test07(String s, String expected) throws SQLException { + assertEquals(pstmt.enquoteNCharLiteral(s), expected); + } + + /* + * Validate a NullPointerException is thrown if the string passed to + * enquoteNCharLiteral is null + */ + @Test(expectedExceptions = NullPointerException.class) + public void test08() throws SQLException { + pstmt.enquoteNCharLiteral(null); + } } diff --git a/test/jdk/java/sql/testng/test/sql/StatementTests.java b/test/jdk/java/sql/testng/test/sql/StatementTests.java index 88697b77f9d25..05c6c72cd3d5b 100644 --- a/test/jdk/java/sql/testng/test/sql/StatementTests.java +++ b/test/jdk/java/sql/testng/test/sql/StatementTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,38 +23,31 @@ package test.sql; import java.sql.SQLException; +import java.sql.Statement; + import static org.testng.Assert.assertEquals; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; + +import org.testng.annotations.*; import util.BaseTest; -import util.StubStatement; +import util.StubConnection; public class StatementTests extends BaseTest { - protected StubStatement stmt; - protected static String maxIdentifier; + private Statement stmt; @BeforeMethod public void setUpMethod() throws Exception { - stmt = new StubStatement(); + stmt = new StubConnection().createStatement(); } - @BeforeClass - public static void setUpClass() throws Exception { - int maxLen = 128; - StringBuilder s = new StringBuilder(maxLen); - for (int i = 0; i < maxLen; i++) { - s.append('a'); - } - maxIdentifier = s.toString(); + @AfterMethod + public void tearDownMethod() throws Exception { + stmt.close(); } /* * Verify that enquoteLiteral creates a valid literal and converts every * single quote to two single quotes */ - @Test(dataProvider = "validEnquotedLiteralValues") public void test00(String s, String expected) throws SQLException { assertEquals(stmt.enquoteLiteral(s), expected); @@ -67,7 +60,6 @@ public void test00(String s, String expected) throws SQLException { @Test(expectedExceptions = NullPointerException.class) public void test01() throws SQLException { stmt.enquoteLiteral(null); - } /* @@ -76,7 +68,6 @@ public void test01() throws SQLException { @Test(dataProvider = "validIdentifierValues") public void test02(String s, boolean alwaysQuote, String expected) throws SQLException { assertEquals(stmt.enquoteIdentifier(s, alwaysQuote), expected); - } /* @@ -87,7 +78,6 @@ public void test02(String s, boolean alwaysQuote, String expected) throws SQLExc expectedExceptions = SQLException.class) public void test03(String s, boolean alwaysQuote) throws SQLException { stmt.enquoteIdentifier(s, alwaysQuote); - } /* @@ -98,7 +88,6 @@ public void test03(String s, boolean alwaysQuote) throws SQLException { expectedExceptions = NullPointerException.class) public void test04(boolean alwaysQuote) throws SQLException { stmt.enquoteIdentifier(null, alwaysQuote); - } /* @@ -116,7 +105,6 @@ public void test05(String s, boolean expected) throws SQLException { @Test(expectedExceptions = NullPointerException.class) public void test06() throws SQLException { stmt.isSimpleIdentifier(null); - } /* @@ -136,97 +124,4 @@ public void test07(String s, String expected) throws SQLException { public void test08() throws SQLException { stmt.enquoteNCharLiteral(null); } - - /* - * DataProvider used to provide strings that will be used to validate - * that enquoteLiteral converts a string to a literal and every instance of - * a single quote will be converted into two single quotes in the literal. - */ - @DataProvider(name = "validEnquotedLiteralValues") - protected Object[][] validEnquotedLiteralValues() { - return new Object[][]{ - {"Hello", "'Hello'"}, - {"G'Day", "'G''Day'"}, - {"'G''Day'", "'''G''''Day'''"}, - {"I'''M", "'I''''''M'"}, - {"The Dark Knight", "'The Dark Knight'"} - - }; - } - - /* - * DataProvider used to provide strings that will be used to validate - * that enqouteIdentifier returns a simple SQL Identifier or a double - * quoted identifier - */ - @DataProvider(name = "validIdentifierValues") - protected Object[][] validEnquotedIdentifierValues() { - return new Object[][]{ - {"b", false, "b"}, - {"b", true, "\"b\""}, - {maxIdentifier, false, maxIdentifier}, - {maxIdentifier, true, "\"" + maxIdentifier + "\""}, - {"Hello", false, "Hello"}, - {"Hello", true, "\"Hello\""}, - {"G'Day", false, "\"G'Day\""}, - {"G'Day", true, "\"G'Day\""}, - {"Bruce Wayne", false, "\"Bruce Wayne\""}, - {"Bruce Wayne", true, "\"Bruce Wayne\""}, - {"GoodDay$", false, "\"GoodDay$\""}, - {"GoodDay$", true, "\"GoodDay$\""},}; - } - - /* - * DataProvider used to provide strings are invalid for enquoteIdentifier - * resulting in a SQLException being thrown - */ - @DataProvider(name = "invalidIdentifierValues") - protected Object[][] invalidEnquotedIdentifierValues() { - return new Object[][]{ - {"Hel\"lo", false}, - {"\"Hel\"lo\"", true}, - {"Hello" + '\0', false}, - {"", false}, - {maxIdentifier + 'a', false},}; - } - - /* - * DataProvider used to provide strings that will be used to validate - * that isSimpleIdentifier returns the correct value based on the - * identifier specified. - */ - @DataProvider(name = "simpleIdentifierValues") - protected Object[][] simpleIdentifierValues() { - return new Object[][]{ - {"b", true}, - {"Hello", true}, - {"\"Gotham\"", false}, - {"G'Day", false}, - {"Bruce Wayne", false}, - {"GoodDay$", false}, - {"Dick_Grayson", true}, - {"Batmobile1966", true}, - {maxIdentifier, true}, - {maxIdentifier + 'a', false}, - {"", false},}; - } - - /* - * DataProvider used to provide strings that will be used to validate - * that enquoteNCharLiteral converts a string to a National Character - * literal and every instance of - * a single quote will be converted into two single quotes in the literal. - */ - @DataProvider(name = "validEnquotedNCharLiteralValues") - protected Object[][] validEnquotedNCharLiteralValues() { - return new Object[][]{ - {"Hello", "N'Hello'"}, - {"G'Day", "N'G''Day'"}, - {"'G''Day'", "N'''G''''Day'''"}, - {"I'''M", "N'I''''''M'"}, - {"N'Hello'", "N'N''Hello'''"}, - {"The Dark Knight", "N'The Dark Knight'"} - - }; - } } diff --git a/test/jdk/java/sql/testng/util/BaseTest.java b/test/jdk/java/sql/testng/util/BaseTest.java index 11e156f878bd6..8751183726dd9 100644 --- a/test/jdk/java/sql/testng/util/BaseTest.java +++ b/test/jdk/java/sql/testng/util/BaseTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,13 +27,9 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -import java.security.Policy; import java.sql.JDBCType; import java.sql.SQLException; -import org.testng.annotations.AfterClass; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; + import org.testng.annotations.DataProvider; public class BaseTest { @@ -47,22 +43,7 @@ public class BaseTest { protected final int errorCode = 21; protected final String[] msgs = {"Exception 1", "cause 1", "Exception 2", "Exception 3", "cause 2"}; - - @BeforeClass - public static void setUpClass() throws Exception { - } - - @AfterClass - public static void tearDownClass() throws Exception { - } - - @BeforeMethod - public void setUpMethod() throws Exception { - } - - @AfterMethod - public void tearDownMethod() throws Exception { - } + private static final String MAX_LENGTH_IDENTIFIER = "a".repeat(128); /* * Take some form of SQLException, serialize and deserialize it @@ -116,4 +97,99 @@ protected Object[][] jdbcTypes() { } return o; } + + /* + * DataProvider used to provide strings that will be used to validate + * that enquoteLiteral converts a string to a literal and every instance of + * a single quote will be converted into two single quotes in the literal. + */ + @DataProvider(name = "validEnquotedLiteralValues") + protected Object[][] validEnquotedLiteralValues() { + return new Object[][]{ + {"Hello", "'Hello'"}, + {"G'Day", "'G''Day'"}, + {"'G''Day'", "'''G''''Day'''"}, + {"I'''M", "'I''''''M'"}, + {"The Dark Knight", "'The Dark Knight'"}, + }; + } + + /* + * DataProvider used to provide strings that will be used to validate + * that enqouteIdentifier returns a simple SQL Identifier or a + * quoted identifier + */ + @DataProvider(name = "validIdentifierValues") + protected Object[][] validEnquotedIdentifierValues() { + return new Object[][]{ + {"b", false, "b"}, + {"b", true, "\"b\""}, + {MAX_LENGTH_IDENTIFIER, false, MAX_LENGTH_IDENTIFIER}, + {MAX_LENGTH_IDENTIFIER, true, "\"" + MAX_LENGTH_IDENTIFIER + "\""}, + {"Hello", false, "Hello"}, + {"Hello", true, "\"Hello\""}, + {"G'Day", false, "\"G'Day\""}, + {"G'Day", true, "\"G'Day\""}, + {"Bruce Wayne", false, "\"Bruce Wayne\""}, + {"Bruce Wayne", true, "\"Bruce Wayne\""}, + {"select", false, "\"select\""}, + {"table", true, "\"table\""}, + {"GoodDay$", false, "\"GoodDay$\""}, + {"GoodDay$", true, "\"GoodDay$\""},}; + } + + /* + * DataProvider used to provide strings are invalid for enquoteIdentifier + * resulting in a SQLException being thrown + */ + @DataProvider(name = "invalidIdentifierValues") + protected Object[][] invalidEnquotedIdentifierValues() { + return new Object[][]{ + {"Hel\"lo", false}, + {"\"Hel\"lo\"", true}, + {"Hello" + '\0', false}, + {"", false}, + {MAX_LENGTH_IDENTIFIER + 'a', false},}; + } + + /* + * DataProvider used to provide strings that will be used to validate + * that isSimpleIdentifier returns the correct value based on the + * identifier specified. + */ + @DataProvider(name = "simpleIdentifierValues") + protected Object[][] simpleIdentifierValues() { + return new Object[][]{ + {"b", true}, + {"Hello", true}, + {"\"Gotham\"", false}, + {"G'Day", false}, + {"Bruce Wayne", false}, + {"GoodDay$", false}, + {"Dick_Grayson", true}, + {"Batmobile1966", true}, + {MAX_LENGTH_IDENTIFIER, true}, + {MAX_LENGTH_IDENTIFIER + 'a', false}, + {"", false}, + {"select", false} + }; + } + + /* + * DataProvider used to provide strings that will be used to validate + * that enquoteNCharLiteral converts a string to a National Character + * literal and every instance of + * a single quote will be converted into two single quotes in the literal. + */ + @DataProvider(name = "validEnquotedNCharLiteralValues") + protected Object[][] validEnquotedNCharLiteralValues() { + return new Object[][]{ + {"Hello", "N'Hello'"}, + {"G'Day", "N'G''Day'"}, + {"'G''Day'", "N'''G''''Day'''"}, + {"I'''M", "N'I''''''M'"}, + {"N'Hello'", "N'N''Hello'''"}, + {"The Dark Knight", "N'The Dark Knight'"} + }; + } } diff --git a/test/jdk/java/sql/testng/util/StubCallableStatement.java b/test/jdk/java/sql/testng/util/StubCallableStatement.java index 1833a44d56d63..4a7c27314bbfc 100644 --- a/test/jdk/java/sql/testng/util/StubCallableStatement.java +++ b/test/jdk/java/sql/testng/util/StubCallableStatement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,24 +26,17 @@ import java.io.Reader; import java.math.BigDecimal; import java.net.URL; -import java.sql.Array; -import java.sql.Blob; -import java.sql.CallableStatement; -import java.sql.Clob; -import java.sql.Date; -import java.sql.NClob; -import java.sql.Ref; -import java.sql.RowId; -import java.sql.SQLException; -import java.sql.SQLXML; -import java.sql.Time; -import java.sql.Timestamp; +import java.sql.*; import java.util.Calendar; import java.util.Map; public class StubCallableStatement extends StubPreparedStatement implements CallableStatement{ + public StubCallableStatement(StubConnection con) { + super(con); + } + @Override public void registerOutParameter(int parameterIndex, int sqlType) throws SQLException { throw new UnsupportedOperationException("Not supported yet."); @@ -608,5 +601,4 @@ public T getObject(int parameterIndex, Class type) throws SQLException { public T getObject(String parameterName, Class type) throws SQLException { throw new UnsupportedOperationException("Not supported yet."); } - } diff --git a/test/jdk/java/sql/testng/util/StubConnection.java b/test/jdk/java/sql/testng/util/StubConnection.java index b9ab97062d353..cd013572adc5e 100644 --- a/test/jdk/java/sql/testng/util/StubConnection.java +++ b/test/jdk/java/sql/testng/util/StubConnection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,20 +44,25 @@ public class StubConnection implements Connection { private boolean autoCommit = false; + private boolean isclosed; + + public StubConnection() { + isclosed = false; + } @Override public Statement createStatement() throws SQLException { - throw new UnsupportedOperationException("Not supported yet."); + return new StubStatement(this); } @Override public PreparedStatement prepareStatement(String sql) throws SQLException { - throw new UnsupportedOperationException("Not supported yet."); + return new StubPreparedStatement(this); } @Override public CallableStatement prepareCall(String sql) throws SQLException { - throw new UnsupportedOperationException("Not supported yet."); + return new StubCallableStatement(this); } @Override @@ -89,17 +94,17 @@ public void rollback() throws SQLException { @Override public void close() throws SQLException { - throw new UnsupportedOperationException("Not supported yet."); + isclosed = true; } @Override public boolean isClosed() throws SQLException { - throw new UnsupportedOperationException("Not supported yet."); + return isclosed; } @Override public DatabaseMetaData getMetaData() throws SQLException { - throw new UnsupportedOperationException("Not supported yet."); + return new StubDatabaseMetaData(); } @Override diff --git a/test/jdk/java/sql/testng/util/StubDatabaseMetaData.java b/test/jdk/java/sql/testng/util/StubDatabaseMetaData.java new file mode 100644 index 0000000000000..3bf70afaa8f69 --- /dev/null +++ b/test/jdk/java/sql/testng/util/StubDatabaseMetaData.java @@ -0,0 +1,907 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package util; + +import java.sql.*; + +public class StubDatabaseMetaData implements DatabaseMetaData { + @Override + public boolean allProceduresAreCallable() throws SQLException { + return false; + } + + @Override + public boolean allTablesAreSelectable() throws SQLException { + return false; + } + + @Override + public String getURL() throws SQLException { + return ""; + } + + @Override + public String getUserName() throws SQLException { + return ""; + } + + @Override + public boolean isReadOnly() throws SQLException { + return false; + } + + @Override + public boolean nullsAreSortedHigh() throws SQLException { + return false; + } + + @Override + public boolean nullsAreSortedLow() throws SQLException { + return false; + } + + @Override + public boolean nullsAreSortedAtStart() throws SQLException { + return false; + } + + @Override + public boolean nullsAreSortedAtEnd() throws SQLException { + return false; + } + + @Override + public String getDatabaseProductName() throws SQLException { + return ""; + } + + @Override + public String getDatabaseProductVersion() throws SQLException { + return ""; + } + + @Override + public String getDriverName() throws SQLException { + return ""; + } + + @Override + public String getDriverVersion() throws SQLException { + return ""; + } + + @Override + public int getDriverMajorVersion() { + return 0; + } + + @Override + public int getDriverMinorVersion() { + return 0; + } + + @Override + public boolean usesLocalFiles() throws SQLException { + return false; + } + + @Override + public boolean usesLocalFilePerTable() throws SQLException { + return false; + } + + @Override + public boolean supportsMixedCaseIdentifiers() throws SQLException { + return false; + } + + @Override + public boolean storesUpperCaseIdentifiers() throws SQLException { + return false; + } + + @Override + public boolean storesLowerCaseIdentifiers() throws SQLException { + return false; + } + + @Override + public boolean storesMixedCaseIdentifiers() throws SQLException { + return false; + } + + @Override + public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException { + return false; + } + + @Override + public boolean storesUpperCaseQuotedIdentifiers() throws SQLException { + return false; + } + + @Override + public boolean storesLowerCaseQuotedIdentifiers() throws SQLException { + return false; + } + + @Override + public boolean storesMixedCaseQuotedIdentifiers() throws SQLException { + return false; + } + + @Override + public String getIdentifierQuoteString() throws SQLException { + return "\""; + } + + @Override + public String getSQLKeywords() throws SQLException { + return ""; + } + + @Override + public String getNumericFunctions() throws SQLException { + return ""; + } + + @Override + public String getStringFunctions() throws SQLException { + return ""; + } + + @Override + public String getSystemFunctions() throws SQLException { + return ""; + } + + @Override + public String getTimeDateFunctions() throws SQLException { + return ""; + } + + @Override + public String getSearchStringEscape() throws SQLException { + return ""; + } + + @Override + public String getExtraNameCharacters() throws SQLException { + return ""; + } + + @Override + public boolean supportsAlterTableWithAddColumn() throws SQLException { + return false; + } + + @Override + public boolean supportsAlterTableWithDropColumn() throws SQLException { + return false; + } + + @Override + public boolean supportsColumnAliasing() throws SQLException { + return false; + } + + @Override + public boolean nullPlusNonNullIsNull() throws SQLException { + return false; + } + + @Override + public boolean supportsConvert() throws SQLException { + return false; + } + + @Override + public boolean supportsConvert(int fromType, int toType) throws SQLException { + return false; + } + + @Override + public boolean supportsTableCorrelationNames() throws SQLException { + return false; + } + + @Override + public boolean supportsDifferentTableCorrelationNames() throws SQLException { + return false; + } + + @Override + public boolean supportsExpressionsInOrderBy() throws SQLException { + return false; + } + + @Override + public boolean supportsOrderByUnrelated() throws SQLException { + return false; + } + + @Override + public boolean supportsGroupBy() throws SQLException { + return false; + } + + @Override + public boolean supportsGroupByUnrelated() throws SQLException { + return false; + } + + @Override + public boolean supportsGroupByBeyondSelect() throws SQLException { + return false; + } + + @Override + public boolean supportsLikeEscapeClause() throws SQLException { + return false; + } + + @Override + public boolean supportsMultipleResultSets() throws SQLException { + return false; + } + + @Override + public boolean supportsMultipleTransactions() throws SQLException { + return false; + } + + @Override + public boolean supportsNonNullableColumns() throws SQLException { + return false; + } + + @Override + public boolean supportsMinimumSQLGrammar() throws SQLException { + return false; + } + + @Override + public boolean supportsCoreSQLGrammar() throws SQLException { + return false; + } + + @Override + public boolean supportsExtendedSQLGrammar() throws SQLException { + return false; + } + + @Override + public boolean supportsANSI92EntryLevelSQL() throws SQLException { + return false; + } + + @Override + public boolean supportsANSI92IntermediateSQL() throws SQLException { + return false; + } + + @Override + public boolean supportsANSI92FullSQL() throws SQLException { + return false; + } + + @Override + public boolean supportsIntegrityEnhancementFacility() throws SQLException { + return false; + } + + @Override + public boolean supportsOuterJoins() throws SQLException { + return false; + } + + @Override + public boolean supportsFullOuterJoins() throws SQLException { + return false; + } + + @Override + public boolean supportsLimitedOuterJoins() throws SQLException { + return false; + } + + @Override + public String getSchemaTerm() throws SQLException { + return ""; + } + + @Override + public String getProcedureTerm() throws SQLException { + return ""; + } + + @Override + public String getCatalogTerm() throws SQLException { + return ""; + } + + @Override + public boolean isCatalogAtStart() throws SQLException { + return false; + } + + @Override + public String getCatalogSeparator() throws SQLException { + return ""; + } + + @Override + public boolean supportsSchemasInDataManipulation() throws SQLException { + return false; + } + + @Override + public boolean supportsSchemasInProcedureCalls() throws SQLException { + return false; + } + + @Override + public boolean supportsSchemasInTableDefinitions() throws SQLException { + return false; + } + + @Override + public boolean supportsSchemasInIndexDefinitions() throws SQLException { + return false; + } + + @Override + public boolean supportsSchemasInPrivilegeDefinitions() throws SQLException { + return false; + } + + @Override + public boolean supportsCatalogsInDataManipulation() throws SQLException { + return false; + } + + @Override + public boolean supportsCatalogsInProcedureCalls() throws SQLException { + return false; + } + + @Override + public boolean supportsCatalogsInTableDefinitions() throws SQLException { + return false; + } + + @Override + public boolean supportsCatalogsInIndexDefinitions() throws SQLException { + return false; + } + + @Override + public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException { + return false; + } + + @Override + public boolean supportsPositionedDelete() throws SQLException { + return false; + } + + @Override + public boolean supportsPositionedUpdate() throws SQLException { + return false; + } + + @Override + public boolean supportsSelectForUpdate() throws SQLException { + return false; + } + + @Override + public boolean supportsStoredProcedures() throws SQLException { + return false; + } + + @Override + public boolean supportsSubqueriesInComparisons() throws SQLException { + return false; + } + + @Override + public boolean supportsSubqueriesInExists() throws SQLException { + return false; + } + + @Override + public boolean supportsSubqueriesInIns() throws SQLException { + return false; + } + + @Override + public boolean supportsSubqueriesInQuantifieds() throws SQLException { + return false; + } + + @Override + public boolean supportsCorrelatedSubqueries() throws SQLException { + return false; + } + + @Override + public boolean supportsUnion() throws SQLException { + return false; + } + + @Override + public boolean supportsUnionAll() throws SQLException { + return false; + } + + @Override + public boolean supportsOpenCursorsAcrossCommit() throws SQLException { + return false; + } + + @Override + public boolean supportsOpenCursorsAcrossRollback() throws SQLException { + return false; + } + + @Override + public boolean supportsOpenStatementsAcrossCommit() throws SQLException { + return false; + } + + @Override + public boolean supportsOpenStatementsAcrossRollback() throws SQLException { + return false; + } + + @Override + public int getMaxBinaryLiteralLength() throws SQLException { + return 0; + } + + @Override + public int getMaxCharLiteralLength() throws SQLException { + return 0; + } + + @Override + public int getMaxColumnNameLength() throws SQLException { + return 0; + } + + @Override + public int getMaxColumnsInGroupBy() throws SQLException { + return 0; + } + + @Override + public int getMaxColumnsInIndex() throws SQLException { + return 0; + } + + @Override + public int getMaxColumnsInOrderBy() throws SQLException { + return 0; + } + + @Override + public int getMaxColumnsInSelect() throws SQLException { + return 0; + } + + @Override + public int getMaxColumnsInTable() throws SQLException { + return 0; + } + + @Override + public int getMaxConnections() throws SQLException { + return 0; + } + + @Override + public int getMaxCursorNameLength() throws SQLException { + return 0; + } + + @Override + public int getMaxIndexLength() throws SQLException { + return 0; + } + + @Override + public int getMaxSchemaNameLength() throws SQLException { + return 0; + } + + @Override + public int getMaxProcedureNameLength() throws SQLException { + return 0; + } + + @Override + public int getMaxCatalogNameLength() throws SQLException { + return 0; + } + + @Override + public int getMaxRowSize() throws SQLException { + return 0; + } + + @Override + public boolean doesMaxRowSizeIncludeBlobs() throws SQLException { + return false; + } + + @Override + public int getMaxStatementLength() throws SQLException { + return 0; + } + + @Override + public int getMaxStatements() throws SQLException { + return 0; + } + + @Override + public int getMaxTableNameLength() throws SQLException { + return 0; + } + + @Override + public int getMaxTablesInSelect() throws SQLException { + return 0; + } + + @Override + public int getMaxUserNameLength() throws SQLException { + return 0; + } + + @Override + public int getDefaultTransactionIsolation() throws SQLException { + return 0; + } + + @Override + public boolean supportsTransactions() throws SQLException { + return false; + } + + @Override + public boolean supportsTransactionIsolationLevel(int level) throws SQLException { + return false; + } + + @Override + public boolean supportsDataDefinitionAndDataManipulationTransactions() throws SQLException { + return false; + } + + @Override + public boolean supportsDataManipulationTransactionsOnly() throws SQLException { + return false; + } + + @Override + public boolean dataDefinitionCausesTransactionCommit() throws SQLException { + return false; + } + + @Override + public boolean dataDefinitionIgnoredInTransactions() throws SQLException { + return false; + } + + @Override + public ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException { + return null; + } + + @Override + public ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) throws SQLException { + return null; + } + + @Override + public ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, String[] types) throws SQLException { + return null; + } + + @Override + public ResultSet getSchemas() throws SQLException { + return null; + } + + @Override + public ResultSet getCatalogs() throws SQLException { + return null; + } + + @Override + public ResultSet getTableTypes() throws SQLException { + return null; + } + + @Override + public ResultSet getColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) throws SQLException { + return null; + } + + @Override + public ResultSet getColumnPrivileges(String catalog, String schema, String table, String columnNamePattern) throws SQLException { + return null; + } + + @Override + public ResultSet getTablePrivileges(String catalog, String schemaPattern, String tableNamePattern) throws SQLException { + return null; + } + + @Override + public ResultSet getBestRowIdentifier(String catalog, String schema, String table, int scope, boolean nullable) throws SQLException { + return null; + } + + @Override + public ResultSet getVersionColumns(String catalog, String schema, String table) throws SQLException { + return null; + } + + @Override + public ResultSet getPrimaryKeys(String catalog, String schema, String table) throws SQLException { + return null; + } + + @Override + public ResultSet getImportedKeys(String catalog, String schema, String table) throws SQLException { + return null; + } + + @Override + public ResultSet getExportedKeys(String catalog, String schema, String table) throws SQLException { + return null; + } + + @Override + public ResultSet getCrossReference(String parentCatalog, String parentSchema, String parentTable, String foreignCatalog, String foreignSchema, String foreignTable) throws SQLException { + return null; + } + + @Override + public ResultSet getTypeInfo() throws SQLException { + return null; + } + + @Override + public ResultSet getIndexInfo(String catalog, String schema, String table, boolean unique, boolean approximate) throws SQLException { + return null; + } + + @Override + public boolean supportsResultSetType(int type) throws SQLException { + return false; + } + + @Override + public boolean supportsResultSetConcurrency(int type, int concurrency) throws SQLException { + return false; + } + + @Override + public boolean ownUpdatesAreVisible(int type) throws SQLException { + return false; + } + + @Override + public boolean ownDeletesAreVisible(int type) throws SQLException { + return false; + } + + @Override + public boolean ownInsertsAreVisible(int type) throws SQLException { + return false; + } + + @Override + public boolean othersUpdatesAreVisible(int type) throws SQLException { + return false; + } + + @Override + public boolean othersDeletesAreVisible(int type) throws SQLException { + return false; + } + + @Override + public boolean othersInsertsAreVisible(int type) throws SQLException { + return false; + } + + @Override + public boolean updatesAreDetected(int type) throws SQLException { + return false; + } + + @Override + public boolean deletesAreDetected(int type) throws SQLException { + return false; + } + + @Override + public boolean insertsAreDetected(int type) throws SQLException { + return false; + } + + @Override + public boolean supportsBatchUpdates() throws SQLException { + return false; + } + + @Override + public ResultSet getUDTs(String catalog, String schemaPattern, String typeNamePattern, int[] types) throws SQLException { + return null; + } + + @Override + public Connection getConnection() throws SQLException { + return null; + } + + @Override + public boolean supportsSavepoints() throws SQLException { + return false; + } + + @Override + public boolean supportsNamedParameters() throws SQLException { + return false; + } + + @Override + public boolean supportsMultipleOpenResults() throws SQLException { + return false; + } + + @Override + public boolean supportsGetGeneratedKeys() throws SQLException { + return false; + } + + @Override + public ResultSet getSuperTypes(String catalog, String schemaPattern, String typeNamePattern) throws SQLException { + return null; + } + + @Override + public ResultSet getSuperTables(String catalog, String schemaPattern, String tableNamePattern) throws SQLException { + return null; + } + + @Override + public ResultSet getAttributes(String catalog, String schemaPattern, String typeNamePattern, String attributeNamePattern) throws SQLException { + return null; + } + + @Override + public boolean supportsResultSetHoldability(int holdability) throws SQLException { + return false; + } + + @Override + public int getResultSetHoldability() throws SQLException { + return 0; + } + + @Override + public int getDatabaseMajorVersion() throws SQLException { + return 0; + } + + @Override + public int getDatabaseMinorVersion() throws SQLException { + return 0; + } + + @Override + public int getJDBCMajorVersion() throws SQLException { + return 0; + } + + @Override + public int getJDBCMinorVersion() throws SQLException { + return 0; + } + + @Override + public int getSQLStateType() throws SQLException { + return 0; + } + + @Override + public boolean locatorsUpdateCopy() throws SQLException { + return false; + } + + @Override + public boolean supportsStatementPooling() throws SQLException { + return false; + } + + @Override + public RowIdLifetime getRowIdLifetime() throws SQLException { + return null; + } + + @Override + public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLException { + return null; + } + + @Override + public boolean supportsStoredFunctionsUsingCallSyntax() throws SQLException { + return false; + } + + @Override + public boolean autoCommitFailureClosesAllResultSets() throws SQLException { + return false; + } + + @Override + public ResultSet getClientInfoProperties() throws SQLException { + return null; + } + + @Override + public ResultSet getFunctions(String catalog, String schemaPattern, String functionNamePattern) throws SQLException { + return null; + } + + @Override + public ResultSet getFunctionColumns(String catalog, String schemaPattern, String functionNamePattern, String columnNamePattern) throws SQLException { + return null; + } + + @Override + public ResultSet getPseudoColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) throws SQLException { + return null; + } + + @Override + public boolean generatedKeyAlwaysReturned() throws SQLException { + return false; + } + + @Override + public T unwrap(Class iface) throws SQLException { + return null; + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + return false; + } +} diff --git a/test/jdk/java/sql/testng/util/StubPreparedStatement.java b/test/jdk/java/sql/testng/util/StubPreparedStatement.java index 4e272bcee2d59..a3b95a65f4ea1 100644 --- a/test/jdk/java/sql/testng/util/StubPreparedStatement.java +++ b/test/jdk/java/sql/testng/util/StubPreparedStatement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,25 +26,15 @@ import java.io.Reader; import java.math.BigDecimal; import java.net.URL; -import java.sql.Array; -import java.sql.Blob; -import java.sql.Clob; -import java.sql.Date; -import java.sql.NClob; -import java.sql.ParameterMetaData; -import java.sql.PreparedStatement; -import java.sql.Ref; -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.RowId; -import java.sql.SQLException; -import java.sql.SQLXML; -import java.sql.Time; -import java.sql.Timestamp; +import java.sql.*; import java.util.Calendar; public class StubPreparedStatement extends StubStatement implements PreparedStatement{ + public StubPreparedStatement(StubConnection con) { + super(con); + } + @Override public ResultSet executeQuery() throws SQLException { throw new UnsupportedOperationException("Not supported yet."); @@ -319,5 +309,4 @@ public void setBlob(int parameterIndex, InputStream inputStream) throws SQLExcep public void setNClob(int parameterIndex, Reader reader) throws SQLException { throw new UnsupportedOperationException("Not supported yet."); } - } diff --git a/test/jdk/java/sql/testng/util/StubStatement.java b/test/jdk/java/sql/testng/util/StubStatement.java index df792e2e4fe03..c8f6a3ac07167 100644 --- a/test/jdk/java/sql/testng/util/StubStatement.java +++ b/test/jdk/java/sql/testng/util/StubStatement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,11 +27,15 @@ import java.sql.SQLException; import java.sql.SQLWarning; import java.sql.Statement; -import java.util.regex.Pattern; -import static java.util.stream.Collectors.joining; public class StubStatement implements Statement { + protected final Connection con; + + public StubStatement(StubConnection con) { + this.con = con; + } + @Override public ResultSet executeQuery(String sql) throws SQLException { throw new UnsupportedOperationException("Not supported yet."); @@ -44,7 +48,7 @@ public int executeUpdate(String sql) throws SQLException { @Override public void close() throws SQLException { - throw new UnsupportedOperationException("Not supported yet."); + con.close(); } @Override @@ -169,7 +173,7 @@ public int[] executeBatch() throws SQLException { @Override public Connection getConnection() throws SQLException { - throw new UnsupportedOperationException("Not supported yet."); + return con; } @Override @@ -251,7 +255,4 @@ public T unwrap(Class iface) throws SQLException { public boolean isWrapperFor(Class iface) throws SQLException { throw new UnsupportedOperationException("Not supported yet."); } - - - } diff --git a/test/jdk/javax/sql/testng/test/rowset/serial/SQLInputImplTests.java b/test/jdk/javax/sql/testng/test/rowset/serial/SQLInputImplTests.java index 95cc508828a08..5bd10ed8848f0 100644 --- a/test/jdk/javax/sql/testng/test/rowset/serial/SQLInputImplTests.java +++ b/test/jdk/javax/sql/testng/test/rowset/serial/SQLInputImplTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -55,7 +55,6 @@ public class SQLInputImplTests extends BaseTest { private final String sqlType = "SUPERHERO"; @BeforeMethod - @Override public void setUpMethod() throws Exception { map = new HashMap<>(); impl = new TestSQLDataImpl("TestSQLData"); @@ -120,7 +119,6 @@ public void test05() throws Exception { SQLInputImpl sqli = new SQLInputImpl(values, map); Object o = sqli.readObject(); assertTrue(hero.equals(o)); - } /* @@ -204,8 +202,6 @@ public void test11() throws Exception { Object[] values = {struct}; SQLInputImpl sqli = new SQLInputImpl(values, map); Object o = sqli.readObject(); - assertTrue(hero.equals(o)); - } } diff --git a/test/jdk/javax/sql/testng/test/rowset/serial/SQLOutputImplTests.java b/test/jdk/javax/sql/testng/test/rowset/serial/SQLOutputImplTests.java index 00f62df6f7966..1f90c9981a7eb 100644 --- a/test/jdk/javax/sql/testng/test/rowset/serial/SQLOutputImplTests.java +++ b/test/jdk/javax/sql/testng/test/rowset/serial/SQLOutputImplTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -65,7 +65,6 @@ public class SQLOutputImplTests extends BaseTest { private SQLOutputImpl outImpl; @BeforeMethod - @Override public void setUpMethod() throws Exception { results = new Vector(); impl = new TestSQLDataImpl("TestSQLData"); From 8ccfcdf92cc647840f5487167b6049d8d2552df2 Mon Sep 17 00:00:00 2001 From: Lance Andersen Date: Thu, 6 Nov 2025 09:16:38 -0500 Subject: [PATCH 10/10] Remove extra space in copyright header --- test/jdk/java/sql/testng/test/sql/StatementTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/jdk/java/sql/testng/test/sql/StatementTests.java b/test/jdk/java/sql/testng/test/sql/StatementTests.java index 05c6c72cd3d5b..f2bfe712eb528 100644 --- a/test/jdk/java/sql/testng/test/sql/StatementTests.java +++ b/test/jdk/java/sql/testng/test/sql/StatementTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it