All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jooq.impl.Tools Maven / Gradle / Ivy

There is a newer version: 3.19.15
Show newest version
/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Other licenses:
 * -----------------------------------------------------------------------------
 * Commercial licenses for this work are available. These replace the above
 * Apache-2.0 license and offer limited warranties, support, maintenance, and
 * commercial database integrations.
 *
 * For more information, please visit: https://www.jooq.org/legal/licensing
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */
package org.jooq.impl;

import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import static java.lang.Character.isJavaIdentifierPart;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.nCopies;
import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.joining;
import static org.jooq.ContextConverter.scoped;
// ...
// ...
// ...
// ...
// ...
// ...
// ...
import static org.jooq.SQLDialect.DERBY;
import static org.jooq.SQLDialect.DUCKDB;
// ...
import static org.jooq.SQLDialect.FIREBIRD;
import static org.jooq.SQLDialect.H2;
// ...
// ...
import static org.jooq.SQLDialect.HSQLDB;
// ...
// ...
// ...
import static org.jooq.SQLDialect.MARIADB;
// ...
import static org.jooq.SQLDialect.MYSQL;
// ...
// ...
// ...
import static org.jooq.SQLDialect.POSTGRES;
// ...
// ...
// ...
// ...
import static org.jooq.SQLDialect.SQLITE;
// ...
// ...
// ...
// ...
import static org.jooq.SQLDialect.TRINO;
import static org.jooq.SQLDialect.YUGABYTEDB;
import static org.jooq.conf.BackslashEscaping.DEFAULT;
import static org.jooq.conf.BackslashEscaping.ON;
import static org.jooq.conf.ParamType.INLINED;
import static org.jooq.conf.ParamType.NAMED;
import static org.jooq.conf.ParamType.NAMED_OR_INLINED;
import static org.jooq.conf.RenderDefaultNullability.IMPLICIT_NULL;
import static org.jooq.conf.RenderQuotedNames.EXPLICIT_DEFAULT_QUOTED;
import static org.jooq.conf.SettingsTools.getBackslashEscaping;
import static org.jooq.conf.SettingsTools.updatablePrimaryKeys;
import static org.jooq.conf.ThrowExceptions.THROW_FIRST;
import static org.jooq.conf.ThrowExceptions.THROW_NONE;
import static org.jooq.exception.DataAccessException.sqlStateClass;
import static org.jooq.impl.CacheType.REFLECTION_CACHE_GET_ANNOTATED_GETTER;
import static org.jooq.impl.CacheType.REFLECTION_CACHE_GET_ANNOTATED_MEMBERS;
import static org.jooq.impl.CacheType.REFLECTION_CACHE_GET_ANNOTATED_SETTERS;
import static org.jooq.impl.CacheType.REFLECTION_CACHE_GET_MATCHING_GETTER;
import static org.jooq.impl.CacheType.REFLECTION_CACHE_GET_MATCHING_MEMBERS;
import static org.jooq.impl.CacheType.REFLECTION_CACHE_GET_MATCHING_SETTERS;
import static org.jooq.impl.CacheType.REFLECTION_CACHE_HAS_COLUMN_ANNOTATIONS;
import static org.jooq.impl.Convert.convert;
import static org.jooq.impl.DDLStatementType.ALTER_SCHEMA;
import static org.jooq.impl.DDLStatementType.ALTER_TABLE;
import static org.jooq.impl.DDLStatementType.ALTER_VIEW;
import static org.jooq.impl.DDLStatementType.CREATE_DATABASE;
import static org.jooq.impl.DDLStatementType.CREATE_DOMAIN;
import static org.jooq.impl.DDLStatementType.CREATE_INDEX;
import static org.jooq.impl.DDLStatementType.CREATE_SCHEMA;
import static org.jooq.impl.DDLStatementType.CREATE_SEQUENCE;
import static org.jooq.impl.DDLStatementType.CREATE_TABLE;
import static org.jooq.impl.DDLStatementType.CREATE_VIEW;
import static org.jooq.impl.DDLStatementType.DROP_INDEX;
import static org.jooq.impl.DDLStatementType.DROP_SCHEMA;
import static org.jooq.impl.DDLStatementType.DROP_SEQUENCE;
import static org.jooq.impl.DDLStatementType.DROP_TABLE;
import static org.jooq.impl.DDLStatementType.DROP_VIEW;
import static org.jooq.impl.DSL.all;
import static org.jooq.impl.DSL.any;
import static org.jooq.impl.DSL.asterisk;
import static org.jooq.impl.DSL.concat;
import static org.jooq.impl.DSL.escape;
import static org.jooq.impl.DSL.getDataType;
import static org.jooq.impl.DSL.keyword;
import static org.jooq.impl.DSL.name;
import static org.jooq.impl.DSL.noCondition;
import static org.jooq.impl.DSL.row;
import static org.jooq.impl.DSL.select;
import static org.jooq.impl.DSL.unquotedName;
import static org.jooq.impl.DSL.val;
import static org.jooq.impl.DefaultExecuteContext.localConnection;
import static org.jooq.impl.DefaultParseContext.SUPPORTS_HASH_COMMENT_SYNTAX;
import static org.jooq.impl.DerivedTable.NO_SUPPORT_CORRELATED_DERIVED_TABLE;
import static org.jooq.impl.Identifiers.QUOTES;
import static org.jooq.impl.Identifiers.QUOTE_END_DELIMITER;
import static org.jooq.impl.Identifiers.QUOTE_END_DELIMITER_ESCAPED;
import static org.jooq.impl.Identifiers.QUOTE_START_DELIMITER;
import static org.jooq.impl.Keywords.K_ALWAYS;
import static org.jooq.impl.Keywords.K_AS;
import static org.jooq.impl.Keywords.K_ATOMIC;
import static org.jooq.impl.Keywords.K_AUTOINCREMENT;
import static org.jooq.impl.Keywords.K_AUTO_INCREMENT;
import static org.jooq.impl.Keywords.K_BEGIN;
import static org.jooq.impl.Keywords.K_BEGIN_CATCH;
import static org.jooq.impl.Keywords.K_BEGIN_TRY;
import static org.jooq.impl.Keywords.K_BY;
import static org.jooq.impl.Keywords.K_CHARACTER_SET;
import static org.jooq.impl.Keywords.K_COLLATE;
import static org.jooq.impl.Keywords.K_DECLARE;
import static org.jooq.impl.Keywords.K_DEFAULT;
import static org.jooq.impl.Keywords.K_DO;
import static org.jooq.impl.Keywords.K_ELSE;
import static org.jooq.impl.Keywords.K_ELSIF;
import static org.jooq.impl.Keywords.K_END;
import static org.jooq.impl.Keywords.K_END_CATCH;
import static org.jooq.impl.Keywords.K_END_IF;
import static org.jooq.impl.Keywords.K_END_TRY;
import static org.jooq.impl.Keywords.K_ENUM;
import static org.jooq.impl.Keywords.K_ERROR;
import static org.jooq.impl.Keywords.K_EXCEPTION;
import static org.jooq.impl.Keywords.K_EXEC;
import static org.jooq.impl.Keywords.K_EXECUTE_BLOCK;
import static org.jooq.impl.Keywords.K_EXECUTE_IMMEDIATE;
import static org.jooq.impl.Keywords.K_EXECUTE_STATEMENT;
import static org.jooq.impl.Keywords.K_GENERATED;
import static org.jooq.impl.Keywords.K_IDENTITY;
import static org.jooq.impl.Keywords.K_IF;
import static org.jooq.impl.Keywords.K_INT;
import static org.jooq.impl.Keywords.K_LIKE;
import static org.jooq.impl.Keywords.K_NOT;
import static org.jooq.impl.Keywords.K_NOT_NULL;
import static org.jooq.impl.Keywords.K_NULL;
import static org.jooq.impl.Keywords.K_NVARCHAR;
import static org.jooq.impl.Keywords.K_PERSISTED;
import static org.jooq.impl.Keywords.K_PRIMARY_KEY;
import static org.jooq.impl.Keywords.K_RAISE;
import static org.jooq.impl.Keywords.K_RAISERROR;
import static org.jooq.impl.Keywords.K_SERIAL;
import static org.jooq.impl.Keywords.K_SERIAL4;
import static org.jooq.impl.Keywords.K_SERIAL8;
import static org.jooq.impl.Keywords.K_SQLSTATE;
import static org.jooq.impl.Keywords.K_START_WITH;
import static org.jooq.impl.Keywords.K_STORED;
import static org.jooq.impl.Keywords.K_THEN;
import static org.jooq.impl.Keywords.K_THROW;
import static org.jooq.impl.Keywords.K_VIRTUAL;
import static org.jooq.impl.Keywords.K_WHEN;
import static org.jooq.impl.QOM.GenerationOption.STORED;
import static org.jooq.impl.QOM.GenerationOption.VIRTUAL;
import static org.jooq.impl.SQLDataType.BLOB;
import static org.jooq.impl.SQLDataType.CLOB;
import static org.jooq.impl.SQLDataType.DECIMAL;
import static org.jooq.impl.SQLDataType.DOUBLE;
import static org.jooq.impl.SQLDataType.INTEGER;
import static org.jooq.impl.SQLDataType.JSON;
import static org.jooq.impl.SQLDataType.JSONB;
import static org.jooq.impl.SQLDataType.OTHER;
import static org.jooq.impl.SQLDataType.SMALLINT;
import static org.jooq.impl.SQLDataType.VARCHAR;
import static org.jooq.impl.SQLDataType.XML;
import static org.jooq.impl.SubqueryCharacteristics.DERIVED_TABLE;
import static org.jooq.impl.SubqueryCharacteristics.PREDICAND;
import static org.jooq.impl.SubqueryCharacteristics.SET_OPERATION;
import static org.jooq.impl.Tools.executeImmediate;
import static org.jooq.impl.Tools.SimpleDataKey.DATA_BLOCK_NESTING;
import static org.jooq.tools.StringUtils.defaultIfNull;

