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 *      http://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.SQLException;
021import java.sql.Statement;
022import java.util.Arrays;
023import java.util.Objects;
024
025import org.apache.commons.dbcp2.PoolingConnection.StatementType;
026
027/**
028 * A key uniquely identifying {@link java.sql.PreparedStatement PreparedStatement}s.
029 *
030 * @since 2.0
031 */
032public class PStmtKey {
033
034    /**
035     * Builder for prepareCall(String sql).
036     */
037    private class PreparedCallSQL implements StatementBuilder {
038        @Override
039        public Statement createStatement(final Connection connection) throws SQLException {
040            return connection.prepareCall(sql);
041        }
042    }
043
044    /**
045     * Builder for prepareCall(String sql, int resultSetType, int resultSetConcurrency).
046     */
047    private class PreparedCallWithResultSetConcurrency implements StatementBuilder {
048        @Override
049        public Statement createStatement(final Connection connection) throws SQLException {
050            return connection.prepareCall(sql, resultSetType.intValue(), resultSetConcurrency.intValue());
051        }
052    }
053
054    /**
055     * Builder for prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability).
056     */
057    private class PreparedCallWithResultSetHoldability implements StatementBuilder {
058        @Override
059        public Statement createStatement(final Connection connection) throws SQLException {
060            return connection.prepareCall(sql, resultSetType.intValue(), resultSetConcurrency.intValue(),
061                    resultSetHoldability.intValue());
062        }
063    }
064
065    /**
066     * Builder for prepareStatement(String sql).
067     */
068    private class PreparedStatementSQL implements StatementBuilder {
069        @Override
070        public Statement createStatement(final Connection connection) throws SQLException {
071            return connection.prepareStatement(sql);
072        }
073    }
074
075    /**
076     * Builder for prepareStatement(String sql, int autoGeneratedKeys).
077     */
078    private class PreparedStatementWithAutoGeneratedKeys implements StatementBuilder {
079        @Override
080        public Statement createStatement(final Connection connection) throws SQLException {
081            return connection.prepareStatement(sql, autoGeneratedKeys.intValue());
082        }
083    }
084
085    /**
086     * Builder for prepareStatement(String sql, int[] columnIndexes).
087     */
088    private class PreparedStatementWithColumnIndexes implements StatementBuilder {
089        @Override
090        public Statement createStatement(final Connection connection) throws SQLException {
091            return connection.prepareStatement(sql, columnIndexes);
092        }
093    }
094
095    /**
096     * Builder for prepareStatement(String sql, String[] columnNames).
097     */
098    private class PreparedStatementWithColumnNames implements StatementBuilder {
099        @Override
100        public Statement createStatement(final Connection connection) throws SQLException {
101            return connection.prepareStatement(sql, columnNames);
102        }
103    }
104
105    /**
106     * Builder for prepareStatement(String sql, int resultSetType, int resultSetConcurrency).
107     */
108    private class PreparedStatementWithResultSetConcurrency implements StatementBuilder {
109        @Override
110        public Statement createStatement(final Connection connection) throws SQLException {
111            return connection.prepareStatement(sql, resultSetType.intValue(), resultSetConcurrency.intValue());
112        }
113    }
114
115    /**
116     * Builder for prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability).
117     */
118    private class PreparedStatementWithResultSetHoldability implements StatementBuilder {
119        @Override
120        public Statement createStatement(final Connection connection) throws SQLException {
121            return connection.prepareStatement(sql, resultSetType.intValue(), resultSetConcurrency.intValue(),
122                    resultSetHoldability.intValue());
123        }
124    }
125
126    /**
127     * Interface for Prepared or Callable Statement.
128     */
129    private interface StatementBuilder {
130        Statement createStatement(Connection connection) throws SQLException;
131    }
132
133    /**
134     * SQL defining Prepared or Callable Statement
135     */
136    private final String sql;
137
138    /**
139     * Result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>, <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>,
140     * or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
141     */
142    private final Integer resultSetType;
143
144    /**
145     * Result set concurrency. A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
146     * <code>ResultSet.CONCUR_UPDATABLE</code>.
147     */
148    private final Integer resultSetConcurrency;
149
150    /**
151     * Result set holdability. One of the following <code>ResultSet</code> constants:
152     * <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
153     */
154    private final Integer resultSetHoldability;
155
156    /** Database catalog. */
157    private final String catalog;
158
159    /** Database schema. */
160    private final String schema;
161
162    /**
163     * A flag indicating whether auto-generated keys should be returned; one of
164     * <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
165     */
166    private final Integer autoGeneratedKeys;
167
168    /**
169     * An array of column indexes indicating the columns that should be returned from the inserted row or rows.
170     */
171    private final int[] columnIndexes;
172
173    /**
174     * An array of column names indicating the columns that should be returned from the inserted row or rows.
175     */
176    private final String[] columnNames;
177
178    /**
179     * Statement type, prepared or callable.
180     */
181    private final StatementType statementType;
182
183    /** Statement builder */
184    private transient StatementBuilder builder;
185
186    /**
187     * Constructs a key to uniquely identify a prepared statement.
188     *
189     * @param sql
190     *            The SQL statement.
191     * @deprecated Use {@link #PStmtKey(String, String, String)}.
192     */
193    @Deprecated
194    public PStmtKey(final String sql) {
195        this(sql, null, StatementType.PREPARED_STATEMENT);
196    }
197
198    /**
199     * Constructs a key to uniquely identify a prepared statement.
200     *
201     * @param sql
202     *            The SQL statement.
203     * @param resultSetType
204     *            A result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
205     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
206     * @param resultSetConcurrency
207     *            A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
208     *            <code>ResultSet.CONCUR_UPDATABLE</code>.
209     * @deprecated Use {@link #PStmtKey(String, String, String, int, int)}.
210     */
211    @Deprecated
212    public PStmtKey(final String sql, final int resultSetType, final int resultSetConcurrency) {
213        this(sql, null, resultSetType, resultSetConcurrency, StatementType.PREPARED_STATEMENT);
214    }
215
216    /**
217     * Constructs a key to uniquely identify a prepared statement.
218     *
219     * @param sql
220     *            The SQL statement.
221     * @param catalog
222     *            The catalog.
223     * @deprecated Use {@link #PStmtKey(String, String, String)}.
224     */
225    @Deprecated
226    public PStmtKey(final String sql, final String catalog) {
227        this(sql, catalog, StatementType.PREPARED_STATEMENT);
228    }
229
230    /**
231     * Constructs a key to uniquely identify a prepared statement.
232     *
233     * @param sql
234     *            The SQL statement.
235     * @param catalog
236     *            The catalog.
237     * @param autoGeneratedKeys
238     *            A flag indicating whether auto-generated keys should be returned; one of
239     *            <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
240     * @deprecated Use {@link #PStmtKey(String, String, String, int)}.
241     */
242    @Deprecated
243    public PStmtKey(final String sql, final String catalog, final int autoGeneratedKeys) {
244        this(sql, catalog, StatementType.PREPARED_STATEMENT, Integer.valueOf(autoGeneratedKeys));
245    }
246
247    /**
248     * Constructs a key to uniquely identify a prepared statement.
249     *
250     * @param sql
251     *            The SQL statement.
252     * @param catalog
253     *            The catalog.
254     * @param resultSetType
255     *            A result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
256     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
257     * @param resultSetConcurrency
258     *            A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
259     *            <code>ResultSet.CONCUR_UPDATABLE</code>.
260     * @deprecated Use @link {@link #PStmtKey(String, String, String, int, int)}.
261     */
262    @Deprecated
263    public PStmtKey(final String sql, final String catalog, final int resultSetType, final int resultSetConcurrency) {
264        this(sql, catalog, resultSetType, resultSetConcurrency, StatementType.PREPARED_STATEMENT);
265    }
266
267    /**
268     * Constructs a key to uniquely identify a prepared statement.
269     *
270     * @param sql
271     *            The SQL statement.
272     * @param catalog
273     *            The catalog.
274     * @param resultSetType
275     *            a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
276     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
277     * @param resultSetConcurrency
278     *            A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
279     *            <code>ResultSet.CONCUR_UPDATABLE</code>
280     * @param resultSetHoldability
281     *            One of the following <code>ResultSet</code> constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
282     *            or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
283     * @deprecated Use {@link #PStmtKey(String, String, String, int, int, int)}.
284     */
285    @Deprecated
286    public PStmtKey(final String sql, final String catalog, final int resultSetType, final int resultSetConcurrency,
287            final int resultSetHoldability) {
288        this(sql, catalog, resultSetType, resultSetConcurrency, resultSetHoldability, StatementType.PREPARED_STATEMENT);
289    }
290
291    /**
292     * Constructs a key to uniquely identify a prepared statement.
293     *
294     * @param sql
295     *            The SQL statement.
296     * @param catalog
297     *            The catalog.
298     * @param resultSetType
299     *            a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
300     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>
301     * @param resultSetConcurrency
302     *            A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
303     *            <code>ResultSet.CONCUR_UPDATABLE</code>.
304     * @param resultSetHoldability
305     *            One of the following <code>ResultSet</code> constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
306     *            or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
307     * @param statementType
308     *            The SQL statement type, prepared or callable.
309     * @deprecated Use {@link #PStmtKey(String, String, String, int, int, int, PoolingConnection.StatementType)}
310     */
311    @Deprecated
312    public PStmtKey(final String sql, final String catalog, final int resultSetType, final int resultSetConcurrency,
313            final int resultSetHoldability, final StatementType statementType) {
314        this.sql = sql;
315        this.catalog = catalog;
316        this.schema = null;
317        this.resultSetType = Integer.valueOf(resultSetType);
318        this.resultSetConcurrency = Integer.valueOf(resultSetConcurrency);
319        this.resultSetHoldability = Integer.valueOf(resultSetHoldability);
320        this.statementType = statementType;
321        this.autoGeneratedKeys = null;
322        this.columnIndexes = null;
323        this.columnNames = null;
324        // create builder
325        if (statementType == StatementType.PREPARED_STATEMENT) {
326            this.builder = new PreparedStatementWithResultSetHoldability();
327        } else if (statementType == StatementType.CALLABLE_STATEMENT) {
328            this.builder = new PreparedCallWithResultSetHoldability();
329        }
330    }
331
332    /**
333     * Constructs a key to uniquely identify a prepared statement.
334     *
335     * @param sql
336     *            The SQL statement.
337     * @param catalog
338     *            The catalog.
339     * @param resultSetType
340     *            A result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
341     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
342     * @param resultSetConcurrency
343     *            A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
344     *            <code>ResultSet.CONCUR_UPDATABLE</code>.
345     * @param statementType
346     *            The SQL statement type, prepared or callable.
347     * @deprecated Use {@link #PStmtKey(String, String, String, int, int, PoolingConnection.StatementType)}.
348     */
349    @Deprecated
350    public PStmtKey(final String sql, final String catalog, final int resultSetType, final int resultSetConcurrency,
351            final StatementType statementType) {
352        this.sql = sql;
353        this.catalog = catalog;
354        this.schema = null;
355        this.resultSetType = Integer.valueOf(resultSetType);
356        this.resultSetConcurrency = Integer.valueOf(resultSetConcurrency);
357        this.resultSetHoldability = null;
358        this.statementType = statementType;
359        this.autoGeneratedKeys = null;
360        this.columnIndexes = null;
361        this.columnNames = null;
362        // create builder
363        if (statementType == StatementType.PREPARED_STATEMENT) {
364            this.builder = new PreparedStatementWithResultSetConcurrency();
365        } else if (statementType == StatementType.CALLABLE_STATEMENT) {
366            this.builder = new PreparedCallWithResultSetConcurrency();
367        }
368    }
369
370    /**
371     * Constructs a key to uniquely identify a prepared statement.
372     *
373     * @param sql
374     *            The SQL statement.
375     * @param catalog
376     *            The catalog.
377     * @param columnIndexes
378     *            An array of column indexes indicating the columns that should be returned from the inserted row or
379     *            rows.
380     * @deprecated Use {@link #PStmtKey(String, String, String, int[])}.
381     */
382    @Deprecated
383    public PStmtKey(final String sql, final String catalog, final int[] columnIndexes) {
384        this.sql = sql;
385        this.catalog = catalog;
386        this.schema = null;
387        this.statementType = StatementType.PREPARED_STATEMENT;
388        this.autoGeneratedKeys = null;
389        this.columnIndexes = columnIndexes == null ? null : Arrays.copyOf(columnIndexes, columnIndexes.length);
390        this.columnNames = null;
391        this.resultSetType = null;
392        this.resultSetConcurrency = null;
393        this.resultSetHoldability = null;
394        // create builder
395        this.builder = new PreparedStatementWithColumnIndexes();
396    }
397
398    /**
399     * Constructs a key to uniquely identify a prepared statement.
400     *
401     * @param sql
402     *            The SQL statement.
403     * @param catalog
404     *            The catalog.
405     * @param statementType
406     *            The SQL statement type, prepared or callable.
407     * @deprecated Use {@link #PStmtKey(String, String, String, PoolingConnection.StatementType)}.
408     */
409    @Deprecated
410    public PStmtKey(final String sql, final String catalog, final StatementType statementType) {
411        this.sql = sql;
412        this.catalog = catalog;
413        this.schema = null;
414        this.statementType = statementType;
415        this.autoGeneratedKeys = null;
416        this.columnIndexes = null;
417        this.columnNames = null;
418        this.resultSetType = null;
419        this.resultSetConcurrency = null;
420        this.resultSetHoldability = null;
421        // create builder
422        if (statementType == StatementType.PREPARED_STATEMENT) {
423            this.builder = new PreparedStatementSQL();
424        } else if (statementType == StatementType.CALLABLE_STATEMENT) {
425            this.builder = new PreparedCallSQL();
426        }
427    }
428
429    /**
430     * Constructs a key to uniquely identify a prepared statement.
431     *
432     * @param sql
433     *            The SQL statement.
434     * @param catalog
435     *            The catalog.
436     * @param statementType
437     *            The SQL statement type, prepared or callable.
438     * @param autoGeneratedKeys
439     *            A flag indicating whether auto-generated keys should be returned; one of
440     *            <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
441     * @deprecated Use {@link #PStmtKey(String, String, String, PoolingConnection.StatementType, Integer)}
442     */
443    @Deprecated
444    public PStmtKey(final String sql, final String catalog, final StatementType statementType,
445            final Integer autoGeneratedKeys) {
446        this.sql = sql;
447        this.catalog = catalog;
448        this.schema = null;
449        this.statementType = statementType;
450        this.autoGeneratedKeys = autoGeneratedKeys;
451        this.columnIndexes = null;
452        this.columnNames = null;
453        this.resultSetType = null;
454        this.resultSetConcurrency = null;
455        this.resultSetHoldability = null;
456        // create builder
457        if (statementType == StatementType.PREPARED_STATEMENT) {
458            this.builder = new PreparedStatementWithAutoGeneratedKeys();
459        } else if (statementType == StatementType.CALLABLE_STATEMENT) {
460            this.builder = new PreparedCallSQL();
461        }
462    }
463
464    /**
465     * Constructs a key to uniquely identify a prepared statement.
466     *
467     * @param sql
468     *            The SQL statement.
469     * @param catalog
470     *            The catalog.
471     * @param schema
472     *            The schema
473     * @since 2.5.0
474     */
475    public PStmtKey(final String sql, final String catalog, final String schema) {
476        this(sql, catalog, schema, StatementType.PREPARED_STATEMENT);
477    }
478
479    /**
480     * Constructs a key to uniquely identify a prepared statement.
481     *
482     * @param sql
483     *            The SQL statement.
484     * @param catalog
485     *            The catalog.
486     * @param schema
487     *            The schema
488     * @param autoGeneratedKeys
489     *            A flag indicating whether auto-generated keys should be returned; one of
490     *            <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
491     * @since 2.5.0
492     */
493    public PStmtKey(final String sql, final String catalog, final String schema, final int autoGeneratedKeys) {
494        this(sql, catalog, schema, StatementType.PREPARED_STATEMENT, Integer.valueOf(autoGeneratedKeys));
495    }
496
497    /**
498     * Constructs a key to uniquely identify a prepared statement.
499     *
500     * @param sql
501     *            The SQL statement.
502     * @param catalog
503     *            The catalog.
504     * @param schema
505     *            The schema
506     * @param resultSetType
507     *            A result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
508     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
509     * @param resultSetConcurrency
510     *            A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
511     *            <code>ResultSet.CONCUR_UPDATABLE</code>.
512     */
513    public PStmtKey(final String sql, final String catalog, final String schema, final int resultSetType, final int resultSetConcurrency) {
514        this(sql, catalog, schema, resultSetType, resultSetConcurrency, StatementType.PREPARED_STATEMENT);
515    }
516
517    /**
518     * Constructs a key to uniquely identify a prepared statement.
519     *
520     * @param sql
521     *            The SQL statement.
522     * @param catalog
523     *            The catalog.
524     * @param schema
525     *            The schema
526     * @param resultSetType
527     *            a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
528     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
529     * @param resultSetConcurrency
530     *            A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
531     *            <code>ResultSet.CONCUR_UPDATABLE</code>
532     * @param resultSetHoldability
533     *            One of the following <code>ResultSet</code> constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
534     *            or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
535     * @since 2.5.0
536     */
537    public PStmtKey(final String sql, final String catalog, final String schema, final int resultSetType, final int resultSetConcurrency,
538            final int resultSetHoldability) {
539        this(sql, catalog, schema, resultSetType, resultSetConcurrency, resultSetHoldability, StatementType.PREPARED_STATEMENT);
540    }
541
542    /**
543     * Constructs a key to uniquely identify a prepared statement.
544     *
545     * @param sql
546     *            The SQL statement.
547     * @param catalog
548     *            The catalog.
549     * @param schema
550     *            The schema.
551     * @param resultSetType
552     *            a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
553     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>
554     * @param resultSetConcurrency
555     *            A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
556     *            <code>ResultSet.CONCUR_UPDATABLE</code>.
557     * @param resultSetHoldability
558     *            One of the following <code>ResultSet</code> constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
559     *            or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
560     * @param statementType
561     *            The SQL statement type, prepared or callable.
562     * @since 2.5.0
563     */
564    public PStmtKey(final String sql, final String catalog, final String schema, final int resultSetType, final int resultSetConcurrency,
565            final int resultSetHoldability, final StatementType statementType) {
566        this.sql = sql;
567        this.catalog = catalog;
568        this.schema = schema;
569        this.resultSetType = Integer.valueOf(resultSetType);
570        this.resultSetConcurrency = Integer.valueOf(resultSetConcurrency);
571        this.resultSetHoldability = Integer.valueOf(resultSetHoldability);
572        this.statementType = statementType;
573        this.autoGeneratedKeys = null;
574        this.columnIndexes = null;
575        this.columnNames = null;
576        // create builder
577        if (statementType == StatementType.PREPARED_STATEMENT) {
578            this.builder = new PreparedStatementWithResultSetHoldability();
579        } else if (statementType == StatementType.CALLABLE_STATEMENT) {
580            this.builder = new PreparedCallWithResultSetHoldability();
581        }
582    }
583
584    /**
585     * Constructs a key to uniquely identify a prepared statement.
586     *
587     * @param sql
588     *            The SQL statement.
589     * @param catalog
590     *            The catalog.
591     * @param schema
592     *            The schema.
593     * @param resultSetType
594     *            A result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
595     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
596     * @param resultSetConcurrency
597     *            A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
598     *            <code>ResultSet.CONCUR_UPDATABLE</code>.
599     * @param statementType
600     *            The SQL statement type, prepared or callable.
601     * @since 2.5.0
602     */
603    public PStmtKey(final String sql, final String catalog, final String schema, final int resultSetType, final int resultSetConcurrency,
604            final StatementType statementType) {
605        this.sql = sql;
606        this.catalog = catalog;
607        this.schema = schema;
608        this.resultSetType = Integer.valueOf(resultSetType);
609        this.resultSetConcurrency = Integer.valueOf(resultSetConcurrency);
610        this.resultSetHoldability = null;
611        this.statementType = statementType;
612        this.autoGeneratedKeys = null;
613        this.columnIndexes = null;
614        this.columnNames = null;
615        // create builder
616        if (statementType == StatementType.PREPARED_STATEMENT) {
617            this.builder = new PreparedStatementWithResultSetConcurrency();
618        } else if (statementType == StatementType.CALLABLE_STATEMENT) {
619            this.builder = new PreparedCallWithResultSetConcurrency();
620        }
621    }
622
623    /**
624     * Constructs a key to uniquely identify a prepared statement.
625     *
626     * @param sql
627     *            The SQL statement.
628     * @param catalog
629     *            The catalog.
630     * @param schema
631     *            The schema.
632     * @param columnIndexes
633     *            An array of column indexes indicating the columns that should be returned from the inserted row or
634     *            rows.
635     */
636    public PStmtKey(final String sql, final String catalog, final String schema, final int[] columnIndexes) {
637        this.sql = sql;
638        this.catalog = catalog;
639        this.schema = schema;
640        this.statementType = StatementType.PREPARED_STATEMENT;
641        this.autoGeneratedKeys = null;
642        this.columnIndexes = columnIndexes == null ? null : Arrays.copyOf(columnIndexes, columnIndexes.length);
643        this.columnNames = null;
644        this.resultSetType = null;
645        this.resultSetConcurrency = null;
646        this.resultSetHoldability = null;
647        // create builder
648        this.builder = new PreparedStatementWithColumnIndexes();
649    }
650
651    /**
652     * Constructs a key to uniquely identify a prepared statement.
653     *
654     * @param sql
655     *            The SQL statement.
656     * @param catalog
657     *            The catalog.
658     * @param schema
659     *            The schema.
660     * @param statementType
661     *            The SQL statement type, prepared or callable.
662     * @since 2.5.0
663     */
664    public PStmtKey(final String sql, final String catalog, final String schema, final StatementType statementType) {
665        this.sql = sql;
666        this.catalog = catalog;
667        this.schema = schema;
668        this.statementType = statementType;
669        this.autoGeneratedKeys = null;
670        this.columnIndexes = null;
671        this.columnNames = null;
672        this.resultSetType = null;
673        this.resultSetConcurrency = null;
674        this.resultSetHoldability = null;
675        // create builder
676        if (statementType == StatementType.PREPARED_STATEMENT) {
677            this.builder = new PreparedStatementSQL();
678        } else if (statementType == StatementType.CALLABLE_STATEMENT) {
679            this.builder = new PreparedCallSQL();
680        }
681    }
682
683    /**
684     * Constructs a key to uniquely identify a prepared statement.
685     *
686     * @param sql
687     *            The SQL statement.
688     * @param catalog
689     *            The catalog.
690     * @param schema
691     *            The schema.
692     * @param statementType
693     *            The SQL statement type, prepared or callable.
694     * @param autoGeneratedKeys
695     *            A flag indicating whether auto-generated keys should be returned; one of
696     *            <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
697     * @since 2.5.0
698     */
699    public PStmtKey(final String sql, final String catalog, final String schema, final StatementType statementType,
700            final Integer autoGeneratedKeys) {
701        this.sql = sql;
702        this.catalog = catalog;
703        this.schema = schema;
704        this.statementType = statementType;
705        this.autoGeneratedKeys = autoGeneratedKeys;
706        this.columnIndexes = null;
707        this.columnNames = null;
708        this.resultSetType = null;
709        this.resultSetConcurrency = null;
710        this.resultSetHoldability = null;
711        // create builder
712        if (statementType == StatementType.PREPARED_STATEMENT) {
713            this.builder = new PreparedStatementWithAutoGeneratedKeys();
714        } else if (statementType == StatementType.CALLABLE_STATEMENT) {
715            this.builder = new PreparedCallSQL();
716        }
717    }
718
719    /**
720     * Constructs a key to uniquely identify a prepared statement.
721     *
722     * @param sql
723     *            The SQL statement.
724     * @param catalog
725     *            The catalog.
726     * @param schema
727     *            The schema.
728     * @param columnNames
729     *            An array of column names indicating the columns that should be returned from the inserted row or rows.
730     * @since 2.5.0
731     */
732    public PStmtKey(final String sql, final String catalog, final String schema, final String[] columnNames) {
733        this.sql = sql;
734        this.catalog = catalog;
735        this.schema = schema;
736        this.statementType = StatementType.PREPARED_STATEMENT;
737        this.autoGeneratedKeys = null;
738        this.columnIndexes = null;
739        this.columnNames = columnNames == null ? null : Arrays.copyOf(columnNames, columnNames.length);
740        this.resultSetType = null;
741        this.resultSetConcurrency = null;
742        this.resultSetHoldability = null;
743        // create builder
744        builder = new PreparedStatementWithColumnNames();
745    }
746
747    /**
748     * Constructs a key to uniquely identify a prepared statement.
749     *
750     * @param sql
751     *            The SQL statement.
752     * @param catalog
753     *            The catalog.
754     * @param columnNames
755     *            An array of column names indicating the columns that should be returned from the inserted row or rows.
756     * @deprecated Use {@link #PStmtKey(String, String, String, String[])}.
757     */
758    @Deprecated
759    public PStmtKey(final String sql, final String catalog, final String[] columnNames) {
760        this.sql = sql;
761        this.catalog = catalog;
762        this.schema = null;
763        this.statementType = StatementType.PREPARED_STATEMENT;
764        this.autoGeneratedKeys = null;
765        this.columnIndexes = null;
766        this.columnNames = columnNames == null ? null : Arrays.copyOf(columnNames, columnNames.length);
767        this.resultSetType = null;
768        this.resultSetConcurrency = null;
769        this.resultSetHoldability = null;
770        // create builder
771        builder = new PreparedStatementWithColumnNames();
772    }
773
774    /**
775     * Creates a new Statement from the given Connection.
776     *
777     * @param connection
778     *            The Connection to use to create the statement.
779     * @return The statement.
780     * @throws SQLException
781     *             Thrown when there is a problem creating the statement.
782     */
783    public Statement createStatement(final Connection connection) throws SQLException {
784        if (builder == null) {
785            throw new IllegalStateException("Prepared statement key is invalid.");
786        }
787        return builder.createStatement(connection);
788    }
789
790    @Override
791    public boolean equals(final Object obj) {
792        if (this == obj) {
793            return true;
794        }
795        if (obj == null) {
796            return false;
797        }
798        if (getClass() != obj.getClass()) {
799            return false;
800        }
801        final PStmtKey other = (PStmtKey) obj;
802        if (!Objects.equals(autoGeneratedKeys, other.autoGeneratedKeys)) {
803            return false;
804        }
805        if (!Objects.equals(catalog, other.catalog)) {
806            return false;
807        }
808        if (!Arrays.equals(columnIndexes, other.columnIndexes)) {
809            return false;
810        }
811        if (!Arrays.equals(columnNames, other.columnNames)) {
812            return false;
813        }
814        if (!Objects.equals(resultSetConcurrency, other.resultSetConcurrency)) {
815            return false;
816        }
817        if (!Objects.equals(resultSetHoldability, other.resultSetHoldability)) {
818            return false;
819        }
820        if (!Objects.equals(resultSetType, other.resultSetType)) {
821            return false;
822        }
823        if (!Objects.equals(schema, other.schema)) {
824            return false;
825        }
826        if (!Objects.equals(sql, other.sql)) {
827            return false;
828        }
829        if (statementType != other.statementType) {
830            return false;
831        }
832        return true;
833    }
834
835    /**
836     * Gets a flag indicating whether auto-generated keys should be returned; one of
837     * <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
838     *
839     * @return a flag indicating whether auto-generated keys should be returned.
840     */
841    public Integer getAutoGeneratedKeys() {
842        return autoGeneratedKeys;
843    }
844
845    /**
846     * The catalog.
847     *
848     * @return The catalog.
849     */
850    public String getCatalog() {
851        return catalog;
852    }
853
854    /**
855     * Gets an array of column indexes indicating the columns that should be returned from the inserted row or rows.
856     *
857     * @return An array of column indexes.
858     */
859    public int[] getColumnIndexes() {
860        return columnIndexes;
861    }
862
863    /**
864     * Gets an array of column names indicating the columns that should be returned from the inserted row or rows.
865     *
866     * @return An array of column names.
867     */
868    public String[] getColumnNames() {
869        return columnNames;
870    }
871
872    /**
873     * Gets the result set concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
874     * <code>ResultSet.CONCUR_UPDATABLE</code>.
875     *
876     * @return The result set concurrency type.
877     */
878    public Integer getResultSetConcurrency() {
879        return resultSetConcurrency;
880    }
881
882    /**
883     * Gets the result set holdability, one of the following <code>ResultSet</code> constants:
884     * <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
885     *
886     * @return The result set holdability.
887     */
888    public Integer getResultSetHoldability() {
889        return resultSetHoldability;
890    }
891
892    /**
893     * Gets the result set type, one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
894     * <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
895     *
896     * @return the result set type.
897     */
898    public Integer getResultSetType() {
899        return resultSetType;
900    }
901
902    /**
903     * The schema.
904     *
905     * @return The catalog.
906     */
907    public String getSchema() {
908        return schema;
909    }
910
911    /**
912     * Gets the SQL statement.
913     *
914     * @return the SQL statement.
915     */
916    public String getSql() {
917        return sql;
918    }
919
920    /**
921     * The SQL statement type.
922     *
923     * @return The SQL statement type.
924     */
925    public StatementType getStmtType() {
926        return statementType;
927    }
928
929    @Override
930    public int hashCode() {
931        final int prime = 31;
932        int result = 1;
933        result = prime * result + ((autoGeneratedKeys == null) ? 0 : autoGeneratedKeys.hashCode());
934        result = prime * result + ((catalog == null) ? 0 : catalog.hashCode());
935        result = prime * result + Arrays.hashCode(columnIndexes);
936        result = prime * result + Arrays.hashCode(columnNames);
937        result = prime * result + ((resultSetConcurrency == null) ? 0 : resultSetConcurrency.hashCode());
938        result = prime * result + ((resultSetHoldability == null) ? 0 : resultSetHoldability.hashCode());
939        result = prime * result + ((resultSetType == null) ? 0 : resultSetType.hashCode());
940        result = prime * result + ((schema == null) ? 0 : schema.hashCode());
941        result = prime * result + ((sql == null) ? 0 : sql.hashCode());
942        result = prime * result + ((statementType == null) ? 0 : statementType.hashCode());
943        return result;
944    }
945
946    @Override
947    public String toString() {
948        final StringBuffer buf = new StringBuffer();
949        buf.append("PStmtKey: sql=");
950        buf.append(sql);
951        buf.append(", catalog=");
952        buf.append(catalog);
953        buf.append(", schema=");
954        buf.append(schema);
955        buf.append(", resultSetType=");
956        buf.append(resultSetType);
957        buf.append(", resultSetConcurrency=");
958        buf.append(resultSetConcurrency);
959        buf.append(", resultSetHoldability=");
960        buf.append(resultSetHoldability);
961        buf.append(", autoGeneratedKeys=");
962        buf.append(autoGeneratedKeys);
963        buf.append(", columnIndexes=");
964        buf.append(Arrays.toString(columnIndexes));
965        buf.append(", columnNames=");
966        buf.append(Arrays.toString(columnNames));
967        buf.append(", statementType=");
968        buf.append(statementType);
969        return buf.toString();
970    }
971}