    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      https://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.dbcp2;
    018
    019import java.sql.Connection;
    020import java.sql.ResultSet;
    021import java.sql.SQLException;
    022import java.sql.Statement;
    023import java.util.Arrays;
    024import java.util.Objects;
    025import java.util.function.Function;
    026
    027import org.apache.commons.dbcp2.PoolingConnection.StatementType;
    028
    029/**
    030 * A key uniquely identifying {@link java.sql.PreparedStatement PreparedStatement}s.
    031 *
    032 * @since 2.0
    033 */
    034public class PStmtKey {
    035
    036    /**
    037     * Interface for Prepared or Callable Statement.
    038     */
    039    @FunctionalInterface
    040    private interface StatementBuilder {
    041        Statement createStatement(Connection connection, PStmtKey key) throws SQLException;
    042    }
    043
    044    private static final StatementBuilder CallConcurrency = (c, k) -> c.prepareCall(k.sql, k.resultSetType, k.resultSetConcurrency);
    045    private static final StatementBuilder CallHoldability = (c, k) -> c.prepareCall(k.sql, k.resultSetType, k.resultSetConcurrency, k.resultSetHoldability);
    046    private static final StatementBuilder CallSQL = (c, k) -> c.prepareCall(k.sql);
    047    private static final StatementBuilder StatementAutoGeneratedKeys = (c, k) -> c.prepareStatement(k.sql, k.autoGeneratedKeys);
    048    private static final StatementBuilder StatementColumnIndexes = (c, k) -> c.prepareStatement(k.sql, k.columnIndexes);
    049    private static final StatementBuilder StatementColumnNames = (c, k) -> c.prepareStatement(k.sql, k.columnNames);
    050    private static final StatementBuilder StatementConcurrency = (c, k) -> c.prepareStatement(k.sql, k.resultSetType, k.resultSetConcurrency);
    051    private static final StatementBuilder StatementHoldability = (c, k) -> c.prepareStatement(k.sql, k.resultSetType, k.resultSetConcurrency,
    052        k.resultSetHoldability);
    053    private static final StatementBuilder StatementSQL = (c, k) -> c.prepareStatement(k.sql);
    054
    055    private static StatementBuilder match(final StatementType statementType, final StatementBuilder prep, final StatementBuilder call) {
    056        switch (Objects.requireNonNull(statementType, "statementType")) {
    057        case PREPARED_STATEMENT:
    058            return prep;
    059        case CALLABLE_STATEMENT:
    060            return call;
    061        default:
    062            throw new IllegalArgumentException(statementType.toString());
    063        }
    064    }
    065
    066    /**
    067     * SQL defining Prepared or Callable Statement
    068     */
    069    private final String sql;
    070
    071    /**
    072     * Result set type; one of {@link ResultSet#TYPE_FORWARD_ONLY}, {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or
    073     * {@link ResultSet#TYPE_SCROLL_SENSITIVE}.
    074     */
    075    private final Integer resultSetType;
    076
    077    /**
    078     * Result set concurrency. A concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or
    079     * {@link ResultSet#CONCUR_UPDATABLE}.
    080     */
    081    private final Integer resultSetConcurrency;
    082
    083    /**
    084     * Result set holdability. One of the following {@link ResultSet} constants: {@link ResultSet#HOLD_CURSORS_OVER_COMMIT}
    085     * or {@link ResultSet#CLOSE_CURSORS_AT_COMMIT}.
    086     */
    087    private final Integer resultSetHoldability;
    088
    089    /**
    090     * Database catalog.
    091     */
    092    private final String catalog;
    093
    094    /**
    095     * Database schema.
    096     */
    097    private final String schema;
    098
    099    /**
    100     * A flag indicating whether auto-generated keys should be returned; one of {@link Statement#RETURN_GENERATED_KEYS} or
    101     * {@link Statement#NO_GENERATED_KEYS}.
    102     */
    103    private final Integer autoGeneratedKeys;
    104
    105    /**
    106     * An array of column indexes indicating the columns that should be returned from the inserted row or rows.
    107     */
    108    private final int[] columnIndexes;
    109
    110    /**
    111     * An array of column names indicating the columns that should be returned from the inserted row or rows.
    112     */
    113    private final String[] columnNames;
    114
    115    /**
    116     * Statement builder.
    117     */
    118    private final transient StatementBuilder statementBuilder;
    119
    120    /**
    121     * Statement type, prepared or callable.
    122     */
    123    private final StatementType statementType;
    124
    125    /**
    126     * Constructs a key to uniquely identify a prepared statement.
    127     *
    128     * @param sql The SQL statement.
    129     * @deprecated Use {@link #PStmtKey(String, String, String)}.
    130     */
    131    @Deprecated
    132    public PStmtKey(final String sql) {
    133        this(sql, null, StatementType.PREPARED_STATEMENT);
    134    }
    135
    136    /**
    137     * Constructs a key to uniquely identify a prepared statement.
    138     *
    139     * @param sql The SQL statement.
    140     * @param resultSetType A result set type; one of {@link ResultSet#TYPE_FORWARD_ONLY},
    141     *        {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or {@link ResultSet#TYPE_SCROLL_SENSITIVE}.
    142     * @param resultSetConcurrency A concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or
    143     *        {@link ResultSet#CONCUR_UPDATABLE}.
    144     * @deprecated Use {@link #PStmtKey(String, String, String, int, int)}.
    145     */
    146    @Deprecated
    147    public PStmtKey(final String sql, final int resultSetType, final int resultSetConcurrency) {
    148        this(sql, null, resultSetType, resultSetConcurrency, StatementType.PREPARED_STATEMENT);
    149    }
    150
    151    /**
    152     * Constructs a key to uniquely identify a prepared statement.
    153     *
    154     * @param sql The SQL statement.
    155     * @param catalog The catalog.
    156     * @deprecated Use {@link #PStmtKey(String, String, String)}.
    157     */
    158    @Deprecated
    159    public PStmtKey(final String sql, final String catalog) {
    160        this(sql, catalog, StatementType.PREPARED_STATEMENT);
    161    }
    162
    163    /**
    164     * Constructs a key to uniquely identify a prepared statement.
    165     *
    166     * @param sql The SQL statement.
    167     * @param catalog The catalog.
    168     * @param autoGeneratedKeys A flag indicating whether auto-generated keys should be returned; one of
    169     *        {@link Statement#RETURN_GENERATED_KEYS} or {@link Statement#NO_GENERATED_KEYS}.
    170     * @deprecated Use {@link #PStmtKey(String, String, String, int)}.
    171     */
    172    @Deprecated
    173    public PStmtKey(final String sql, final String catalog, final int autoGeneratedKeys) {
    174        this(sql, catalog, StatementType.PREPARED_STATEMENT, autoGeneratedKeys);
    175    }
    176
    177    /**
    178     * Constructs a key to uniquely identify a prepared statement.
    179     *
    180     * @param sql The SQL statement.
    181     * @param catalog The catalog.
    182     * @param resultSetType A result set type; one of {@link ResultSet#TYPE_FORWARD_ONLY},
    183     *        {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or {@link ResultSet#TYPE_SCROLL_SENSITIVE}.
    184     * @param resultSetConcurrency A concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or
    185     *        {@link ResultSet#CONCUR_UPDATABLE}.
    186     * @deprecated Use {@link #PStmtKey(String, String, String, int, int)}.
    187     */
    188    @Deprecated
    189    public PStmtKey(final String sql, final String catalog, final int resultSetType, final int resultSetConcurrency) {
    190        this(sql, catalog, resultSetType, resultSetConcurrency, StatementType.PREPARED_STATEMENT);
    191    }
    192
    193    /**
    194     * Constructs a key to uniquely identify a prepared statement.
    195     *
    196     * @param sql The SQL statement.
    197     * @param catalog The catalog.
    198     * @param resultSetType a result set type; one of {@link ResultSet#TYPE_FORWARD_ONLY},
    199     *        {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or {@link ResultSet#TYPE_SCROLL_SENSITIVE}.
    200     * @param resultSetConcurrency A concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or
    201     *        {@link ResultSet#CONCUR_UPDATABLE}
    202     * @param resultSetHoldability One of the following {@link ResultSet} constants:
    203     *        {@link ResultSet#HOLD_CURSORS_OVER_COMMIT} or {@link ResultSet#CLOSE_CURSORS_AT_COMMIT}.
    204     * @deprecated Use {@link #PStmtKey(String, String, String, int, int, int)}.
    205     */
    206    @Deprecated
    207    public PStmtKey(final String sql, final String catalog, final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability) {
    208        this(sql, catalog, resultSetType, resultSetConcurrency, resultSetHoldability, StatementType.PREPARED_STATEMENT);
    209    }
    210
    211    /**
    212     * Constructs a key to uniquely identify a prepared statement.
    213     *
    214     * @param sql The SQL statement.
    215     * @param catalog The catalog.
    216     * @param resultSetType a result set type; one of {@link ResultSet#TYPE_FORWARD_ONLY},
    217     *        {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or {@link ResultSet#TYPE_SCROLL_SENSITIVE}
    218     * @param resultSetConcurrency A concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or
    219     *        {@link ResultSet#CONCUR_UPDATABLE}.
    220     * @param resultSetHoldability One of the following {@link ResultSet} constants:
    221     *        {@link ResultSet#HOLD_CURSORS_OVER_COMMIT} or {@link ResultSet#CLOSE_CURSORS_AT_COMMIT}.
    222     * @param statementType The SQL statement type, prepared or callable.
    223     * @deprecated Use {@link #PStmtKey(String, String, String, int, int, int, PoolingConnection.StatementType)}
    224     */
    225    @Deprecated
    226    public PStmtKey(final String sql, final String catalog, final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability,
    227        final StatementType statementType) {
    228        this(sql, catalog, null, resultSetType, resultSetConcurrency, resultSetHoldability, null, null, null, statementType,
    229            k -> match(statementType, StatementHoldability, CallHoldability));
    230    }
    231
    232    /**
    233     * Constructs a key to uniquely identify a prepared statement.
    234     *
    235     * @param sql The SQL statement.
    236     * @param catalog The catalog.
    237     * @param resultSetType A result set type; one of {@link ResultSet#TYPE_FORWARD_ONLY},
    238     *        {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or {@link ResultSet#TYPE_SCROLL_SENSITIVE}.
    239     * @param resultSetConcurrency A concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or
    240     *        {@link ResultSet#CONCUR_UPDATABLE}.
    241     * @param statementType The SQL statement type, prepared or callable.
    242     * @deprecated Use {@link #PStmtKey(String, String, String, int, int, PoolingConnection.StatementType)}.
    243     */
    244    @Deprecated
    245    public PStmtKey(final String sql, final String catalog, final int resultSetType, final int resultSetConcurrency, final StatementType statementType) {
    246        this(sql, catalog, null, resultSetType, resultSetConcurrency, null, null, null, null, statementType,
    247            k -> match(statementType, StatementConcurrency, CallConcurrency));
    248    }
    249
    250    /**
    251     * Constructs a key to uniquely identify a prepared statement.
    252     *
    253     * @param sql The SQL statement.
    254     * @param catalog The catalog.
    255     * @param columnIndexes An array of column indexes indicating the columns that should be returned from the inserted row
    256     *        or rows.
    257     * @deprecated Use {@link #PStmtKey(String, String, String, int[])}.
    258     */
    259    @Deprecated
    260    public PStmtKey(final String sql, final String catalog, final int[] columnIndexes) {
    261        this(sql, catalog, null, null, null, null, null, columnIndexes, null, StatementType.PREPARED_STATEMENT, StatementColumnIndexes);
    262    }
    263
    264    /**
    265     * Constructs a key to uniquely identify a prepared statement.
    266     *
    267     * @param sql The SQL statement.
    268     * @param catalog The catalog.
    269     * @param statementType The SQL statement type, prepared or callable.
    270     * @deprecated Use {@link #PStmtKey(String, String, String, PoolingConnection.StatementType)}.
    271     */
    272    @Deprecated
    273    public PStmtKey(final String sql, final String catalog, final StatementType statementType) {
    274        this(sql, catalog, null, null, null, null, null, null, null, statementType, k -> match(statementType, StatementSQL, CallSQL));
    275    }
    276
    277    /**
    278     * Constructs a key to uniquely identify a prepared statement.
    279     *
    280     * @param sql The SQL statement.
    281     * @param catalog The catalog.
    282     * @param statementType The SQL statement type, prepared or callable.
    283     * @param autoGeneratedKeys A flag indicating whether auto-generated keys should be returned; one of
    284     *        {@link Statement#RETURN_GENERATED_KEYS} or {@link Statement#NO_GENERATED_KEYS}.
    285     * @deprecated Use {@link #PStmtKey(String, String, String, PoolingConnection.StatementType, Integer)}
    286     */
    287    @Deprecated
    288    public PStmtKey(final String sql, final String catalog, final StatementType statementType, final Integer autoGeneratedKeys) {
    289        this(sql, catalog, null, null, null, null, autoGeneratedKeys, null, null, statementType,
    290            k -> match(statementType, StatementAutoGeneratedKeys, CallSQL));
    291    }
    292
    293    /**
    294     * Constructs a key to uniquely identify a prepared statement.
    295     *
    296     * @param sql The SQL statement.
    297     * @param catalog The catalog.
    298     * @param schema The schema
    299     * @since 2.5.0
    300     */
    301    public PStmtKey(final String sql, final String catalog, final String schema) {
    302        this(sql, catalog, schema, StatementType.PREPARED_STATEMENT);
    303    }
    304
    305    /**
    306     * Constructs a key to uniquely identify a prepared statement.
    307     *
    308     * @param sql The SQL statement.
    309     * @param catalog The catalog.
    310     * @param schema The schema
    311     * @param autoGeneratedKeys A flag indicating whether auto-generated keys should be returned; one of
    312     *        {@link Statement#RETURN_GENERATED_KEYS} or {@link Statement#NO_GENERATED_KEYS}.
    313     * @since 2.5.0
    314     */
    315    public PStmtKey(final String sql, final String catalog, final String schema, final int autoGeneratedKeys) {
    316        this(sql, catalog, schema, StatementType.PREPARED_STATEMENT, autoGeneratedKeys);
    317    }
    318
    319    /**
    320     * Constructs a key to uniquely identify a prepared statement.
    321     *
    322     * @param sql The SQL statement.
    323     * @param catalog The catalog.
    324     * @param schema The schema
    325     * @param resultSetType A result set type; one of {@link ResultSet#TYPE_FORWARD_ONLY},
    326     *        {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or {@link ResultSet#TYPE_SCROLL_SENSITIVE}.
    327     * @param resultSetConcurrency A concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or
    328     *        {@link ResultSet#CONCUR_UPDATABLE}.
    329     */
    330    public PStmtKey(final String sql, final String catalog, final String schema, final int resultSetType, final int resultSetConcurrency) {
    331        this(sql, catalog, schema, resultSetType, resultSetConcurrency, StatementType.PREPARED_STATEMENT);
    332    }
    333
    334    /**
    335     * Constructs a key to uniquely identify a prepared statement.
    336     *
    337     * @param sql The SQL statement.
    338     * @param catalog The catalog.
    339     * @param schema The schema
    340     * @param resultSetType a result set type; one of {@link ResultSet#TYPE_FORWARD_ONLY},
    341     *        {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or {@link ResultSet#TYPE_SCROLL_SENSITIVE}.
    342     * @param resultSetConcurrency A concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or
    343     *        {@link ResultSet#CONCUR_UPDATABLE}
    344     * @param resultSetHoldability One of the following {@link ResultSet} constants:
    345     *        {@link ResultSet#HOLD_CURSORS_OVER_COMMIT} or {@link ResultSet#CLOSE_CURSORS_AT_COMMIT}.
    346     * @since 2.5.0
    347     */
    348    public PStmtKey(final String sql, final String catalog, final String schema, final int resultSetType, final int resultSetConcurrency,
    349        final int resultSetHoldability) {
    350        this(sql, catalog, schema, resultSetType, resultSetConcurrency, resultSetHoldability, StatementType.PREPARED_STATEMENT);
    351    }
    352
    353    /**
    354     * Constructs a key to uniquely identify a prepared statement.
    355     *
    356     * @param sql The SQL statement.
    357     * @param catalog The catalog.
    358     * @param schema The schema.
    359     * @param resultSetType a result set type; one of {@link ResultSet#TYPE_FORWARD_ONLY},
    360     *        {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or {@link ResultSet#TYPE_SCROLL_SENSITIVE}
    361     * @param resultSetConcurrency A concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or
    362     *        {@link ResultSet#CONCUR_UPDATABLE}.
    363     * @param resultSetHoldability One of the following {@link ResultSet} constants:
    364     *        {@link ResultSet#HOLD_CURSORS_OVER_COMMIT} or {@link ResultSet#CLOSE_CURSORS_AT_COMMIT}.
    365     * @param statementType The SQL statement type, prepared or callable.
    366     * @since 2.5.0
    367     */
    368    public PStmtKey(final String sql, final String catalog, final String schema, final int resultSetType, final int resultSetConcurrency,
    369        final int resultSetHoldability, final StatementType statementType) {
    370        this(sql, catalog, schema, resultSetType, resultSetConcurrency, resultSetHoldability, null, null, null, statementType,
    371            k -> match(statementType, StatementHoldability, CallHoldability));
    372    }
    373
    374    /**
    375     * Constructs a key to uniquely identify a prepared statement.
    376     *
    377     * @param sql The SQL statement.
    378     * @param catalog The catalog.
    379     * @param schema The schema.
    380     * @param resultSetType A result set type; one of {@link ResultSet#TYPE_FORWARD_ONLY},
    381     *        {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or {@link ResultSet#TYPE_SCROLL_SENSITIVE}.
    382     * @param resultSetConcurrency A concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or
    383     *        {@link ResultSet#CONCUR_UPDATABLE}.
    384     * @param statementType The SQL statement type, prepared or callable.
    385     * @since 2.5.0
    386     */
    387    public PStmtKey(final String sql, final String catalog, final String schema, final int resultSetType, final int resultSetConcurrency,
    388        final StatementType statementType) {
    389        this(sql, catalog, schema, resultSetType, resultSetConcurrency, null, null, null, null, statementType,
    390            k -> match(statementType, StatementConcurrency, CallConcurrency));
    391    }
    392
    393    /**
    394     * Constructs a key to uniquely identify a prepared statement.
    395     *
    396     * @param sql The SQL statement.
    397     * @param catalog The catalog.
    398     * @param schema The schema.
    399     * @param columnIndexes An array of column indexes indicating the columns that should be returned from the inserted row
    400     *        or rows.
    401     */
    402    public PStmtKey(final String sql, final String catalog, final String schema, final int[] columnIndexes) {
    403        this(sql, catalog, schema, null, null, null, null, columnIndexes, null, StatementType.PREPARED_STATEMENT, StatementColumnIndexes);
    404    }
    405
    406    private PStmtKey(final String sql, final String catalog, final String schema, final Integer resultSetType, final Integer resultSetConcurrency,
    407        final Integer resultSetHoldability, final Integer autoGeneratedKeys, final int[] columnIndexes, final String[] columnNames,
    408        final StatementType statementType, final Function<PStmtKey, StatementBuilder> statementBuilder) {
    409        this.sql = Objects.requireNonNull(sql, "sql").trim();
    410        this.catalog = catalog;
    411        this.schema = schema;
    412        this.resultSetType = resultSetType;
    413        this.resultSetConcurrency = resultSetConcurrency;
    414        this.resultSetHoldability = resultSetHoldability;
    415        this.autoGeneratedKeys = autoGeneratedKeys;
    416        this.columnIndexes = clone(columnIndexes);
    417        this.columnNames = clone(columnNames);
    418        this.statementBuilder = Objects.requireNonNull(Objects.requireNonNull(statementBuilder, "statementBuilder").apply(this), "statementBuilder");
    419        this.statementType = statementType;
    420    }
    421
    422    // Root constructor.
    423    private PStmtKey(final String sql, final String catalog, final String schema, final Integer resultSetType, final Integer resultSetConcurrency,
    424        final Integer resultSetHoldability, final Integer autoGeneratedKeys, final int[] columnIndexes, final String[] columnNames,
    425        final StatementType statementType, final StatementBuilder statementBuilder) {
    426        this.sql = sql;
    427        this.catalog = catalog;
    428        this.schema = schema;
    429        this.resultSetType = resultSetType;
    430        this.resultSetConcurrency = resultSetConcurrency;
    431        this.resultSetHoldability = resultSetHoldability;
    432        this.autoGeneratedKeys = autoGeneratedKeys;
    433        this.columnIndexes = clone(columnIndexes);
    434        this.columnNames = clone(columnNames);
    435        this.statementBuilder = Objects.requireNonNull(statementBuilder, "statementBuilder");
    436        this.statementType = statementType;
    437    }
    438
    439    /**
    440     * Constructs a key to uniquely identify a prepared statement.
    441     *
    442     * @param sql The SQL statement.
    443     * @param catalog The catalog.
    444     * @param schema The schema.
    445     * @param statementType The SQL statement type, prepared or callable.
    446     * @since 2.5.0
    447     */
    448    public PStmtKey(final String sql, final String catalog, final String schema, final StatementType statementType) {
    449        this(sql, catalog, schema, null, null, null, null, null, null, statementType, k -> match(statementType, StatementSQL, CallSQL));
    450    }
    451
    452    /**
    453     * Constructs a key to uniquely identify a prepared statement.
    454     *
    455     * @param sql The SQL statement.
    456     * @param catalog The catalog.
    457     * @param schema The schema.
    458     * @param statementType The SQL statement type, prepared or callable.
    459     * @param autoGeneratedKeys A flag indicating whether auto-generated keys should be returned; one of
    460     *        {@link Statement#RETURN_GENERATED_KEYS} or {@link Statement#NO_GENERATED_KEYS}.
    461     * @since 2.5.0
    462     */
    463    public PStmtKey(final String sql, final String catalog, final String schema, final StatementType statementType, final Integer autoGeneratedKeys) {
    464        this(sql, catalog, schema, null, null, null, autoGeneratedKeys, null, null, statementType,
    465            k -> match(statementType, StatementAutoGeneratedKeys, CallSQL));
    466    }
    467
    468    /**
    469     * Constructs a key to uniquely identify a prepared statement.
    470     *
    471     * @param sql The SQL statement.
    472     * @param catalog The catalog.
    473     * @param schema The schema.
    474     * @param columnNames An array of column names indicating the columns that should be returned from the inserted row or
    475     *        rows.
    476     * @since 2.5.0
    477     */
    478    public PStmtKey(final String sql, final String catalog, final String schema, final String[] columnNames) {
    479        this(sql, catalog, schema, null, null, null, null, null, columnNames, StatementType.PREPARED_STATEMENT, StatementColumnNames);
    480    }
    481
    482    /**
    483     * Constructs a key to uniquely identify a prepared statement.
    484     *
    485     * @param sql The SQL statement.
    486     * @param catalog The catalog.
    487     * @param columnNames An array of column names indicating the columns that should be returned from the inserted row or
    488     *        rows.
    489     * @deprecated Use {@link #PStmtKey(String, String, String, String[])}.
    490     */
    491    @Deprecated
    492    public PStmtKey(final String sql, final String catalog, final String[] columnNames) {
    493        this(sql, catalog, null, null, null, null, null, null, columnNames, StatementType.PREPARED_STATEMENT, StatementColumnNames);
    494    }
    495
    496    private int[] clone(final int[] array) {
    497        return array == null ? null : array.clone();
    498    }
    499
    500    private String[] clone(final String[] array) {
    501        return array == null ? null : array.clone();
    502    }
    503
    504    /**
    505     * Creates a new Statement from the given Connection.
    506     *
    507     * @param connection The Connection to use to create the statement.
    508     * @return The statement.
    509     * @throws SQLException Thrown when there is a problem creating the statement.
    510     */
    511    public Statement createStatement(final Connection connection) throws SQLException {
    512        return statementBuilder.createStatement(connection, this);
    513    }
    514
    515    @Override
    516    public boolean equals(final Object obj) {
    517        if (this == obj) {
    518            return true;
    519        }
    520        if (obj == null || getClass() != obj.getClass()) {
    521            return false;
    522        }
    523        final PStmtKey other = (PStmtKey) obj;
    524        if (!Objects.equals(autoGeneratedKeys, other.autoGeneratedKeys) || !Objects.equals(catalog, other.catalog)
    525                || !Arrays.equals(columnIndexes, other.columnIndexes) || !Arrays.equals(columnNames, other.columnNames)) {
    526            return false;
    527        }
    528        if (!Objects.equals(resultSetConcurrency, other.resultSetConcurrency) || !Objects.equals(resultSetHoldability, other.resultSetHoldability)
    529                || !Objects.equals(resultSetType, other.resultSetType) || !Objects.equals(schema, other.schema)) {
    530            return false;
    531        }
    532        if (!Objects.equals(sql, other.sql)) {
    533            return false;
    534        }
    535        return statementType == other.statementType;
    536    }
    537
    538    /**
    539     * Gets a flag indicating whether auto-generated keys should be returned; one of {@link Statement#RETURN_GENERATED_KEYS}
    540     * or {@link Statement#NO_GENERATED_KEYS}.
    541     *
    542     * @return a flag indicating whether auto-generated keys should be returned.
    543     */
    544    public Integer getAutoGeneratedKeys() {
    545        return autoGeneratedKeys;
    546    }
    547
    548    /**
    549     * Gets the catalog.
    550     *
    551     * @return The catalog.
    552     */
    553    public String getCatalog() {
    554        return catalog;
    555    }
    556
    557    /**
    558     * Gets an array of column indexes indicating the columns that should be returned from the inserted row or rows.
    559     *
    560     * @return An array of column indexes.
    561     */
    562    public int[] getColumnIndexes() {
    563        return clone(columnIndexes);
    564    }
    565
    566    /**
    567     * Gets an array of column names indicating the columns that should be returned from the inserted row or rows.
    568     *
    569     * @return An array of column names.
    570     */
    571    public String[] getColumnNames() {
    572        return clone(columnNames);
    573    }
    574
    575    /**
    576     * Gets the result set concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or
    577     * {@link ResultSet#CONCUR_UPDATABLE}.
    578     *
    579     * @return The result set concurrency type.
    580     */
    581    public Integer getResultSetConcurrency() {
    582        return resultSetConcurrency;
    583    }
    584
    585    /**
    586     * Gets the result set holdability, one of the following {@link ResultSet} constants:
    587     * {@link ResultSet#HOLD_CURSORS_OVER_COMMIT} or {@link ResultSet#CLOSE_CURSORS_AT_COMMIT}.
    588     *
    589     * @return The result set holdability.
    590     */
    591    public Integer getResultSetHoldability() {
    592        return resultSetHoldability;
    593    }
    594
    595    /**
    596     * Gets the result set type, one of {@link ResultSet#TYPE_FORWARD_ONLY}, {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or
    597     * {@link ResultSet#TYPE_SCROLL_SENSITIVE}.
    598     *
    599     * @return the result set type.
    600     */
    601    public Integer getResultSetType() {
    602        return resultSetType;
    603    }
    604
    605    /**
    606     * Gets the schema.
    607     *
    608     * @return The catalog.
    609     */
    610    public String getSchema() {
    611        return schema;
    612    }
    613
    614    /**
    615     * Gets the SQL statement.
    616     *
    617     * @return the SQL statement.
    618     */
    619    public String getSql() {
    620        return sql;
    621    }
    622
    623    /**
    624     * Gets the SQL statement type.
    625     *
    626     * @return The SQL statement type.
    627     */
    628    public StatementType getStmtType() {
    629        return statementType;
    630    }
    631
    632    @Override
    633    public int hashCode() {
    634        return Objects.hash(autoGeneratedKeys, catalog, Arrays.hashCode(columnIndexes), Arrays.hashCode(columnNames), resultSetConcurrency,
    635            resultSetHoldability, resultSetType, schema, sql, statementType);
    636    }
    637
    638    @Override
    639    public String toString() {
    640        final StringBuilder buf = new StringBuilder();
    641        buf.append("PStmtKey: sql=");
    642        buf.append(sql);
    643        buf.append(", catalog=");
    644        buf.append(catalog);
    645        buf.append(", schema=");
    646        buf.append(schema);
    647        buf.append(", resultSetType=");
    648        buf.append(resultSetType);
    649        buf.append(", resultSetConcurrency=");
    650        buf.append(resultSetConcurrency);
    651        buf.append(", resultSetHoldability=");
    652        buf.append(resultSetHoldability);
    653        buf.append(", autoGeneratedKeys=");
    654        buf.append(autoGeneratedKeys);
    655        buf.append(", columnIndexes=");
    656        buf.append(Arrays.toString(columnIndexes));
    657        buf.append(", columnNames=");
    658        buf.append(Arrays.toString(columnNames));
    659        buf.append(", statementType=");
    660        buf.append(statementType);
    661        return buf.toString();
    662    }
    663}