import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinPool.ManagedBlocker;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.regex.MatchResult;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.IntStream;
import java.util.stream.Stream;

// ...
// ...
// ...
import org.jooq.Asterisk;
import org.jooq.Attachable;
import org.jooq.BindContext;
import org.jooq.Catalog;
import org.jooq.Check;
import org.jooq.Clause;
import org.jooq.CommonTableExpression;
import org.jooq.Condition;
import org.jooq.Configuration;
import org.jooq.Context;
import org.jooq.ContextConverter;
import org.jooq.Converter;
import org.jooq.ConverterContext;
import org.jooq.ConverterProvider;
import org.jooq.Converters;
import org.jooq.Cursor;
import org.jooq.DSLContext;
import org.jooq.DataType;
import org.jooq.EmbeddableRecord;
import org.jooq.EnumType;
import org.jooq.ExecuteContext;
import org.jooq.ExecuteListener;
import org.jooq.Field;
import org.jooq.FieldOrRow;
import org.jooq.FieldOrRowOrSelect;
import org.jooq.Fields;
import org.jooq.ForeignKey;
import org.jooq.Function1;
import org.jooq.Function2;
import org.jooq.Function3;
import org.jooq.Generator;
import org.jooq.JSON;
import org.jooq.JSONB;
import org.jooq.JSONEntry;
import org.jooq.JoinType;
import org.jooq.Name;
import org.jooq.OrderField;
import org.jooq.Param;
// ...
import org.jooq.QualifiedAsterisk;
import org.jooq.QualifiedRecord;
import org.jooq.QuantifiedSelect;
import org.jooq.Query;
import org.jooq.QueryPart;
import org.jooq.Record;
import org.jooq.Record1;
import org.jooq.RecordQualifier;
import org.jooq.RenderContext;
import org.jooq.RenderContext.CastMode;
import org.jooq.Result;
import org.jooq.ResultOrRows;
import org.jooq.ResultQuery;
import org.jooq.Results;
import org.jooq.Row;
import org.jooq.SQLDialect;
import org.jooq.Schema;
import org.jooq.Scope;
import org.jooq.Select;
import org.jooq.SelectFieldOrAsterisk;
import org.jooq.SortField;
import org.jooq.Source;
import org.jooq.Table;
import org.jooq.TableElement;
import org.jooq.TableField;
import org.jooq.TableRecord;
import org.jooq.UDT;
import org.jooq.UDTRecord;
import org.jooq.UpdatableRecord;
import org.jooq.WindowSpecification;
import org.jooq.XML;
import org.jooq.conf.BackslashEscaping;
import org.jooq.conf.NestedCollectionEmulation;
import org.jooq.conf.ParamType;
import org.jooq.conf.ParseNameCase;
import org.jooq.conf.RenderDefaultNullability;
import org.jooq.conf.RenderMapping;
import org.jooq.conf.RenderQuotedNames;
import org.jooq.conf.Settings;
import org.jooq.conf.SettingsTools;
import org.jooq.conf.StatementType;
import org.jooq.conf.ThrowExceptions;
import org.jooq.exception.DataAccessException;
import org.jooq.exception.DataException;
import org.jooq.exception.DataTypeException;
import org.jooq.exception.DetachedException;
import org.jooq.exception.ExceptionTools;
import org.jooq.exception.IntegrityConstraintViolationException;
import org.jooq.exception.MappingException;
import org.jooq.exception.NoDataFoundException;
import org.jooq.exception.SQLStateClass;
import org.jooq.exception.TemplatingException;
import org.jooq.exception.TooManyRowsException;
import org.jooq.impl.QOM.Quantifier;
import org.jooq.impl.QOM.UEmpty;
import org.jooq.impl.ResultsImpl.ResultOrRowsImpl;
import org.jooq.tools.Ints;
import org.jooq.tools.JooqLogger;
import org.jooq.tools.StringUtils;
import org.jooq.tools.jdbc.JDBCUtils;
import org.jooq.tools.reflect.Reflect;
import org.jooq.tools.reflect.ReflectException;
import org.jooq.types.UByte;
import org.jooq.types.UInteger;
import org.jooq.types.ULong;
import org.jooq.types.UShort;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import io.r2dbc.spi.R2dbcException;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;

