001/*
002 * ============================================================================
003 *  Copyright © 2002-2023 by Thomas Thrien.
004 *  All Rights Reserved.
005 * ============================================================================
006 *  Licensed to the public under the agreements of the GNU Lesser General Public
007 *  License, version 3.0 (the "License"). You may obtain a copy of the License at
008 *
009 *       http://www.gnu.org/licenses/lgpl.html
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, WITHOUT
013 *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
014 *  License for the specific language governing permissions and limitations
015 *  under the License.
016 */
017
018package org.tquadrat.foundation.sql.internal;
019
020import static java.lang.String.format;
021import static java.lang.Thread.currentThread;
022import static java.sql.JDBCType.ARRAY;
023import static java.sql.JDBCType.BIGINT;
024import static java.sql.JDBCType.BLOB;
025import static java.sql.JDBCType.BOOLEAN;
026import static java.sql.JDBCType.CLOB;
027import static java.sql.JDBCType.DATALINK;
028import static java.sql.JDBCType.DATE;
029import static java.sql.JDBCType.DOUBLE;
030import static java.sql.JDBCType.INTEGER;
031import static java.sql.JDBCType.LONGNVARCHAR;
032import static java.sql.JDBCType.LONGVARBINARY;
033import static java.sql.JDBCType.LONGVARCHAR;
034import static java.sql.JDBCType.NCHAR;
035import static java.sql.JDBCType.NCLOB;
036import static java.sql.JDBCType.NUMERIC;
037import static java.sql.JDBCType.REAL;
038import static java.sql.JDBCType.REF;
039import static java.sql.JDBCType.ROWID;
040import static java.sql.JDBCType.SMALLINT;
041import static java.sql.JDBCType.SQLXML;
042import static java.sql.JDBCType.TIME;
043import static java.sql.JDBCType.TIMESTAMP;
044import static java.sql.JDBCType.TINYINT;
045import static java.sql.JDBCType.VARBINARY;
046import static java.sql.JDBCType.VARCHAR;
047import static org.apiguardian.api.API.Status.INTERNAL;
048import static org.apiguardian.api.API.Status.STABLE;
049import static org.tquadrat.foundation.lang.CommonConstants.NULL_STRING;
050import static org.tquadrat.foundation.lang.Objects.isNull;
051import static org.tquadrat.foundation.lang.Objects.nonNull;
052import static org.tquadrat.foundation.lang.Objects.requireNonNullArgument;
053import static org.tquadrat.foundation.lang.Objects.requireNotBlankArgument;
054import static org.tquadrat.foundation.lang.Objects.requireNotEmptyArgument;
055
056import java.io.InputStream;
057import java.io.Reader;
058import java.math.BigDecimal;
059import java.net.URL;
060import java.sql.Array;
061import java.sql.Blob;
062import java.sql.Clob;
063import java.sql.Connection;
064import java.sql.Date;
065import java.sql.NClob;
066import java.sql.PreparedStatement;
067import java.sql.Ref;
068import java.sql.ResultSet;
069import java.sql.ResultSetMetaData;
070import java.sql.RowId;
071import java.sql.SQLException;
072import java.sql.SQLType;
073import java.sql.SQLWarning;
074import java.sql.SQLXML;
075import java.sql.Time;
076import java.sql.Timestamp;
077import java.time.ZoneId;
078import java.util.Calendar;
079import java.util.Collection;
080import java.util.HashMap;
081import java.util.List;
082import java.util.Map;
083import java.util.Set;
084
085import org.apiguardian.api.API;
086import org.tquadrat.foundation.annotation.ClassVersion;
087import org.tquadrat.foundation.lang.Objects;
088import org.tquadrat.foundation.sql.EnhancedPreparedStatement;
089import org.tquadrat.foundation.sql.ParameterMetaData;
090import org.tquadrat.foundation.util.LazyMap;
091
092/**
093 *  The base class for implementations of
094 *  {@link EnhancedPreparedStatement}.
095 *
096 *  @version $Id: EnhancedPreparedStatementBase.java 1100 2024-02-16 23:33:45Z tquadrat $
097 *  @extauthor Thomas Thrien - thomas.thrien@tquadrat.org
098 *  @UMLGraph.link
099 *  @since 0.1.0
100 */
101@SuppressWarnings( "OverlyComplexClass" )
102@ClassVersion( sourceVersion = "$Id: EnhancedPreparedStatementBase.java 1100 2024-02-16 23:33:45Z tquadrat $" )
103@API( status = STABLE, since = "0.1.0" )
104public abstract sealed class EnhancedPreparedStatementBase implements EnhancedPreparedStatement
105    permits EnhancedPreparedStatementImpl
106{
107        /*---------------*\
108    ====** Inner Classes **====================================================
109        \*---------------*/
110    /**
111     *  The base class for a variant of
112     *  {@link java.sql.ParameterMetaData}
113     *  that is used by
114     *  {@link org.tquadrat.foundation.sql.EnhancedPreparedStatement}.
115     *
116     *  @version $Id: EnhancedPreparedStatementBase.java 1100 2024-02-16 23:33:45Z tquadrat $
117     *  @extauthor Thomas Thrien - thomas.thrien@tquadrat.org
118     *  @UMLGraph.link
119     *  @since 0.1.0
120     */
121    @SuppressWarnings( "ProtectedInnerClass" )
122    @ClassVersion( sourceVersion = "$Id: EnhancedPreparedStatementBase.java 1100 2024-02-16 23:33:45Z tquadrat $" )
123    @API( status = INTERNAL, since = "0.1.0" )
124    protected final class ParameterMetaDataImpl extends ParameterMetaDataBase
125    {
126            /*--------------*\
127        ====** Constructors **=================================================
128            \*--------------*/
129        /**
130         *  Creates a new instance of {@code ParameterMetaDataImpl}.
131         *
132         *  @param metaData The wrapped metadata instance.
133         */
134        public ParameterMetaDataImpl( final java.sql.ParameterMetaData metaData )
135        {
136            super( metaData );
137        }   //  ParameterMetaDataImpl()
138
139            /*---------*\
140        ====** Methods **======================================================
141            \*---------*/
142        /**
143         *  {@inheritDoc}
144         */
145        @Override
146        public final int[] getParameterIndexes( final String parameterName ) throws SQLException
147        {
148            final var retValue = EnhancedPreparedStatementBase.this.getParameterIndexes( parameterName );
149
150            //---* Done *----------------------------------------------------------
151            return retValue;
152        }   //  getParameterIndexes()
153
154        /**
155         *  {@inheritDoc}
156         */
157        @Override
158        public final Collection<String> getParameterNames()
159        {
160            final var retValue = EnhancedPreparedStatementBase.this.getParameterNames();
161
162            //---* Done *----------------------------------------------------------
163            return retValue;
164        }   //  getParameterNames()
165    }
166    //  class ParameterMetaDataImpl
167
168        /*-----------*\
169    ====** Constants **========================================================
170        \*-----------*/
171
172        /*------------*\
173    ====** Attributes **=======================================================
174        \*------------*/
175    /**
176     *  The counter for the added batch commands.
177     */
178    private int m_BatchCounter = 0;
179
180    /**
181     *  <p>{@summary The parameter index.}</p>
182     *  <p>The parameter names are the keys for the map, the values are the
183     *  indexes.</p>
184     */
185    private final Map<String,int []> m_ParameterIndex = new HashMap<>();
186
187    /**
188     *  The wrapped
189     *  {@link java.sql.PreparedStatement}.
190     */
191    private final PreparedStatement m_PreparedStatement;
192
193    /**
194     *  The statement source.
195     */
196    private final String m_SourceStatement;
197
198    /**
199     *  The values.
200     */
201    private final LazyMap<String,StatementValue> m_Values = LazyMap.use( HashMap::new );
202
203        /*------------------------*\
204    ====** Static Initialisations **===========================================
205        \*------------------------*/
206
207        /*--------------*\
208    ====** Constructors **=====================================================
209        \*--------------*/
210    /**
211     *  Creates a new instance of {@code EnhancedPreparedStatementBase}.
212     *
213     *  @param  sourceStatement The original SQL statement with the
214     *      placeholders; mainly used for logging purposes.
215     *  @param  preparedStatement   The wrapped instance of
216     *      {@link PreparedStatement}.
217     *  @param  parameterIndex  The mapping for the named placeholders to the
218     *      position based placeholders.
219     */
220    protected EnhancedPreparedStatementBase( final String sourceStatement, final PreparedStatement preparedStatement, final Map<String,int[]> parameterIndex )
221    {
222        m_SourceStatement = requireNotEmptyArgument( sourceStatement, "sourceStatement" );
223        m_PreparedStatement = requireNonNullArgument( preparedStatement, "preparedStatement" );
224        m_ParameterIndex.putAll( requireNonNullArgument( parameterIndex, "parameterIndex" ) );
225    }   //  EnhancedPreparedStatementBase()
226
227        /*---------*\
228    ====** Methods **==========================================================
229        \*---------*/
230    /**
231     *  {@inheritDoc}
232     */
233    @Override
234    public final void addBatch() throws SQLException
235    {
236        if( isLoggingEnabled() ) doLogging( format( "addBatch() #%d", m_BatchCounter ), addStacktrace() ? currentThread().getStackTrace() : null );
237        m_PreparedStatement.addBatch();
238        ++m_BatchCounter;
239    }   //  addBatch()
240
241    /**
242     *  Returns a flag that indicates whether a stacktrace should be added to
243     *  the logging.
244     *
245     *  @return {@code true} if the stacktrace should be created and added,
246     *      {@code false} otherwise.
247     */
248    @SuppressWarnings( "BooleanMethodNameMustStartWithQuestion" )
249    protected abstract boolean addStacktrace();
250
251    /**
252     *  {@inheritDoc}
253     */
254    @Override
255    public final void cancel() throws SQLException
256    {
257        m_PreparedStatement.cancel();
258        if( isLoggingEnabled() ) doLogging( "cancel()", addStacktrace() ? currentThread().getStackTrace() : null );
259    }   //  cancel()
260
261    /**
262     *  {@inheritDoc}
263     */
264    @Override
265    public final void clearBatch() throws SQLException
266    {
267        m_PreparedStatement.clearBatch();
268        m_BatchCounter = 0;
269    }   //  clearBatch()
270
271    /**
272     *  {@inheritDoc}
273     */
274    @Override
275    public void clearParameters() throws SQLException
276    {
277        m_PreparedStatement.clearParameters();
278        m_Values.clear();
279    }   //  clearParameters()
280
281    /**
282     *  {@inheritDoc}
283     */
284    @Override
285    public final void clearWarnings() throws SQLException { m_PreparedStatement.clearWarnings(); }
286
287    /**
288     *  {@inheritDoc}
289     */
290    @Override
291    public final void close() throws SQLException { m_PreparedStatement.close(); }
292
293    @Override
294    public final void closeOnCompletion() throws SQLException { m_PreparedStatement.closeOnCompletion(); }
295
296    /**
297     *  Composes the log information and sends it.
298     *
299     *  @param  operation   The operation that logs.
300     *  @param  stackTrace  The stack trace; can be {@code null}.
301     */
302    @SuppressWarnings( "MethodCanBeVariableArityMethod" )
303    protected abstract void doLogging( final String operation, final StackTraceElement [] stackTrace );
304
305    /**
306     *  {@inheritDoc}
307     */
308    @Override
309    public final String enquoteIdentifier( final String identifier, final boolean alwaysQuote ) throws SQLException
310    {
311        final var retValue = m_PreparedStatement.enquoteIdentifier( identifier, alwaysQuote );
312
313        //---* Done *----------------------------------------------------------
314        return retValue;
315    }   //  enquoteIdentifier()
316
317    /**
318     *  {@inheritDoc}
319     */
320    @Override
321    public final String enquoteLiteral( final String value ) throws SQLException { return m_PreparedStatement.enquoteLiteral( value ); }
322
323    /**
324     *  {@inheritDoc}
325     */
326    @Override
327    public final String enquoteNCharLiteral( final String s ) throws SQLException
328    {
329        final var retValue = m_PreparedStatement.enquoteNCharLiteral( s );
330
331        //---* Done *----------------------------------------------------------
332        return retValue;
333    }   //  enquoteNCharLiteral()
334
335    /**
336     *  {@inheritDoc}
337     */
338    @Override
339    public final boolean execute() throws SQLException
340    {
341        if( isLoggingEnabled() ) doLogging( "execute()", addStacktrace() ? currentThread().getStackTrace() : null );
342        final var retValue = m_PreparedStatement.execute();
343
344        //---* Done *----------------------------------------------------------
345        return retValue;
346    }   //  execute()
347
348    /**
349     *  {@inheritDoc}
350     */
351    @Override
352    public final int[] executeBatch() throws SQLException
353    {
354        final var retValue = m_PreparedStatement.executeBatch();
355        m_BatchCounter = 0;
356
357        //---* Done *----------------------------------------------------------
358        return retValue;
359    }   //  executeBatch()
360
361    @Override
362    public long[] executeLargeBatch() throws SQLException
363    {
364        final var retValue = m_PreparedStatement.executeLargeBatch();
365        m_BatchCounter = 0;
366
367        //---* Done *----------------------------------------------------------
368        return retValue;
369    }   //  executeLargeBatch()
370
371    /**
372     *  {@inheritDoc}
373     */
374    @Override
375    public final long executeLargeUpdate() throws SQLException
376    {
377        if( isLoggingEnabled() ) doLogging( "executeLargeUpdate()", addStacktrace() ? currentThread().getStackTrace() : null );
378        final var retValue = m_PreparedStatement.executeLargeUpdate();
379
380        //---* Done *----------------------------------------------------------
381        return retValue;
382    }   //  executeLargeUpdate()
383
384    /**
385     *  {@inheritDoc}
386     */
387    @Override
388    public final ResultSet executeQuery() throws SQLException
389    {
390        if( isLoggingEnabled() ) doLogging( "executeQuery()", addStacktrace() ? currentThread().getStackTrace() : null );
391        final var retValue = m_PreparedStatement.executeQuery();
392
393        //---* Done *----------------------------------------------------------
394        return retValue;
395    }   //  executeQuery()
396
397    /**
398     *  {@inheritDoc}
399     */
400    @Override
401    public final int executeUpdate() throws SQLException
402    {
403        if( isLoggingEnabled() ) doLogging( "executeUpdate()", addStacktrace() ? currentThread().getStackTrace() : null );
404        final var retValue = m_PreparedStatement.executeUpdate();
405
406        //---* Done *----------------------------------------------------------
407        return retValue;
408    }   //  executeUpdate()
409
410    /**
411     *  Provides the current values for logging purposes.
412     *
413     *  @return The current values.
414     */
415    protected final Collection<StatementValue> getCurrentValues() { return m_Values.isPresent() ? List.of() : m_Values.values(); }
416
417    /**
418     *  {@inheritDoc}
419     */
420    @Override
421    public final Connection getConnection() throws SQLException { return m_PreparedStatement.getConnection(); }
422
423    /**
424     *  {@inheritDoc}
425     */
426    @Override
427    public final int getFetchDirection() throws SQLException { return m_PreparedStatement.getFetchDirection(); }
428
429    /**
430     *  {@inheritDoc}
431     */
432    @Override
433    public final int getFetchSize() throws SQLException { return m_PreparedStatement.getFetchSize(); }
434
435    /**
436     *  {@inheritDoc}
437     */
438    @Override
439    public final ResultSet getGeneratedKeys() throws SQLException { return m_PreparedStatement.getGeneratedKeys(); }
440
441    /**
442     *  {@inheritDoc}
443     */
444    @Override
445    public final long getLargeMaxRows() throws SQLException { return m_PreparedStatement.getLargeMaxRows(); }
446
447    /**
448     *  {@inheritDoc}
449     */
450    @Override
451    public final long getLargeUpdateCount() throws SQLException { return m_PreparedStatement.getLargeUpdateCount(); }
452
453    /**
454     *  {@inheritDoc}
455     */
456    @Override
457    public final int getMaxFieldSize() throws SQLException { return m_PreparedStatement.getMaxFieldSize(); }
458
459    /**
460     *  {@inheritDoc}
461     */
462    @Override
463    public final int getMaxRows() throws SQLException { return m_PreparedStatement.getMaxRows(); }
464
465    /**
466     *  {@inheritDoc}
467     */
468    @Override
469    public final ResultSetMetaData getMetaData() throws SQLException { return m_PreparedStatement.getMetaData(); }
470
471    /**
472     *  {@inheritDoc}
473     */
474    @Override
475    public final boolean getMoreResults() throws SQLException { return m_PreparedStatement.getMoreResults(); }
476
477    /**
478     *  {@inheritDoc}
479     */
480    @Override
481    public final boolean getMoreResults( final int current ) throws SQLException { return m_PreparedStatement.getMoreResults( current ); }
482
483    /**
484     *  Returns the parameter indexes for the given parameter name.
485     *
486     *  @param  parameterName   The name of the parameter, prefixed by a colon.
487     *  @return The parameter indexes for this parameter name.
488     *  @throws SQLException    The given parameter name is not defined.
489     */
490    protected final int [] getParameterIndexes( final String parameterName ) throws SQLException
491    {
492        final var retValue = m_ParameterIndex.get( requireNotBlankArgument( parameterName, "parameterName" ) );
493        if( isNull( retValue ) ) throw new SQLException( "Parameter name '%1$s' unknown".formatted( parameterName ) );
494
495        //---* Done *----------------------------------------------------------
496        return retValue;
497    }   //  getParameterIndexes()
498
499    @Override
500    public ParameterMetaData getParameterMetaData() throws SQLException
501    {
502        final ParameterMetaData retValue = new ParameterMetaDataImpl( m_PreparedStatement.getParameterMetaData() );
503
504        //---* Done *----------------------------------------------------------
505        return retValue;
506    }   //  getParameterMetaData()
507
508    /**
509     *  Returns the parameter names for this {@code EnhancedPreparedStatement}.
510     *
511     *  @return The parameter names.
512     */
513    protected final Collection<String> getParameterNames()
514    {
515        final var retValue = Set.copyOf( m_ParameterIndex.keySet() );
516
517        //---* Done *----------------------------------------------------------
518        return retValue;
519    }   //  getParameterNames()
520
521    /**
522     *  {@inheritDoc}
523     */
524    @Override
525    public final int getQueryTimeout() throws SQLException { return m_PreparedStatement.getQueryTimeout(); }
526
527    /**
528     *  {@inheritDoc}
529     */
530    @Override
531    public final ResultSet getResultSet() throws SQLException { return m_PreparedStatement.getResultSet(); }
532
533    /**
534     *  {@inheritDoc}
535     */
536    @Override
537    public final int getResultSetConcurrency() throws SQLException { return m_PreparedStatement.getResultSetConcurrency(); }
538
539    /**
540     *  {@inheritDoc}
541     */
542    @Override
543    public final int getResultSetHoldability() throws SQLException { return m_PreparedStatement.getResultSetHoldability(); }
544
545    /**
546     *  {@inheritDoc}
547     */
548    @Override
549    public final int getResultSetType() throws SQLException { return m_PreparedStatement.getResultSetType(); }
550
551    /**
552     *  Provides the source statement for logging purposes.
553     *
554     *  @return The source statement.
555     */
556    protected final String getSourceStatement() { return m_SourceStatement; }
557
558    /**
559     *  {@inheritDoc}
560     */
561    @Override
562    public final int getUpdateCount() throws SQLException { return m_PreparedStatement.getUpdateCount(); }
563
564    /**
565     *  {@inheritDoc}
566     */
567    @Override
568    public final SQLWarning getWarnings() throws SQLException { return m_PreparedStatement.getWarnings(); }
569
570    /**
571     *  {@inheritDoc}
572     */
573    @Override
574    public final boolean isClosed() throws SQLException { return m_PreparedStatement.isClosed(); }
575
576    /**
577     *  {@inheritDoc}
578     */
579    @Override
580    public final boolean isCloseOnCompletion() throws SQLException { return m_PreparedStatement.isCloseOnCompletion(); }
581
582    /**
583     *  {@inheritDoc}
584     */
585    @Override
586    public abstract boolean isLoggingEnabled();
587
588    /**
589     *  {@inheritDoc}
590     */
591    @Override
592    public final boolean isPoolable() throws SQLException { return m_PreparedStatement.isPoolable(); }
593
594    /**
595     *  {@inheritDoc}
596     */
597    @Override
598    public final boolean isSimpleIdentifier( final String identifier ) throws SQLException
599    {
600        final var retValue = m_PreparedStatement.isSimpleIdentifier( requireNotEmptyArgument( identifier, "identifier" ) );
601
602        //---* Done *----------------------------------------------------------
603        return retValue;
604    }   //  isSimpleIdentifier()
605
606    /**
607     *  {@inheritDoc}
608     */
609    @Override
610    public final void setArray( final String parameterName, final Array value ) throws SQLException
611    {
612        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setArray( index, value );
613        if( isLoggingEnabled() )
614        {
615            m_Values.put( parameterName, new StatementValue( parameterName, ARRAY, Objects.toString( value ) ) );
616        }
617    }   //  setArray()
618
619    /**
620     *  {@inheritDoc}
621     */
622    @Override
623    public void setAsciiStream( final String parameterName, final InputStream value, final int length ) throws SQLException
624    {
625        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setAsciiStream( index, value, length );
626        if( isLoggingEnabled() )
627        {
628            final var valueText = isNull( value ) ? NULL_STRING : format( "%1$s (%2$d)", value.getClass().getName(), length );
629            m_Values.put( parameterName, new StatementValue( parameterName, LONGVARCHAR, valueText ) );
630        }
631    }   //  setAsciiStream()
632
633    /**
634     *  {@inheritDoc}
635     */
636    @Override
637    public void setAsciiStream( final String parameterName, final InputStream value, final long length ) throws SQLException
638    {
639        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setAsciiStream( index, value, length );
640        if( isLoggingEnabled() )
641        {
642            final var valueText = isNull( value ) ? NULL_STRING : format( "%1$s (%2$d)", value.getClass().getName(), length );
643            m_Values.put( parameterName, new StatementValue( parameterName, LONGVARCHAR, valueText ) );
644        }
645    }   //  setAsciiStream()
646
647    /**
648     *  {@inheritDoc}
649     */
650    @Override
651    public void setAsciiStream( final String parameterName, final InputStream value ) throws SQLException
652    {
653        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setAsciiStream( index, value );
654        if( isLoggingEnabled() )
655        {
656            final var valueText = isNull( value ) ? NULL_STRING : value.getClass().getName();
657            m_Values.put( parameterName, new StatementValue( parameterName, LONGVARCHAR, valueText ) );
658        }
659    }   //  setAsciiStream()
660
661    /**
662     *  {@inheritDoc}
663     */
664    @Override
665    public void setBinaryStream( final String parameterName, final InputStream value, final int length ) throws SQLException
666    {
667        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setBinaryStream( index, value, length );
668        if( isLoggingEnabled() )
669        {
670            final var valueText = isNull( value ) ? NULL_STRING : format( "%1$s (%2$d)", value.getClass().getName(), length );
671            m_Values.put( parameterName, new StatementValue( parameterName, LONGVARBINARY, valueText ) );
672        }
673    }   //  setBinaryStream()
674
675    /**
676     *  {@inheritDoc}
677     */
678    @Override
679    public void setBinaryStream( final String parameterName, final InputStream value, final long length ) throws SQLException
680    {
681        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setBinaryStream( index, value, length );
682        if( isLoggingEnabled() )
683        {
684            final var valueText = isNull( value ) ? NULL_STRING : format( "%1$s (%2$d)", value.getClass().getName(), length );
685            m_Values.put( parameterName, new StatementValue( parameterName, LONGVARBINARY, valueText ) );
686        }
687    }   //  setBinaryStream()
688
689    /**
690     *  {@inheritDoc}
691     */
692    @Override
693    public void setBinaryStream( final String parameterName, final InputStream value ) throws SQLException
694    {
695        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setBinaryStream( index, value );
696        if( isLoggingEnabled() )
697        {
698            final var valueText = isNull( value ) ? NULL_STRING : value.getClass().getName();
699            m_Values.put( parameterName, new StatementValue( parameterName, LONGVARBINARY, valueText ) );
700        }
701    }   //  setBinaryStream()
702
703    /**
704     *  {@inheritDoc}
705     */
706    @SuppressWarnings( "CallToNumericToString" )
707    @Override
708    public final void setBigDecimal( final String parameterName, final BigDecimal value ) throws SQLException
709    {
710        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setBigDecimal( index, value );
711        if( isLoggingEnabled() )
712        {
713            m_Values.put( parameterName, new StatementValue( parameterName, NUMERIC, value.toString() ) );
714        }
715    }   //  setBigDecimal()
716
717    /**
718     *  {@inheritDoc}
719     */
720    @Override
721    public final void setBlob( final String parameterName, final Blob value ) throws SQLException
722    {
723        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setBlob( index, value );
724        if( isLoggingEnabled() )
725        {
726            final var valueText = isNull( value ) ? NULL_STRING : value.getClass().getName();
727            m_Values.put( parameterName, new StatementValue( parameterName, BLOB, valueText ) );
728        }
729    }   //  setBlob()
730
731    /**
732     *  {@inheritDoc}
733     */
734    @Override
735    public final void setBlob( final String parameterName, final InputStream value, final long length ) throws SQLException
736    {
737        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setBlob( index, value, length );
738        if( isLoggingEnabled() )
739        {
740            final var valueText = isNull( value ) ? NULL_STRING : format( "%1$s (%2$d)", value.getClass().getName(), length );
741            m_Values.put( parameterName, new StatementValue( parameterName, BLOB, valueText ) );
742        }
743    }   //  setBlob()
744
745    /**
746     *  {@inheritDoc}
747     */
748    @Override
749    public final void setBlob( final String parameterName, final InputStream value ) throws SQLException
750    {
751        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setBlob( index, value );
752        if( isLoggingEnabled() )
753        {
754            final var valueText = isNull( value ) ? NULL_STRING : value.getClass().getName();
755            m_Values.put( parameterName, new StatementValue( parameterName, BLOB, valueText ) );
756        }
757    }   //  setBlob()
758
759    /**
760     *  {@inheritDoc}
761     */
762    @Override
763    public final void setBoolean( final String parameterName, final boolean value ) throws SQLException
764    {
765        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setBoolean( index, value );
766        if( isLoggingEnabled() )
767        {
768            m_Values.put( parameterName, new StatementValue( parameterName, BOOLEAN, value ? "true" : "false" ) );
769        }
770    }   //  setBoolean()
771
772    /**
773     *  {@inheritDoc}
774     */
775    @Override
776    public final void setByte( final String parameterName, final byte value ) throws SQLException
777    {
778        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setByte( index, value );
779        if( isLoggingEnabled() )
780        {
781            m_Values.put( parameterName, new StatementValue( parameterName, TINYINT, Byte.toString( value ) ) );
782        }
783    }   //  setByte()
784
785    /**
786     *  {@inheritDoc}
787     */
788    @Override
789    public final void setBytes( final String parameterName, final byte[] value ) throws SQLException
790    {
791        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setBytes( index, value );
792        if( isLoggingEnabled() )
793        {
794            final var valueText = isNull( value ) ? NULL_STRING : format( "%1$s (%2$d)", value.getClass().getName(), value.length );
795            m_Values.put( parameterName, new StatementValue( parameterName, VARBINARY, valueText ) );
796        }
797    }   //  setBytes()
798
799    /**
800     *  {@inheritDoc}
801     */
802    @Override
803    public final void setCharacterStream( final String parameterName, final Reader value, final int length ) throws SQLException
804    {
805        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setCharacterStream( index, value, length );
806        if( isLoggingEnabled() )
807        {
808            final var valueText = isNull( value ) ? NULL_STRING : format( "%1$s (%2$d)", value.getClass().getName(), length );
809            m_Values.put( parameterName, new StatementValue( parameterName, LONGVARCHAR, valueText ) );
810        }
811    }   //  setCharacterStream()
812
813    /**
814     *  {@inheritDoc}
815     */
816    @Override
817    public final void setCharacterStream( final String parameterName, final Reader value, final long length ) throws SQLException
818    {
819        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setCharacterStream( index, value, length );
820        if( isLoggingEnabled() )
821        {
822            final var valueText = isNull( value ) ? NULL_STRING : format( "%1$s (%2$d)", value.getClass().getName(), length );
823            m_Values.put( parameterName, new StatementValue( parameterName, LONGVARCHAR, valueText ) );
824        }
825    }   //  setCharacterStream()
826
827    /**
828     *  {@inheritDoc}
829     */
830    @Override
831    public final void setCharacterStream( final String parameterName, final Reader value ) throws SQLException
832    {
833        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setCharacterStream( index, value );
834        if( isLoggingEnabled() )
835        {
836            final var valueText = isNull( value ) ? NULL_STRING : value.getClass().getName();
837            m_Values.put( parameterName, new StatementValue( parameterName, LONGVARCHAR, valueText ) );
838        }
839    }   //  setCharacterStream()
840
841    /**
842     *  {@inheritDoc}
843     */
844    @Override
845    public final void setClob( final String parameterName, final Clob value ) throws SQLException
846    {
847        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setClob( index, value );
848        if( isLoggingEnabled() )
849        {
850            final var valueText = isNull( value ) ? NULL_STRING : value.getClass().getName();
851            m_Values.put( parameterName, new StatementValue( parameterName, CLOB, valueText ) );
852        }
853    }   //  setClob()
854
855    /**
856     *  {@inheritDoc}
857     */
858    @Override
859    public final void setClob( final String parameterName, final Reader value, final long length ) throws SQLException
860    {
861        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setClob( index, value, length );
862        if( isLoggingEnabled() )
863        {
864            final var valueText = isNull( value ) ? NULL_STRING : format( "%1$s (%2$d)", value.getClass().getName(), length );
865            m_Values.put( parameterName, new StatementValue( parameterName, CLOB, valueText ) );
866        }
867    }   //  setClob()
868
869    /**
870     *  {@inheritDoc}
871     */
872    @Override
873    public final void setCursorName( final String name ) throws SQLException { m_PreparedStatement.setCursorName( name ); }
874
875    /**
876     *  {@inheritDoc}
877     */
878    @Override
879    public final void setClob( final String parameterName, final Reader value ) throws SQLException
880    {
881        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setClob( index, value );
882        if( isLoggingEnabled() )
883        {
884            final var valueText = isNull( value ) ? NULL_STRING : value.getClass().getName();
885            m_Values.put( parameterName, new StatementValue( parameterName, CLOB, valueText ) );
886        }
887    }   //  setClob()
888
889    /**
890     *  {@inheritDoc}
891     */
892    @Override
893    public final void setDate( final String parameterName, final Date value ) throws SQLException
894    {
895        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setDate( index, value );
896        if( isLoggingEnabled() )
897        {
898            var valueText = NULL_STRING;
899            if( nonNull( value ) )
900            {
901                final var instant = value.toInstant();
902                valueText = format( "%2$s (%1$s)", instant, instant.atZone( ZoneId.systemDefault() ).toLocalDate() );
903            }
904            m_Values.put( parameterName, new StatementValue( parameterName, DATE, valueText ) );
905        }
906    }   //  setDate()
907
908    /**
909     *  {@inheritDoc}
910     */
911    @SuppressWarnings( "UseOfObsoleteDateTimeApi" )
912    @Override
913    public void setDate( final String parameterName, final Date value, final Calendar calendar ) throws SQLException
914    {
915        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setDate( index, value, calendar );
916        if( isLoggingEnabled() )
917        {
918            var valueText = NULL_STRING;
919            if( nonNull( value ) )
920            {
921                final var instant = value.toInstant();
922                final var timezone = isNull( calendar ) ? ZoneId.systemDefault() : calendar.getTimeZone().toZoneId();
923                valueText = format( "%2$s (%1$s)", instant, instant.atZone( timezone ).toLocalDate() );
924            }
925            m_Values.put( parameterName, new StatementValue( parameterName, DATE, valueText ) );
926        }
927    }   //  setDate()
928
929    /**
930     *  {@inheritDoc}
931     */
932    @Override
933    public final void setDouble( final String parameterName, final double value ) throws SQLException
934    {
935        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setDouble( index, value );
936        if( isLoggingEnabled() )
937        {
938            m_Values.put( parameterName, new StatementValue( parameterName, DOUBLE, Double.toString( value ) ) );
939        }
940    }   //  setDouble()
941
942    /**
943     *  {@inheritDoc}
944     */
945    @Override
946    public final void setFetchDirection( final int direction ) throws SQLException { m_PreparedStatement.setFetchDirection( direction ); }
947
948    /**
949     *  {@inheritDoc}
950     */
951    @Override
952    public final void setFetchSize( final int rows ) throws SQLException { m_PreparedStatement.setFetchSize( rows ); }
953
954    /**
955     *  {@inheritDoc}
956     */
957    @Override
958    public final void setFloat( final String parameterName, final float value ) throws SQLException
959    {
960        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setFloat( index, value );
961        if( isLoggingEnabled() )
962        {
963            m_Values.put( parameterName, new StatementValue( parameterName, REAL, Float.toString( value ) ) );
964        }
965    }   //  setFloat()
966
967    /**
968     *  {@inheritDoc}
969     */
970    @Override
971    public final void setInt( final String parameterName, final int value ) throws SQLException
972    {
973        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setInt( index, value );
974        if( isLoggingEnabled() )
975        {
976            m_Values.put( parameterName, new StatementValue( parameterName, INTEGER, Integer.toString( value ) ) );
977        }
978    }   //  setInt()
979
980    /**
981     *  {@inheritDoc}
982     */
983    @Override
984    public final void setLargeMaxRows( final long max ) throws SQLException { m_PreparedStatement.setLargeMaxRows( max ); }
985
986    /**
987     *  {@inheritDoc}
988     */
989    @Override
990    public final void setLong( final String parameterName, final long value ) throws SQLException
991    {
992        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setLong( index, value );
993        if( isLoggingEnabled() )
994        {
995            m_Values.put( parameterName, new StatementValue( parameterName, BIGINT, Long.toString( value ) ) );
996        }
997    }   //  setLong()
998
999    /**
1000     *  {@inheritDoc}
1001     */
1002    @Override
1003    public final void setMaxFieldSize( final int max ) throws SQLException { m_PreparedStatement.setMaxFieldSize( max ); }
1004
1005    /**
1006     *  {@inheritDoc}
1007     */
1008    @Override
1009    public final void setMaxRows( final int max ) throws SQLException { m_PreparedStatement.setMaxRows( max ); }
1010
1011    /**
1012     *  {@inheritDoc}
1013     */
1014    @Override
1015    public final void setNCharacterStream( final String parameterName, final Reader value, final long length ) throws SQLException
1016    {
1017        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setNCharacterStream( index, value, length );
1018        if( isLoggingEnabled() )
1019        {
1020            final var valueText = isNull( value ) ? NULL_STRING : format( "%1$s (%2$d)", value.getClass().getName(), length );
1021            m_Values.put( parameterName, new StatementValue( parameterName, LONGNVARCHAR, valueText ) );
1022        }
1023    }   //  setNCharacterStream()
1024
1025    /**
1026     *  {@inheritDoc}
1027     */
1028    @Override
1029    public final void setNCharacterStream( final String parameterName, final Reader value ) throws SQLException
1030    {
1031        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setNCharacterStream( index, value );
1032        if( isLoggingEnabled() )
1033        {
1034            final var valueText = isNull( value ) ? NULL_STRING : value.getClass().getName();
1035            m_Values.put( parameterName, new StatementValue( parameterName, LONGNVARCHAR, valueText ) );
1036        }
1037    }   //  setNCharacterStream()
1038
1039    /**
1040     *  {@inheritDoc}
1041     */
1042    @Override
1043    public final void setNClob( final String parameterName, final NClob value ) throws SQLException
1044    {
1045        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setNClob( index, value );
1046        if( isLoggingEnabled() )
1047        {
1048            final var valueText = isNull( value ) ? NULL_STRING : value.getClass().getName();
1049            m_Values.put( parameterName, new StatementValue( parameterName, NCLOB, valueText ) );
1050        }
1051    }   //  setNClob()
1052
1053    /**
1054     *  {@inheritDoc}
1055     */
1056    @Override
1057    public final void setNClob( final String parameterName, final Reader value, final long length ) throws SQLException
1058    {
1059        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setNClob( index, value, length );
1060        if( isLoggingEnabled() )
1061        {
1062            final var valueText = isNull( value ) ? NULL_STRING : format( "%1$s (%2$d)", value.getClass().getName(), length );
1063            m_Values.put( parameterName, new StatementValue( parameterName, NCLOB, valueText ) );
1064        }
1065    }   //  setNClob()
1066
1067    /**
1068     *  {@inheritDoc}
1069     */
1070    @Override
1071    public final void setNClob( final String parameterName, final Reader value ) throws SQLException
1072    {
1073        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setNClob( index, value );
1074        if( isLoggingEnabled() )
1075        {
1076            final var valueText = isNull( value ) ? NULL_STRING : value.getClass().getName();
1077            m_Values.put( parameterName, new StatementValue( parameterName, NCLOB, valueText ) );
1078        }
1079    }   //  setNClob()
1080
1081    /**
1082     *  {@inheritDoc}
1083     */
1084    @Override
1085    public final void setNString( final String parameterName, final String value ) throws SQLException
1086    {
1087        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setNString( index, value );
1088        if( isLoggingEnabled() )
1089        {
1090            m_Values.put( parameterName, new StatementValue( parameterName, NCHAR, value ) );
1091        }
1092    }   //  setNString()
1093
1094    /**
1095     *  {@inheritDoc}
1096     */
1097    @Override
1098    public final void setNull( final String parameterName, final SQLType sqlType ) throws SQLException
1099    {
1100        final var type = requireNonNullArgument( sqlType, "sqlType" ).getVendorTypeNumber();
1101        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setNull( index, type );
1102        if( isLoggingEnabled() )
1103        {
1104            m_Values.put( parameterName, new StatementValue( parameterName, sqlType, NULL_STRING ) );
1105        }
1106    }   //  setNull()
1107
1108    /**
1109     *  {@inheritDoc}
1110     */
1111    @Override
1112    public void setNull( final String parameterName, final SQLType sqlType, final String typeName ) throws SQLException
1113    {
1114        final var type = requireNonNullArgument( sqlType, "sqlType" ).getVendorTypeNumber();
1115        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setNull( index, type, typeName );
1116        if( isLoggingEnabled() )
1117        {
1118            m_Values.put( parameterName, new StatementValue( parameterName, sqlType, format( "%1$s (%2$s)", NULL_STRING, typeName ) ) );
1119        }
1120    }   //  setNull()
1121
1122    /**
1123     *  {@inheritDoc}
1124     */
1125    @Override
1126    public final void setObject( final String parameterName, final Object value ) throws SQLException
1127    {
1128        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setObject( index, value );
1129        if( isLoggingEnabled() )
1130        {
1131            m_Values.put( parameterName, new StatementValue( parameterName, null, Objects.toString( value ) ) );
1132        }
1133    }   //  setObject()
1134
1135    /**
1136     *  {@inheritDoc}
1137     */
1138    @Override
1139    public final void setObject( final String parameterName, final Object value, final SQLType targetSqlType, final int scaleOrLength ) throws SQLException
1140    {
1141        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setObject( index, value, scaleOrLength );
1142        if( isLoggingEnabled() )
1143        {
1144            m_Values.put( parameterName, new StatementValue( parameterName, targetSqlType, format( "%1$s (%2$d)", Objects.toString( value ), scaleOrLength ) ) );
1145        }
1146    }   //  setObject()
1147
1148    /**
1149     *  {@inheritDoc}
1150     */
1151    @Override
1152    public void setObject( final String parameterName, final Object value, final SQLType targetSqlType ) throws SQLException
1153    {
1154        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setObject( index, value );
1155        if( isLoggingEnabled() )
1156        {
1157            m_Values.put( parameterName, new StatementValue( parameterName, targetSqlType, Objects.toString( value ) ) );
1158        }
1159    }   //  setObject()
1160
1161    /**
1162     *  {@inheritDoc}
1163     */
1164    @Override
1165    public final void setQueryTimeout( final int timeout ) throws SQLException { m_PreparedStatement.setQueryTimeout( timeout ); }
1166
1167    /**
1168     *  {@inheritDoc}
1169     */
1170    @Override
1171    public final void setRef( final String parameterName, final Ref value ) throws SQLException
1172    {
1173        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setRef( index, value );
1174        if( isLoggingEnabled() )
1175        {
1176            m_Values.put( parameterName, new StatementValue( parameterName, REF, Objects.toString( value ) ) );
1177        }
1178    }   //  setRef()
1179
1180    /**
1181     *  {@inheritDoc}
1182     */
1183    @Override
1184    public final void setRowId( final String parameterName, final RowId value ) throws SQLException
1185    {
1186        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setRowId( index, value );
1187        if( isLoggingEnabled() )
1188        {
1189            m_Values.put( parameterName, new StatementValue( parameterName, ROWID, Objects.toString( value ) ) );
1190        }
1191    }   //  setRowId()
1192
1193    /**
1194     *  {@inheritDoc}
1195     */
1196    @Override
1197    public final void setShort( final String parameterName, final short value ) throws SQLException
1198    {
1199        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setShort( index, value );
1200        if( isLoggingEnabled() )
1201        {
1202            m_Values.put( parameterName, new StatementValue( parameterName, SMALLINT, Short.toString( value ) ) );
1203        }
1204    }   //  setShort()
1205
1206    /**
1207     *  {@inheritDoc}
1208     */
1209    @Override
1210    public final void setSQLXML( final String parameterName, final SQLXML value ) throws SQLException
1211    {
1212        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setSQLXML( index, value );
1213        if( isLoggingEnabled() )
1214        {
1215            final var valueText = isNull( value ) ? NULL_STRING : value.getClass().getName();
1216            m_Values.put( parameterName, new StatementValue( parameterName, SQLXML, valueText ) );
1217        }
1218    }   //  setSQLXML
1219
1220    /**
1221     *  {@inheritDoc}
1222     */
1223    @Override
1224    public final void setString( final String parameterName, final String value ) throws SQLException
1225    {
1226        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setString( index, value );
1227        if( isLoggingEnabled() )
1228        {
1229            m_Values.put( parameterName, new StatementValue( parameterName, VARCHAR, value ) );
1230        }
1231    }   //  setString()
1232
1233    /**
1234     *  {@inheritDoc}
1235     */
1236    @Override
1237    public final void setTime( final String parameterName, final Time value ) throws SQLException
1238    {
1239        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setTime( index, value );
1240        if( isLoggingEnabled() )
1241        {
1242            var valueText = NULL_STRING;
1243            if( nonNull( value ) )
1244            {
1245                final var instant = value.toInstant();
1246                valueText = format( "%2$s (%1$s)", instant, instant.atZone( ZoneId.systemDefault() ).toLocalTime() );
1247            }
1248            m_Values.put( parameterName, new StatementValue( parameterName, TIME, valueText ) );
1249        }
1250    }   //  setTime()
1251
1252    /**
1253     *  {@inheritDoc}
1254     */
1255    @SuppressWarnings( "UseOfObsoleteDateTimeApi" )
1256    @Override
1257    public final void setTime( final String parameterName, final Time value, final Calendar calendar ) throws SQLException
1258    {
1259        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setTime( index, value, calendar );
1260        if( isLoggingEnabled() )
1261        {
1262            var valueText = NULL_STRING;
1263            if( nonNull( value ) )
1264            {
1265                final var instant = value.toInstant();
1266                final var timezone = isNull( calendar ) ? ZoneId.systemDefault() : calendar.getTimeZone().toZoneId();
1267                valueText = format( "%2$s (%1$s)", instant, instant.atZone( timezone ).toLocalTime() );
1268            }
1269            m_Values.put( parameterName, new StatementValue( parameterName, TIME, valueText ) );
1270        }
1271    }   //  setTime()
1272
1273    /**
1274     *  {@inheritDoc}
1275     */
1276    @Override
1277    public final void setTimestamp( final String parameterName, final Timestamp value ) throws SQLException
1278    {
1279        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setTimestamp( index, value );
1280        if( isLoggingEnabled() )
1281        {
1282            final var valueText = isNull( value ) ? NULL_STRING : value.toInstant().atZone( ZoneId.systemDefault() ).toString();
1283            m_Values.put( parameterName, new StatementValue( parameterName, TIMESTAMP, valueText ) );
1284        }
1285    }   //  setTimestamp()
1286
1287    /**
1288     *  {@inheritDoc}
1289     */
1290    @SuppressWarnings( "UseOfObsoleteDateTimeApi" )
1291    @Override
1292    public final void setTimestamp( final String parameterName, final Timestamp value, final Calendar calendar ) throws SQLException
1293    {
1294        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setTimestamp( index, value, calendar );
1295        if( isLoggingEnabled() )
1296        {
1297            final var timezone = isNull( calendar ) ? ZoneId.systemDefault() : calendar.getTimeZone().toZoneId();
1298            final var valueText = isNull( value ) ? NULL_STRING : value.toInstant().atZone( timezone ).toString();
1299            m_Values.put( parameterName, new StatementValue( parameterName, TIMESTAMP, valueText ) );
1300        }
1301    }   //  setTimestamp()
1302
1303    /**
1304     *  {@inheritDoc}
1305     */
1306    @Override
1307    public final void setURL( final String parameterName, final URL value ) throws SQLException
1308    {
1309        for( final var index : getParameterIndexes( parameterName ) ) m_PreparedStatement.setURL( index, value );
1310        if( isLoggingEnabled() )
1311        {
1312            final var valueText = isNull( value ) ? NULL_STRING : value.toExternalForm();
1313            m_Values.put( parameterName, new StatementValue( parameterName, DATALINK, valueText ) );
1314        }
1315    }   //  setURL()
1316}
1317//  class EnhancedPreparedStatementBase
1318
1319/*
1320 *  End of File
1321 */