src/jdk.incubator.adba/share/classes/jdk/incubator/sql2/OperationGroup.java
branchJDK-8188051-branch
changeset 56828 64304e37e9b1
parent 56824 62e92191354d
child 56832 4f7713e6a308
equal deleted inserted replaced
56827:1a36ad36c9e9 56828:64304e37e9b1
    26 
    26 
    27 import java.time.Duration;
    27 import java.time.Duration;
    28 import java.util.concurrent.CompletionStage;
    28 import java.util.concurrent.CompletionStage;
    29 import java.util.function.Consumer;
    29 import java.util.function.Consumer;
    30 import java.util.logging.Logger;
    30 import java.util.logging.Logger;
       
    31 import java.util.regex.Pattern;
    31 import java.util.stream.Collector;
    32 import java.util.stream.Collector;
    32 
    33 
    33 /**
    34 /**
    34  * <p>
    35  * <p>
    35  * A set of {@link Operation}s that share certain properties, are managed as a
    36  * A set of {@link Operation}s that share certain properties, are managed as a
   389    * convenience methods do so.</p>
   390    * convenience methods do so.</p>
   390    *
   391    *
   391    * The type argument {@link S} of the containing {@link OperationGroup} must
   392    * The type argument {@link S} of the containing {@link OperationGroup} must
   392    * be a supertype of {@link TransactionOutcome}.
   393    * be a supertype of {@link TransactionOutcome}.
   393    *
   394    *
   394    * @param trans the TransactionEnd that determines whether the Operation does a 
   395    * @param trans the TransactionCompletion that determines whether the Operation does a 
   395    * database commit or a database rollback.
   396  database commit or a database rollback.
   396    * @return an {@link Operation} that will end the database transaction.
   397    * @return an {@link Operation} that will end the database transaction.
   397    * @throws IllegalStateException if this {@link OperationGroup} has been
   398    * @throws IllegalStateException if this {@link OperationGroup} has been
   398    * submitted and is not held or is parallel.
   399    * submitted and is not held or is parallel.
   399    */
   400    */
   400   public Operation<TransactionOutcome> endTransactionOperation(TransactionEnd trans);
   401   public Operation<TransactionOutcome> endTransactionOperation(TransactionCompletion trans);
   401 
   402 
   402   /**
   403   /**
   403    * Convenience method that creates and submits a endTransaction
   404    * Convenience method that creates and submits a endTransaction
   404    * {@link Operation} that commits by default but can be set to rollback by
   405    * {@link Operation} that commits by default but can be set to rollback by
   405    * calling {@link TransactionEnd#setRollbackOnly}. The endTransaction Operation
   406    * calling {@link TransactionCompletion#setRollbackOnly}. The endTransaction Operation
   406    * is never skipped.
   407    * is never skipped.
   407    *
   408    *
   408    * @param trans the TransactionEnd that determines whether the {@link Operation} is a
   409    * @param trans the TransactionCompletion that determines whether the {@link Operation} is a
   409    * database commit or a database rollback.
   410    * database commit or a database rollback.
   410    * @return a {@link CompletionStage} that is completed with the outcome of the 
   411    * @return a {@link CompletionStage} that is completed with the outcome of the 
   411    * transaction
   412    * transaction
   412    * @throws IllegalStateException if this {@link OperationGroup} has been
   413    * @throws IllegalStateException if this {@link OperationGroup} has been
   413    * submitted and is not held or is parallel.
   414    * submitted and is not held or is parallel.
   414    */
   415    */
   415   public default CompletionStage<TransactionOutcome> commitMaybeRollback(TransactionEnd trans) {
   416   public default CompletionStage<TransactionOutcome> commitMaybeRollback(TransactionCompletion trans) {
   416     catchErrors();
   417     catchErrors();
   417     return this.endTransactionOperation(trans).submit().getCompletionStage();
   418     return this.endTransactionOperation(trans).submit().getCompletionStage();
   418   }
   419   }
   419 
   420 
   420   /**
   421   /**
   459    * @param logger used by the implementation to log significant events
   460    * @param logger used by the implementation to log significant events
   460    * @return this {@link OperationGroup}
   461    * @return this {@link OperationGroup}
   461    */
   462    */
   462   public OperationGroup<S, T> logger(Logger logger);
   463   public OperationGroup<S, T> logger(Logger logger);
   463 
   464 
   464   // Covariant overrides
   465   /**
       
   466    * Returns a {@code String} enclosed in single quotes. Any occurrence of a
       
   467    * single quote within the string will be replaced by two single quotes.
       
   468    *
       
   469    * <blockquote>
       
   470    * <table class="striped">
       
   471    * <caption>Examples of the conversion:</caption>
       
   472    * <thead>
       
   473    * <tr><th scope="col">Value</th><th scope="col">Result</th></tr>
       
   474    * </thead>
       
   475    * <tbody style="text-align:center">
       
   476    * <tr> <th scope="row">Hello</th> <td>'Hello'</td> </tr>
       
   477    * <tr> <th scope="row">G'Day</th> <td>'G''Day'</td> </tr>
       
   478    * <tr> <th scope="row">'G''Day'</th>
       
   479    * <td>'''G''''Day'''</td> </tr>
       
   480    * <tr> <th scope="row">I'''M</th> <td>'I''''''M'</td>
       
   481    * </tr>
       
   482    *
       
   483    * </tbody>
       
   484    * </table>
       
   485    * </blockquote>
       
   486    *
       
   487    * @implNote JDBC driver implementations may need to provide their own
       
   488    * implementation of this method in order to meet the requirements of the
       
   489    * underlying datasource.
       
   490    * @param val a character string. Not null
       
   491    * @return A string enclosed by single quotes with every single quote
       
   492    * converted to two single quotes. Not null
       
   493    * @throws NullPointerException if {@code val} is {@code null}
       
   494    * @throws IllegalArgumentException if {@code val} cannot be enquoted
       
   495    */
       
   496   default String enquoteLiteral(String val) {
       
   497     return "'" + val.replace("'", "''") + "'";
       
   498   }
       
   499 
       
   500   /**
       
   501    * Returns a SQL identifier. If {@code identifier} is a simple SQL identifier:
       
   502    * <ul>
       
   503    * <li>Return the original value if {@code alwaysQuote} is {@code false}</li>
       
   504    * <li>Return a delimited identifier if {@code alwaysQuote} is
       
   505    * {@code true}</li>
       
   506    * </ul>
       
   507    *
       
   508    * If {@code identifier} is not a simple SQL identifier, {@code identifier}
       
   509    * will be enclosed in double quotes if not already present. If the datasource
       
   510    * does not support double quotes for delimited identifiers, the identifier
       
   511    * should be enclosed by the string returned from
       
   512    * {@link DatabaseMetaData#getIdentifierQuoteString}. If the datasource does
       
   513    * not support delimited identifiers, a
       
   514    * {@code SQLFeatureNotSupportedException} should be thrown.
       
   515    * <p>
       
   516    * A {@code SQLException} will be thrown if {@code identifier} contains any
       
   517    * characters invalid in a delimited identifier or the identifier length is
       
   518    * invalid for the datasource.
       
   519    *
       
   520    * @implSpec The default implementation uses the following criteria to
       
   521    * determine a valid simple SQL identifier:
       
   522    * <ul>
       
   523    * <li>The string is not enclosed in double quotes</li>
       
   524    * <li>The first character is an alphabetic character from a through z, or
       
   525    * from A through Z</li>
       
   526    * <li>The name only contains alphanumeric characters or the character
       
   527    * "_"</li>
       
   528    * </ul>
       
   529    *
       
   530    * The default implementation will throw a {@code SQLException} if:
       
   531    * <ul>
       
   532    * <li>{@code identifier} contains a {@code null} character or double quote
       
   533    * and is not a simple SQL identifier.</li>
       
   534    * <li>The length of {@code identifier} is less than 1 or greater than 128
       
   535    * characters
       
   536    * </ul>
       
   537    * <blockquote>
       
   538    * <table class="striped" >
       
   539    * <caption>Examples of the conversion:</caption>
       
   540    * <thead>
       
   541    * <tr>
       
   542    * <th scope="col">identifier</th>
       
   543    * <th scope="col">alwaysQuote</th>
       
   544    * <th scope="col">Result</th></tr>
       
   545    * </thead>
       
   546    * <tbody>
       
   547    * <tr>
       
   548    * <th scope="row">Hello</th>
       
   549    * <td>false</td>
       
   550    * <td>Hello</td>
       
   551    * </tr>
       
   552    * <tr>
       
   553    * <th scope="row">Hello</th>
       
   554    * <td>true</td>
       
   555    * <td>"Hello"</td>
       
   556    * </tr>
       
   557    * <tr>
       
   558    * <th scope="row">G'Day</th>
       
   559    * <td>false</td>
       
   560    * <td>"G'Day"</td>
       
   561    * </tr>
       
   562    * <tr>
       
   563    * <th scope="row">"Bruce Wayne"</th>
       
   564    * <td>false</td>
       
   565    * <td>"Bruce Wayne"</td>
       
   566    * </tr>
       
   567    * <tr>
       
   568    * <th scope="row">"Bruce Wayne"</th>
       
   569    * <td>true</td>
       
   570    * <td>"Bruce Wayne"</td>
       
   571    * </tr>
       
   572    * <tr>
       
   573    * <th scope="row">GoodDay$</th>
       
   574    * <td>false</td>
       
   575    * <td>"GoodDay$"</td>
       
   576    * </tr>
       
   577    * <tr>
       
   578    * <th scope="row">Hello"World</th>
       
   579    * <td>false</td>
       
   580    * <td>SQLException</td>
       
   581    * </tr>
       
   582    * <tr>
       
   583    * <th scope="row">"Hello"World"</th>
       
   584    * <td>false</td>
       
   585    * <td>SQLException</td>
       
   586    * </tr>
       
   587    * </tbody>
       
   588    * </table>
       
   589    * </blockquote>
       
   590    * @implNote JDBC driver implementations may need to provide their own
       
   591    * implementation of this method in order to meet the requirements of the
       
   592    * underlying datasource.
       
   593    * @param identifier a SQL identifier. Not null
       
   594    * @param alwaysQuote indicates if a simple SQL identifier should be returned
       
   595    * as a quoted identifier
       
   596    * @return A simple SQL identifier or a delimited identifier. Not null
       
   597    * @throws NullPointerException if identifier is {@code null}
       
   598    * @throws IllegalArgumentException if {@code identifier} can not be converted 
       
   599    * to a valid identifier
       
   600    */
       
   601   default String enquoteIdentifier(String identifier, boolean alwaysQuote) {
       
   602     int len = identifier.length();
       
   603     if (len < 1 || len > 128) {
       
   604       throw new IllegalArgumentException("Invalid name");
       
   605     }
       
   606     if (Pattern.compile("[\\p{Alpha}][\\p{Alnum}_]*").matcher(identifier).matches()) {
       
   607       return alwaysQuote ? "\"" + identifier + "\"" : identifier;
       
   608     }
       
   609     if (identifier.matches("^\".+\"$")) {
       
   610       identifier = identifier.substring(1, len - 1);
       
   611     }
       
   612     if (Pattern.compile("[^\u0000\"]+").matcher(identifier).matches()) {
       
   613       return "\"" + identifier + "\"";
       
   614     }
       
   615     else {
       
   616       throw new IllegalArgumentException("Invalid name");
       
   617     }
       
   618   }
       
   619 
       
   620   /**
       
   621    * Retrieves whether {@code identifier} is a simple SQL identifier.
       
   622    *
       
   623    * @implSpec The default implementation uses the following criteria to
       
   624    * determine a valid simple SQL identifier:
       
   625    * <ul>
       
   626    * <li>The string is not enclosed in double quotes</li>
       
   627    * <li>The first character is an alphabetic character from a through z, or
       
   628    * from A through Z</li>
       
   629    * <li>The string only contains alphanumeric characters or the character
       
   630    * "_"</li>
       
   631    * <li>The string is between 1 and 128 characters in length inclusive</li>
       
   632    * </ul>
       
   633    *
       
   634    * <blockquote>
       
   635    * <table class="striped" >
       
   636    * <caption>Examples of the conversion:</caption>
       
   637    * <thead>
       
   638    * <tr>
       
   639    * <th scope="col">identifier</th>
       
   640    * <th scope="col">Simple Identifier</th>
       
   641    * </thead>
       
   642    *
       
   643    * <tbody>
       
   644    * <tr>
       
   645    * <th scope="row">Hello</th>
       
   646    * <td>true</td>
       
   647    * </tr>
       
   648    * <tr>
       
   649    * <th scope="row">G'Day</th>
       
   650    * <td>false</td>
       
   651    * </tr>
       
   652    * <tr>
       
   653    * <th scope="row">"Bruce Wayne"</th>
       
   654    * <td>false</td>
       
   655    * </tr>
       
   656    * <tr>
       
   657    * <th scope="row">GoodDay$</th>
       
   658    * <td>false</td>
       
   659    * </tr>
       
   660    * <tr>
       
   661    * <th scope="row">Hello"World</th>
       
   662    * <td>false</td>
       
   663    * </tr>
       
   664    * <tr>
       
   665    * <th scope="row">"Hello"World"</th>
       
   666    * <td>false</td>
       
   667    * </tr>
       
   668    * </tbody>
       
   669    * </table>
       
   670    * </blockquote>
       
   671    * @implNote JDBC driver implementations may need to provide their own
       
   672    * implementation of this method in order to meet the requirements of the
       
   673    * underlying datasource.
       
   674    * @param identifier a SQL identifier. Not null
       
   675    * @return true if a simple SQL identifier, false otherwise
       
   676    * @throws NullPointerException if identifier is {@code null}
       
   677    */
       
   678   default boolean isSimpleIdentifier(String identifier) {
       
   679     int len = identifier.length();
       
   680     return len >= 1 && len <= 128
       
   681             && Pattern.compile("[\\p{Alpha}][\\p{Alnum}_]*").matcher(identifier).matches();
       
   682   }
       
   683 
       
   684   /**
       
   685    * Returns a {@code String} representing a National Character Set Literal
       
   686    * enclosed in single quotes and prefixed with a upper case letter N. Any
       
   687    * occurrence of a single quote within the string will be replaced by two
       
   688    * single quotes.
       
   689    *
       
   690    * <blockquote>
       
   691    * <table class="striped">
       
   692    * <caption>Examples of the conversion:</caption>
       
   693    * <thead>
       
   694    * <tr>
       
   695    * <th scope="col">Value</th>
       
   696    * <th scope="col">Result</th>
       
   697    * </tr>
       
   698    * </thead>
       
   699    * <tbody>
       
   700    * <tr> <th scope="row">Hello</th> <td>N'Hello'</td> </tr>
       
   701    * <tr> <th scope="row">G'Day</th> <td>N'G''Day'</td> </tr>
       
   702    * <tr> <th scope="row">'G''Day'</th>
       
   703    * <td>N'''G''''Day'''</td> </tr>
       
   704    * <tr> <th scope="row">I'''M</th> <td>N'I''''''M'</td>
       
   705    * <tr> <th scope="row">N'Hello'</th> <td>N'N''Hello'''</td> </tr>
       
   706    *
       
   707    * </tbody>
       
   708    * </table>
       
   709    * </blockquote>
       
   710    *
       
   711    * @implNote JDBC driver implementations may need to provide their own
       
   712    * implementation of this method in order to meet the requirements of the
       
   713    * underlying datasource. An implementation of enquoteNCharLiteral may accept
       
   714    * a different set of characters than that accepted by the same drivers
       
   715    * implementation of enquoteLiteral.
       
   716    * @param val a character string. Not null
       
   717    * @return the result of replacing every single quote character in the
       
   718    * argument by two single quote characters where this entire result is then
       
   719    * prefixed with 'N'. Not null.
       
   720    * @throws NullPointerException if {@code val} is {@code null}
       
   721    * @throws IllegalArgumentException if {@code val} cannot be enquoted
       
   722    */
       
   723   default String enquoteNCharLiteral(String val) {
       
   724     return "N'" + val.replace("'", "''") + "'";
       
   725   }
       
   726 
       
   727     // Covariant overrides
   465   @Override
   728   @Override
   466   public OperationGroup<S, T> timeout(Duration minTime);
   729   public OperationGroup<S, T> timeout(Duration minTime);
   467 
   730 
   468   @Override
   731   @Override
   469   public OperationGroup<S, T> onError(Consumer<Throwable> handler);
   732   public OperationGroup<S, T> onError(Consumer<Throwable> handler);