/**
 * General internal jOOQ utilities
 *
 * @author Lukas Eder
 */
final class Tools {

    static final JooqLogger                 log                           = JooqLogger.getLogger(Tools.class);

    // ------------------------------------------------------------------------
    // Empty arrays for use with Collection.toArray()
    // ------------------------------------------------------------------------

    static final byte[]                     EMPTY_BYTE                    = {};
    static final Catalog[]                  EMPTY_CATALOG                 = {};
    static final Check[]                 EMPTY_CHECK                   = {};
    static final Clause[]                   EMPTY_CLAUSE                  = {};
    static final Collection[]            EMPTY_COLLECTION              = {};
    static final CommonTableExpression[] EMPTY_COMMON_TABLE_EXPRESSION = {};
    static final ExecuteListener[]          EMPTY_EXECUTE_LISTENER        = {};
    static final Field[]                 EMPTY_FIELD                   = {};
    static final FieldOrRow[]               EMPTY_FIELD_OR_ROW            = {};
    static final int[]                      EMPTY_INT                     = {};
    static final JSONEntry[]             EMPTY_JSONENTRY               = {};
    static final Name[]                     EMPTY_NAME                    = {};
    static final Object[]                   EMPTY_OBJECT                  = {};
    static final Param[]                 EMPTY_PARAM                   = {};
    static final OrderField[]            EMPTY_ORDERFIELD              = {};
    static final Query[]                    EMPTY_QUERY                   = {};
    static final QueryPart[]                EMPTY_QUERYPART               = {};
    static final Record[]                   EMPTY_RECORD                  = {};
    static final Row[]                      EMPTY_ROW                     = {};
    static final Schema[]                   EMTPY_SCHEMA                  = {};
    static final SortField[]             EMPTY_SORTFIELD               = {};
    static final Source[]                   EMPTY_SOURCE                  = {};
    static final String[]                   EMPTY_STRING                  = {};
    static final Table[]                 EMPTY_TABLE                   = {};
    static final TableField[]         EMPTY_TABLE_FIELD             = {};
    static final TableRecord[]           EMPTY_TABLE_RECORD            = {};
    static final UpdatableRecord[]       EMPTY_UPDATABLE_RECORD        = {};

    // ------------------------------------------------------------------------
    // Some constants for use with Context.data()
    // ------------------------------------------------------------------------

    /**
     * A common super types for {@link BooleanDataKey}, {@link SimpleDataKey} and {@link ExtendedDataKey}
     */
    sealed interface DataKey {

        /**
         * Whether this data key resets itself to {@link #resetValue()} when
         * entering in a new scope of depth {@link #resetThreshold()}.
         */
        boolean resetInSubqueryScope();

        /**
         * The value to reset itself to.
         */
        Object resetValue();

        /**
         * The depth after which the key resets itself.
         */
        int resetThreshold();
    }

    /**
     * Keys for {@link Configuration#data()}, which may be referenced frequently
     * and represent a {@code boolean} value and are thus stored in an
     * {@link EnumSet} for speedier access.
     */
    enum BooleanDataKey implements DataKey {

        /**
         * [#13468] The WHERE clause in a SELECT is mandatory for the current
         * scope.
         */
        DATA_MANDATORY_WHERE_CLAUSE(true, null, 1),

        /**
         * [#1520] Count the number of bind values, and potentially enforce a
         * static statement.
         */
        DATA_COUNT_BIND_VALUES,

        /**
         * [#1520] Enforce executing static statements.
         * 

* Some SQL dialects support only a limited amount of bind variables. This * flag is set when static statements have too many bind variables. Known * values are: *

    *
  • {@link SQLDialect#ACCESS} : 768
  • *
  • {@link SQLDialect#ASE} : 2000
  • *
  • {@link SQLDialect#INGRES} : 1024
  • *
  • {@link SQLDialect#ORACLE} : 32767
  • *
  • {@link SQLDialect#POSTGRES} : 32767
  • *
  • {@link SQLDialect#SQLITE} : 999
  • *
  • {@link SQLDialect#SQLSERVER} : 2100
  • *
  • {@link org.jooq.SQLDialect#TERADATA} : 2536
  • *
*/ DATA_FORCE_STATIC_STATEMENT, /** * [#2665] Omit the emission of clause events by {@link QueryPart}s. *

* Some {@link QueryPart}s may contain further {@link QueryPart}s for whom * {@link Clause} emission should be avoided. For example * {@link Clause#FIELD_REFERENCE} may contain a * {@link Clause#TABLE_REFERENCE}. */ DATA_OMIT_CLAUSE_EVENT_EMISSION, /** * [#2665] Wrap derived tables in parentheses. *

* Before allowing for hooking into the SQL transformation SPI, new * {@link RenderContext} instances could be created to "try" to render a * given SQL subclause before inserting it into the real SQL string. This * practice should no longer be pursued, as such "sub-renderers" will emit / * divert {@link Clause} events. */ DATA_WRAP_DERIVED_TABLES_IN_PARENTHESES, /** * [#1905] This constant is used internally by jOOQ to indicate to * subqueries that they're being rendered in the context of a row value * expression predicate. */ DATA_ROW_VALUE_EXPRESSION_PREDICATE_SUBQUERY, /** * [#1629] The {@link Connection#getAutoCommit()} flag value before starting * a new transaction. */ DATA_DEFAULT_TRANSACTION_PROVIDER_AUTOCOMMIT, /** * [#2080] When emulating OFFSET pagination in certain databases, synthetic * aliases are generated that must be referenced also in * ORDER BY clauses, in lieu of their corresponding original * aliases. * [#8898] Oracle doesn't support aliases in RETURNING clauses. */ DATA_UNALIAS_ALIASED_EXPRESSIONS, /** * [#7139] No data must be selected in the SELECT statement. */ DATA_SELECT_NO_DATA(true, null, 1), /** * [#3381] Omit the {@link Clause#SELECT_INTO}, as it is being emulated. */ DATA_OMIT_INTO_CLAUSE, /** * [#1658] Specify whether the trailing LIMIT clause needs to be rendered. */ DATA_RENDER_TRAILING_LIMIT_IF_APPLICABLE, /** * [#13509] In some cases, it may be desirable to enforce appending a * LIMIT clause, when there's an ORDER BY * clause, e.g. to prevent the optimiser from removing the seemingly * unnecessary sort. */ DATA_FORCE_LIMIT_WITH_ORDER_BY, /** * [#3886] Whether a list has already been indented. */ DATA_LIST_ALREADY_INDENTED, /** * [#3338] [#5086] Whether a constraint is being referenced (rather than * declared). */ DATA_CONSTRAINT_REFERENCE, /** * [#1206] Whether to collect Semi / Anti JOIN. */ DATA_COLLECT_SEMI_ANTI_JOIN, /** * [#11486] An INSERT … SELECT statement. */ DATA_INSERT_SELECT, /** * [#2995] An INSERT INTO t SELECT statement. Without any * explicit column list, the SELECT statement must not be * wrapped in parentheses (which would be interpreted as the column * list's parentheses). */ DATA_INSERT_SELECT_WITHOUT_INSERT_COLUMN_LIST, /** * [#3579] [#6431] [#7222] There are nested set operations in the current * {@link Select} scope. */ DATA_NESTED_SET_OPERATIONS, /** * [#9925] In some cases the AS keyword is required for * aliasing, e.g. XML. */ DATA_AS_REQUIRED(true, null, 0), /** * [#12030] MULTISET conditions need to render the MULTISET emulation * differently to implement MULTISET semantics (ORDER agnostic) */ DATA_MULTISET_CONDITION, /** * [#12021] MULTISET content may need to be rendered differently (e.g. * nested ROW types). */ DATA_MULTISET_CONTENT, /** * [#15991] ROW content may need to be rendered differently (e.g. * nested ROW types). */ DATA_ROW_CONTENT, /** * [#12072] In some cases, it's recommended to generate an explicit * ELSE NULL clause in a CASE expression. */ DATA_FORCE_CASE_ELSE_NULL, /** * [#12092] Whether the @@group_concat_max_len value has already been * set. */ DATA_GROUP_CONCAT_MAX_LEN_SET, /** * [#11543] Whether the @@innodb_lock_wait_timeout value has already * been set. */ DATA_LOCK_WAIT_TIMEOUT_SET, /** * [#13573] We're parsing the ON CONFLICT clause, in which * the VALUES() function or EXCLUDED pseudo * table have a special semantics. */ DATA_PARSE_ON_CONFLICT, /** * [#228] [#13808] We're in a store assignment context. *

* This includes e.g. *

    *
  • INSERT columns list.
  • *
  • UPDATE … SET clause.
  • *
  • The procedural assignment statement.
  • *
*/ DATA_STORE_ASSIGNMENT, /** * [#14985] We're in a context where implicit joins are being rendered, * not the query itself. */ DATA_RENDER_IMPLICIT_JOIN, /** * [#17088] Tell the {@link RenderContext} that we're rendering for * R2DBC. */ DATA_RENDER_FOR_R2DBC ; private final boolean resetInSubqueryScope; private final Object resetValue; private final int resetThreshold; private BooleanDataKey() { this(false, null, 0); } private BooleanDataKey(boolean resetInSubqueryScope, Object resetValue, int resetThreshold) { this.resetInSubqueryScope = resetInSubqueryScope; this.resetValue = resetValue; this.resetThreshold = resetThreshold; } @Override public final boolean resetInSubqueryScope() { return resetInSubqueryScope; } @Override public final Object resetValue() { return resetValue; } @Override public final int resetThreshold() { return resetThreshold; } } /** * Keys for {@link Configuration#data()}, which may be referenced frequently * and are thus stored in an {@link EnumMap} for speedier access. */ enum SimpleDataKey implements DataKey { /** * The level of anonymous block nesting, in case we're generating a block. */ DATA_BLOCK_NESTING, /** * [#2744] Currently rendering the data change delta table syntax. *

* In some dialects, a FINAL TABLE (INSERT …) clause exists, which * corresponds to the PostgreSQL INSERT … RETURNING clause. */ DATA_RENDERING_DATA_CHANGE_DELTA_TABLE, /** * [#531] The local window definitions. *

* The window definitions declared in the WINDOW clause are * needed in the SELECT clause when emulating them by inlining * window specifications. */ DATA_WINDOW_DEFINITIONS(true, null, 0), /** * [#1629] The {@link Connection#getAutoCommit()} flag value before starting * a new transaction. */ DATA_DEFAULT_TRANSACTION_PROVIDER_SAVEPOINTS, /** * [#1629] The {@link DefaultConnectionProvider} instance to be used during * the transaction. */ DATA_DEFAULT_TRANSACTION_PROVIDER_CONNECTION, /** * [#2080] When emulating OFFSET pagination in certain databases, synthetic * aliases are generated that must be referenced also in * ORDER BY clauses, in lieu of their corresponding original * aliases. */ DATA_OVERRIDE_ALIASES_IN_ORDER_BY, /** * [#3381] The table to be used for the {@link Clause#SELECT_INTO} clause. */ DATA_SELECT_INTO_TABLE, /** * [#1206] The collected Semi / Anti JOIN predicates. */ DATA_COLLECTED_SEMI_ANTI_JOIN, /** * [#5764] Sometimes, it is necessary to prepend some SQL to the * generated SQL. *

* This needs to be done e.g. to emulate inline table valued parameters * in SQL Server: *

*


         * -- With TVP bind variable:
         * SELECT * FROM func (?)
         *
         * -- Inlining TVP bind variable:
         * DECLARE @t TABLE_TYPE;
         * INSERT INTO @t VALUES (?),(?),...,(?);
         * SELECT * FROM func (@t)
         * 
*/ DATA_PREPEND_SQL, /** * [#12092] Sometimes, it is necessary to append some SQL to the * generated SQL. *

* This needs to be done e.g. to make sure the * MySQL @@group_concat_max_len setting is set to an appropriate value, * and reset to the previous value again. *

*


         * SET @t = @@group_concat_max_len;
         * SET @@group_concat_max_len = 4294967295;
         * SELECT group_concat(...);
         * SET @@group_concat_max_len = @t;
         * 
*/ DATA_APPEND_SQL, /** * [#6583] The target table on which a DML operation operates on. */ DATA_DML_TARGET_TABLE, /** * [#6583] [#14742] The target table on which a DML operation operates on. */ DATA_DML_USING_TABLES, /** * [#8479] There is a WHERE clause to be emulated for ON DUPLICATE KEY */ DATA_ON_DUPLICATE_KEY_WHERE, /** * [#3607] [#8522] CTEs that need to be added to the top level CTE * section. */ DATA_TOP_LEVEL_CTE, /** * [#10540] Aliases to be applied to the current SELECT * statement. */ DATA_SELECT_ALIASES, ; private final boolean resetInSubqueryScope; private final Object resetValue; private final int resetThreshold; private SimpleDataKey() { this(false, null, 0); } private SimpleDataKey(boolean resetInSubqueryScope, Object resetValue, int resetThreshold) { this.resetInSubqueryScope = resetInSubqueryScope; this.resetValue = resetValue; this.resetThreshold = resetThreshold; } @Override public final boolean resetInSubqueryScope() { return resetInSubqueryScope; } @Override public final Object resetValue() { return resetValue; } @Override public final int resetThreshold() { return resetThreshold; } } /** * Keys for {@link Configuration#data()}, which may be referenced very * infrequently and are thus stored in an ordinary {@link HashMap} for a * more optimal memory layout. */ enum ExtendedDataKey implements DataKey { /** * [#4498] [#7552] The original INSERT ON DUPLICATE KEY UPDATE query * that produced a MERGE statement for its emulation. */ DATA_INSERT_ON_DUPLICATE_KEY_UPDATE, /** * [#9017] We've already transformed ROWNUM expressions to LIMIT. */ DATA_TRANSFORM_ROWNUM_TO_LIMIT, /** * [#1535] [#11851] The window function object that uses a * {@link WindowSpecification}. */ DATA_WINDOW_FUNCTION, /** * [#8893] Whether {@link TableField} should be qualified with their * tables when rendering in the current scope. */ DATA_RENDER_TABLE(true, null, 0), /** * [#15982] The base type of an empty array in the current scope. */ DATA_EMPTY_ARRAY_BASE_TYPE ; private final boolean resetInSubqueryScope; private final Object resetValue; private final int resetThreshold; private ExtendedDataKey() { this(false, null, 0); } private ExtendedDataKey(boolean resetInSubqueryScope, Object resetValue, int resetThreshold) { this.resetInSubqueryScope = resetInSubqueryScope; this.resetValue = resetValue; this.resetThreshold = resetThreshold; } @Override public final boolean resetInSubqueryScope() { return resetInSubqueryScope; } @Override public final Object resetValue() { return resetValue; } @Override public final int resetThreshold() { return resetThreshold; } } static final DataKey[] DATAKEY_RESET_IN_SUBQUERY_SCOPE; static { DATAKEY_RESET_IN_SUBQUERY_SCOPE = Stream .concat( Stream.concat(Stream.of(BooleanDataKey.values()), Stream.of(SimpleDataKey.values())), Stream.of(ExtendedDataKey.values())) .filter(t -> t.resetInSubqueryScope()) .toArray(DataKey[]::new); } // ------------------------------------------------------------------------ // Other constants // ------------------------------------------------------------------------ /** * The default escape character for [a] LIKE [b] ESCAPE […] * clauses. */ static final char ESCAPE = '!'; /** * A lock for the initialisation of other static members */ private static final Object initLock = new Object(); /** * Indicating whether JPA (jakarta.persistence) is on the * classpath. */ private static volatile JPANamespace jpaNamespace; /** * Indicating whether Kotlin (kotlin.*) is on the classpath. */ private static volatile Boolean isKotlinAvailable; private static volatile Reflect ktJvmClassMapping; private static volatile Reflect ktKClasses; private static volatile Reflect ktKClass; private static volatile Reflect ktKTypeParameter; /** * [#3696] The maximum number of consumed exceptions in * {@link #consumeExceptions(Configuration, PreparedStatement, SQLException)} * helps prevent infinite loops and {@link OutOfMemoryError}. */ static int maxConsumedExceptions = 256; static int maxConsumedResults = 65536; /** * A pattern for the dash line syntax */ private static final Pattern DASH_PATTERN = Pattern.compile("(-+)"); /** * A pattern for the pipe line syntax */ private static final Pattern PIPE_PATTERN = Pattern.compile("(?<=\\|)([^|]+)(?=\\|)"); /** * A pattern for the dash line syntax */ private static final Pattern PLUS_PATTERN = Pattern.compile("\\+(-+)(?=\\+)"); /** * All characters that are matched by Java's interpretation of \s. *

* For a more accurate set of whitespaces, refer to * http://stackoverflow.com/a/4731164/521799. In the event of SQL * processing, it is probably safe to ignore most of those alternative * Unicode whitespaces. */ private static final char[] WHITESPACE_CHARACTERS = " \t\n\u000B\f\r".toCharArray(); /** * Acceptable prefixes for JDBC escape syntax. */ private static final char[][] JDBC_ESCAPE_PREFIXES = { "{fn ".toCharArray(), "{d ".toCharArray(), "{t ".toCharArray(), "{ts ".toCharArray() }; private static final char[] TOKEN_SINGLE_LINE_COMMENT = { '-', '-' }; private static final char[] TOKEN_SINGLE_LINE_COMMENT_C = { '/', '/' }; private static final char[] TOKEN_HASH = { '#' }; private static final char[] TOKEN_MULTI_LINE_COMMENT_OPEN = { '/', '*' }; private static final char[] TOKEN_MULTI_LINE_COMMENT_CLOSE = { '*', '/' }; private static final char[] TOKEN_APOS = { '\'' }; private static final char[] TOKEN_ESCAPED_APOS = { '\'', '\'' }; /** * "Suffixes" that are placed behind a "?" character to form an operator, * rather than a JDBC bind variable. This is particularly useful to prevent * parsing PostgreSQL operators as bind variables, as can be seen here: * https://www.postgresql.org/docs/current/static/functions-json.html, * https://www.postgresql.org/docs/current/static/ltree.html, * https://www.postgresql.org/docs/current/static/functions-geometry.html. *

* [#5307] Known PostgreSQL JSON operators: *

    *
  • ?|
  • *
  • ?&
  • *
*

* [#7035] Known PostgreSQL LTREE operators: *

    *
  • ? (we cannot handle this one)
  • *
  • ?@>
  • *
  • ?<@
  • *
  • ?~
  • *
  • ?@
  • *
*

* [#7037] Known PostgreSQL Geometry operators: *

    *
  • ?#
  • *
  • ?-
  • *
  • ?|
  • *
*/ private static final char[][] NON_BIND_VARIABLE_SUFFIXES = { { '?' }, { '|' }, { '&' }, { '@' }, { '<' }, { '~' }, { '#' }, { '-' } }; /** * "Suffixes" that are placed behind a "?" character to form a JDBC bind * variable, rather than an operator. *

* [#11442] The above NON_BIND_VARIABLE_SUFFIXES leads to false positives, * such as "?<>", which is a non-equality operator, not * an operator on its own. */ private static final char[][] BIND_VARIABLE_SUFFIXES = { { '<', '>' } }; /** * All hexadecimal digits accessible through array index, e.g. * HEX_DIGITS[15] == 'f'. */ private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray(); private static final byte[] HEX_LOOKUP = { /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, /* 0x40 */ 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 */ 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static final Set REQUIRES_BACKSLASH_ESCAPING = SQLDialect.supportedBy(MARIADB, MYSQL); static final Set NO_SUPPORT_NULL = SQLDialect.supportedBy(DERBY, FIREBIRD, H2, HSQLDB, TRINO); static final Set NO_SUPPORT_NOT_NULL = SQLDialect.supportedBy(TRINO); static final Set NO_SUPPORT_BINARY_TYPE_LENGTH = SQLDialect.supportedBy(POSTGRES, TRINO, YUGABYTEDB); static final Set NO_SUPPORT_CAST_TYPE_IN_DDL = SQLDialect.supportedBy(MARIADB, MYSQL); static final Set SUPPORT_NON_BIND_VARIABLE_SUFFIXES = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB); static final Set SUPPORT_POSTGRES_LITERALS = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB); static final Set DEFAULT_BEFORE_NULL = SQLDialect.supportedBy(FIREBIRD, HSQLDB); static final Set NO_SUPPORT_TIMESTAMP_PRECISION = SQLDialect.supportedBy(DERBY, FIREBIRD); static final Set NO_SUPPORT_TIME_PRECISION = SQLDialect.supportedBy(DERBY, FIREBIRD); static final Set DEFAULT_TIMESTAMP_NOT_NULL = SQLDialect.supportedBy(MARIADB); static final Set REQUIRES_PARENTHESISED_DEFAULT = SQLDialect.supportedBy(SQLITE); static final Set REQUIRES_PARENTHESISED_DEFAULT_FOR_LOBS = SQLDialect.supportedBy(MYSQL); // ------------------------------------------------------------------------ // XXX: Record constructors and related methods // ------------------------------------------------------------------------ /** * Turn a {@link Result} into a list of {@link Row} */ static final List rows(Result result) { return map(result, r -> r.valuesRow()); } /** * Create a new record */ static final RecordDelegate newRecord(boolean fetched, Class type) { return newRecord(fetched, type, null); } /** * Create a new record. */ static final RecordDelegate newRecord(boolean fetched, Class type, AbstractRow fields) { return newRecord(fetched, type, fields, null); } /** * Create a new {@link Table} or {@link UDT} record. */ static final RecordDelegate newRecord(boolean fetched, RecordQualifier type) { return newRecord(fetched, type, null); } /** * Create a new {@link Table} or {@link UDT} record. */ static final RecordDelegate newRecord(boolean fetched, RecordQualifier type, Configuration configuration) { return newRecord( fetched, recordFactory( type, type.getRecordType(), (AbstractRow) type.fieldsRow() ), configuration ); } /** * Create a new record. */ static final RecordDelegate newRecord(boolean fetched, Class type, AbstractRow fields, Configuration configuration) { return newRecord(fetched, recordFactory(null, type, fields), configuration); } /** * Create a new record. */ static final RecordDelegate newRecord(boolean fetched, Supplier factory, Configuration configuration) { return new RecordDelegate<>(configuration, factory, fetched); } @SuppressWarnings({ "unchecked", "rawtypes" }) static final AbstractRow row0(FieldsImpl fields) { switch (fields.size()) { case 1: return new RowImpl1(fields); case 2: return new RowImpl2(fields); case 3: return new RowImpl3(fields); case 4: return new RowImpl4(fields); case 5: return new RowImpl5(fields); case 6: return new RowImpl6(fields); case 7: return new RowImpl7(fields); case 8: return new RowImpl8(fields); case 9: return new RowImpl9(fields); case 10: return new RowImpl10(fields); case 11: return new RowImpl11(fields); case 12: return new RowImpl12(fields); case 13: return new RowImpl13(fields); case 14: return new RowImpl14(fields); case 15: return new RowImpl15(fields); case 16: return new RowImpl16(fields); case 17: return new RowImpl17(fields); case 18: return new RowImpl18(fields); case 19: return new RowImpl19(fields); case 20: return new RowImpl20(fields); case 21: return new RowImpl21(fields); case 22: return new RowImpl22(fields); default: return (AbstractRow) new RowImplN(fields); } } static final AbstractRow row0(Collection> fields) { return row0(fields.toArray(EMPTY_FIELD)); } static final AbstractRow row0(Field... fields) { return row0(new FieldsImpl<>(fields)); } static final Class recordType(int length) { switch (length) { case 1: return RecordImpl1.class; case 2: return RecordImpl2.class; case 3: return RecordImpl3.class; case 4: return RecordImpl4.class; case 5: return RecordImpl5.class; case 6: return RecordImpl6.class; case 7: return RecordImpl7.class; case 8: return RecordImpl8.class; case 9: return RecordImpl9.class; case 10: return RecordImpl10.class; case 11: return RecordImpl11.class; case 12: return RecordImpl12.class; case 13: return RecordImpl13.class; case 14: return RecordImpl14.class; case 15: return RecordImpl15.class; case 16: return RecordImpl16.class; case 17: return RecordImpl17.class; case 18: return RecordImpl18.class; case 19: return RecordImpl19.class; case 20: return RecordImpl20.class; case 21: return RecordImpl21.class; case 22: return RecordImpl22.class; default: return RecordImplN.class; } } /** * Create a new record factory. */ @SuppressWarnings({ "unchecked" }) static final Supplier recordFactory( RecordQualifier qualifier, Class type, AbstractRow row ) { // An ad-hoc type resulting from a JOIN or arbitrary SELECT if (type == AbstractRecord.class || type == Record.class || InternalRecord.class.isAssignableFrom(type)) { switch (row.size()) { case 1: return () -> (R) new RecordImpl1<>(row); case 2: return () -> (R) new RecordImpl2<>(row); case 3: return () -> (R) new RecordImpl3<>(row); case 4: return () -> (R) new RecordImpl4<>(row); case 5: return () -> (R) new RecordImpl5<>(row); case 6: return () -> (R) new RecordImpl6<>(row); case 7: return () -> (R) new RecordImpl7<>(row); case 8: return () -> (R) new RecordImpl8<>(row); case 9: return () -> (R) new RecordImpl9<>(row); case 10: return () -> (R) new RecordImpl10<>(row); case 11: return () -> (R) new RecordImpl11<>(row); case 12: return () -> (R) new RecordImpl12<>(row); case 13: return () -> (R) new RecordImpl13<>(row); case 14: return () -> (R) new RecordImpl14<>(row); case 15: return () -> (R) new RecordImpl15<>(row); case 16: return () -> (R) new RecordImpl16<>(row); case 17: return () -> (R) new RecordImpl17<>(row); case 18: return () -> (R) new RecordImpl18<>(row); case 19: return () -> (R) new RecordImpl19<>(row); case 20: return () -> (R) new RecordImpl20<>(row); case 21: return () -> (R) new RecordImpl21<>(row); case 22: return () -> (R) new RecordImpl22<>(row); default: return () -> (R) new RecordImplN(row); } } // Any generated record else { try { // [#919] Allow for accessing non-public constructors final Constructor constructor = qualifier instanceof TableImpl t ? t.getRecordConstructor() : Reflect.accessible(type.getDeclaredConstructor()); return () -> { try { return constructor.newInstance(); } catch (Exception e) { throw new IllegalStateException("Could not construct new record", e); } }; } catch (Exception e) { throw new IllegalStateException("Could not construct new record", e); } } } private static final JooqLogger logResetTouchedOnNotNull = JooqLogger.getLogger(Tools.class, "logResetTouchedOnNotNull", 5); /** * [#2700] [#3582] If a POJO attribute is NULL, but the column is NOT NULL * then we should let the database apply DEFAULT values */ @SuppressWarnings({ "unchecked", "rawtypes" }) static final void resetChangedOnNotNull(Record record) { int size = record.size(); ConverterContext c = null; for (int i = 0; i < size; i++) { Field field = record.field(i); // [#17224] Reset the touched value only if T typed value is null, not U type! // [#17272] Read only converters (such as Field.convertFrom()) can't convert back to the T type try { if (!field.getDataType().nullable() && field.getConverter().toSupported() && scoped(field.getConverter()).to(record.get(i), c == null ? c = converterContext(record) : c) == null ) record.changed(i, false); } catch (Exception e) { logResetTouchedOnNotNull.warn( "Exception in Converter", """ An exception when calling in {converter}.to(). If this converter doesn't support the to() method, it is recommended to override Converter.toSupported() to prevent this call. """.replace("{converter}", field.getConverter().getClass().getName()), e ); } } } /** * Get an attachable's configuration or a new {@link DefaultConfiguration} * if null. */ static final Configuration configurationOrThrow(Attachable attachable) { if (attachable.configuration() == null) throw new DetachedException("No configuration attached: " + attachable); else return configuration(attachable.configuration()); } /** * Get an attachable's configuration or a new {@link DefaultConfiguration} * if null. */ static final Configuration configuration(Attachable attachable) { return configuration(attachable.configuration()); } /** * Get a configuration or a new {@link DefaultConfiguration} if * null. */ static final Configuration configuration(Configuration configuration) { return configuration != null ? configuration : new DefaultConfiguration(); } /** * Get a configuration or a new {@link DefaultConfiguration} if * null. */ static final Configuration configuration(Scope scope) { return configuration(scope != null ? scope.configuration() : null); } /** * Get a converter from a {@link ConverterProvider} or null if * no converter could be provided. */ static final ContextConverter converter(Configuration configuration, T instance, Class tType, Class uType) { Converter result = configuration(configuration).converterProvider().provide(tType, uType); if (result == null) result = CONFIG.get().converterProvider().provide(tType, uType); // [#11823] [#12208] The new ad-hoc conversion API tries to avoid the Class literal // meaning there are perfectly reasonable API usages when using MULTISET // where we can't decide on a converter prior to having an actual result // type - so, let's try again if we have the result value. if (result == null && tType == Converters.UnknownType.class) result = converter(configuration, instance, (Class) (instance == null ? Object.class : instance.getClass()), uType); return result == null ? null : scoped(result); } /** * Get a converter from a {@link ConverterProvider} or null if * no converter could be provided. */ static final ContextConverter converterOrFail(Configuration configuration, T instance, Class tType, Class uType) { ContextConverter result = converter(configuration, instance, tType, uType); if (result == null) throw new DataTypeException("No Converter found for types " + tType.getName() + " and " + uType.getName()); return result; } /** * Get a converter from a {@link ConverterProvider}. */ static final ContextConverter converterOrFail(Attachable attachable, T instance, Class tType, Class uType) { return converterOrFail(configuration(attachable), instance, tType, uType); } /** * Get a configuration's settings or default settings if the configuration * is null. */ static final Settings settings(Attachable attachable) { return configuration(attachable).settings(); } /** * Get a configuration's settings or default settings if the configuration * is null. */ static final Settings settings(Configuration configuration) { return configuration(configuration).settings(); } static final T attach(Attachable attachable, Configuration configuration, Supplier supplier) { Configuration previous = attachable.configuration(); try { attachable.attach(configuration); return supplier.get(); } finally { attachable.attach(previous); } } static final boolean attachRecords(Configuration configuration) { if (configuration != null) { Settings settings = configuration.settings(); if (settings != null) return !FALSE.equals(settings.isAttachRecords()); } return true; } static final Field[] fieldArray(Collection> fields) { return fields == null ? null : fields.toArray(EMPTY_FIELD); } // ------------------------------------------------------------------------ // XXX: Data-type related methods // ------------------------------------------------------------------------ static final DataType[] dataTypes(Class[] types) { return map(types, t -> t != null ? getDataType(t) : getDataType(Object.class), DataType[]::new); } // ------------------------------------------------------------------------ // XXX: General utility methods // ------------------------------------------------------------------------ private static final int FIELD_NAME_CACHE_SIZE = 128; private static final String[] FIELD_NAME_STRINGS; private static final Name[] FIELD_NAMES; static { FIELD_NAME_STRINGS = IntStream.range(0, FIELD_NAME_CACHE_SIZE).mapToObj(Tools::fieldNameString0).toArray(String[]::new); FIELD_NAMES = IntStream.range(0, FIELD_NAME_CACHE_SIZE).mapToObj(i -> name(FIELD_NAME_STRINGS[i])).toArray(Name[]::new); } static final SortField sortField(OrderField field) { if (field instanceof SortField s) return s; else if (field instanceof Field f) return f.sortDefault(); else throw new IllegalArgumentException("Field not supported : " + field); } static final SortField[] sortFields(OrderField[] fields) { if (fields instanceof SortField[] s) return s; else return map(fields, o -> sortField(o), SortField[]::new); } static final List> sortFields(Collection> fields) { return Tools.map(fields, (OrderField o) -> sortField(o)); } // TODO: Check if these field names are ever really needed, or if we can just use the C field names private static final String fieldNameString0(int index) { return "v" + index; } static final String fieldNameString(int index) { return index < FIELD_NAME_CACHE_SIZE ? FIELD_NAME_STRINGS[index] : fieldNameString0(index); } static final Name fieldName(int index) { return index < FIELD_NAME_CACHE_SIZE ? FIELD_NAMES[index] : name(fieldNameString0(index)); } static final Name[] fieldNames(int length) { Name[] result = new Name[length]; for (int i = 0; i < length; i++) result[i] = fieldName(i); return result; } static final String[] fieldNameStrings(int length) { String[] result = new String[length]; for (int i = 0; i < length; i++) result[i] = fieldNameString(i); return result; } static final Field[] fields(int length) { return fields(length, SQLDataType.OTHER); } @SuppressWarnings("unchecked") static final Field[] fields(int length, DataType type) { Field[] result = new Field[length]; for (int i = 0; i < length; i++) result[i] = DSL.field(fieldName(i), type); return result; } static final boolean reference(Field field) { return field instanceof TableField || field instanceof SQLField && ((SQLField) field).delegate.isName; } static final Field unqualified(Field field) { return field instanceof TableField ? DSL.field(field.getUnqualifiedName(), field.getDataType()) : field; } static final SortField unqualified(SortField field) { return field.$field(unqualified(field.$field())); } static final List> unaliasedFields(Collection> fields) { return map(fields, (f, i) -> DSL.field(fieldName(i), f.getDataType()).as(f)); } static final ReferenceImpl aliasedKey(ForeignKey key, Table child, Table parent) { // [#10603] [#5050] TODO: Solve aliasing constraints more generically // [#8762] We can't dereference child.fields() or parent.fields() here yet, because this method is being called by // the TableImpl constructor, meaning the fields are not initialised yet. return new ReferenceImpl<>( child, key.getQualifiedName(), Tools.fieldsByName(child, key.getFieldsArray()), key.getKey(), Tools.fieldsByName(parent, key.getKeyFieldsArray()), key.enforced() ); } static final List> aliasedFields(Collection> fields) { return map(fields, (f, i) -> f.as(fieldName(i))); } static final List> fieldsByName(String[] fieldNames) { return fieldsByName(null, fieldNames); } static final Field[] fieldsByName(Name tableName, int length) { Field[] result = new Field[length]; if (tableName == null) for (int i = 0; i < length; i++) result[i] = DSL.field(fieldName(i)); else for (int i = 0; i < length; i++) result[i] = DSL.field(name(tableName, fieldName(i))); return result; } @SuppressWarnings("unchecked") static final TableField[] fieldsByName(Table tableName, Field[] fieldNames) { if (tableName == null) return map(fieldNames, n -> (TableField) DSL.field(n.getUnqualifiedName(), n.getDataType()), TableField[]::new); else return map(fieldNames, n -> (TableField) DSL.field(tableName.getQualifiedName().append(n.getUnqualifiedName()), n.getDataType()), TableField[]::new); } static final List> fieldsByName(Name tableName, Name[] fieldNames) { if (tableName == null) return map(fieldNames, n -> DSL.field(n)); else return map(fieldNames, n -> DSL.field(name(tableName, n))); } static final List> fieldsByName(String tableName, String[] fieldNames) { if (StringUtils.isEmpty(tableName)) return map(fieldNames, n -> DSL.field(name(n))); else return map(fieldNames, n -> DSL.field(name(tableName, n))); } static final List> fieldsByName(Name[] names) { return map(names, n -> DSL.field(n)); } static final Name[] names(String[] names) { return map(names, n -> DSL.name(n), Name[]::new); } static final List names(Collection names) { return map(names, n -> n instanceof Name name ? name : DSL.name(String.valueOf(n))); } static final String sanitiseName(Configuration configuration, String name) { switch (configuration.family()) { default: return name; } } static final List> jsonEntries(Field[] entries) { return Tools.map(entries, (Field f) -> DSL.jsonEntry(f)); } private static final IllegalArgumentException fieldExpected(Object value) { return new IllegalArgumentException("Cannot interpret argument of type " + value.getClass() + " as a Field: " + value); } /** * [#461] [#473] [#2597] [#8234] Some internals need a cast only if necessary. */ @SuppressWarnings("unchecked") static final Field[] castAllIfNeeded(Field[] fields, Class type) { Field[] castFields = new Field[fields.length]; for (int i = 0; i < fields.length; i++) castFields[i] = castIfNeeded(fields[i], type); return castFields; } /** * [#461] [#473] [#2597] [#8234] Some internals need a cast only if necessary. */ @SuppressWarnings("unchecked") static final Field[] castAllIfNeeded(Field[] fields, DataType type) { Field[] castFields = new Field[fields.length]; for (int i = 0; i < fields.length; i++) castFields[i] = castIfNeeded(fields[i], type); return castFields; } /** * [#461] [#473] [#2597] [#8234] Some internals need a cast only if necessary. */ @SuppressWarnings("unchecked") static final Field castIfNeeded(Field field, Class type) { if (field.getType().equals(type)) return (Field) field; else return field.cast(type); } /** * [#461] [#473] [#2597] [#8234] Some internals need a cast only if necessary. */ @SuppressWarnings("unchecked") static final Field castIfNeeded(Field field, DataType type) { if (field.getDataType().equals(type)) return (Field) field; else return field.cast(type); } /** * [#461] [#473] [#2597] [#8234] Some internals need a cast only if necessary. */ @SuppressWarnings("unchecked") static final Field castIfNeeded(Field field, Field type) { if (field.getDataType().equals(type.getDataType())) return (Field) field; else return field.cast(type); } static final Field castNullLiteralIfNeeded(Context ctx, Field field) { switch (ctx.family()) { case HSQLDB: case POSTGRES: case YUGABYTEDB: // [#16367] The NULL literal defaults to type TEXT in PostgreSQL. // if we know the data type, we should cast it explicitly if (ctx.subquery()) { Field f = unalias(field); if (isInlineVal1(ctx, f, v -> v == null) && !f.getDataType().isOther() && !f.getDataType().isString() ) { Field cast = f.cast(f.getDataType()); return f == field ? cast : cast.as(field); } } break; } return field; } // The following overloads help performance by avoiding runtime data type lookups // ------------------------------------------------------------------------------ static final Param field(byte value) { return val((Object) value, SQLDataType.TINYINT); } static final Param field(Byte value) { return val((Object) value, SQLDataType.TINYINT); } static final Param field(UByte value) { return val((Object) value, SQLDataType.TINYINTUNSIGNED); } static final Param field(short value) { return val((Object) value, SQLDataType.SMALLINT); } static final Param field(Short value) { return val((Object) value, SQLDataType.SMALLINT); } static final Param field(UShort value) { return val((Object) value, SQLDataType.SMALLINTUNSIGNED); } static final Param field(int value) { return val((Object) value, SQLDataType.INTEGER); } static final Param field(Integer value) { return val((Object) value, SQLDataType.INTEGER); } static final Param field(UInteger value) { return val((Object) value, SQLDataType.INTEGERUNSIGNED); } static final Param field(long value) { return val((Object) value, SQLDataType.BIGINT); } static final Param field(Long value) { return val((Object) value, SQLDataType.BIGINT); } static final Param field(ULong value) { return val((Object) value, SQLDataType.BIGINTUNSIGNED); } static final Param field(float value) { return val((Object) value, SQLDataType.REAL); } static final Param field(Float value) { return val((Object) value, SQLDataType.REAL); } static final Param field(double value) { return val((Object) value, SQLDataType.DOUBLE); } static final Param field(Double value) { return val((Object) value, SQLDataType.DOUBLE); } static final Param field(boolean value) { return val((Object) value, SQLDataType.BOOLEAN); } static final Param field(Boolean value) { return val((Object) value, SQLDataType.BOOLEAN); } static final Param field(BigDecimal value) { return val((Object) value, SQLDataType.DECIMAL); } static final Param field(BigInteger value) { return val((Object) value, SQLDataType.DECIMAL_INTEGER); } static final Param field(byte[] value) { return val((Object) value, SQLDataType.VARBINARY); } static final Param field(String value) { return val((Object) value, SQLDataType.VARCHAR); } static final Param field(Date value) { return val((Object) value, SQLDataType.DATE); } static final Param