com.nimbusds.infinispan.persistence.sql.JOOQFixes Maven / Gradle / Ivy
package com.nimbusds.infinispan.persistence.sql;
import com.google.common.base.Splitter;
import net.jcip.annotations.ThreadSafe;
import org.infinispan.persistence.spi.PersistenceException;
import org.jooq.Field;
import org.jooq.Merge;
import org.jooq.SQLDialect;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Fixes incorrect SQL statements generated by jOOQ.
*/
@ThreadSafe
class JOOQFixes {
/**
* The SQL dialects that require a merge fix.
*/
static final Set REQUIRE_MERGE_FIX = Set.of(SQLDialect.POSTGRES_9_3, SQLDialect.POSTGRES_9_4, SQLDialect.POSTGRES_9_5);
/**
* Flag if an PostgreSQL merge statement fix is required.
*/
private final boolean pgMergeFixRequired;
/**
* Flag if an Oracle CLOB literal fix is required.
*/
private final boolean oracleClobFixRequired;
/**
* The cutoff length of Oracle string literals. The Oracle
* {@code MAX_STRING_SIZE = STANDARD = 4000 bytes}, halve to allow for
* a mix UTF multibyte chars.
*
* See https://docs.oracle.com/database/121/SQLRF/sql_elements001.htm#SQLRF55623
*/
static final int ORACLE_STRING_LITERAL_CUTOFF_LENGTH = 2000;
/**
* The primary key constraint name, {@code null} if N/A.
*/
private final String pkConstraintName;
/**
* Creates a new instance for applying fixes to jOOQ generated SQL
* statements.
*
* @param sqlDialect The configured SQL dialect. Must not be
* {@code null}.
* @param createTableStmt The create table SQL statement. Must not be
* {@code null}.
*/
public JOOQFixes(final SQLDialect sqlDialect, final String createTableStmt) {
if (REQUIRE_MERGE_FIX.contains(sqlDialect)) {
pkConstraintName = parsePrimaryKeyConstraintName(createTableStmt);
if (pkConstraintName == null) {
throw new PersistenceException("Couldn't determine primary key constraint name from SQL create table statement: " + createTableStmt);
}
pgMergeFixRequired = true;
} else {
pgMergeFixRequired = false;
pkConstraintName = null;
}
oracleClobFixRequired = SQLDialect.ORACLE.equals(sqlDialect);
}
/**
* Fixes the specified SQL merge statement for PostgreSQL databases.
*
* @param mergeStatement The ready merge statement. Must not be
* {@code null}.
*
* @return The (fixed) SQL statement ready for execution.
*/
String fixMergeStatement(final Merge mergeStatement) {
if (! pgMergeFixRequired) {
return mergeStatement.toString();
}
return mergeStatement.toString().replace("on conflict ([unknown primary key])", "on conflict on constraint " + pkConstraintName);
}
/**
* Parses the primary key constraint name from the specified create
* table SQL statement.
*
* @param createTableStatement The create table SQL statement.
*
* @return The primary key constraint name, {@code null} if missing or
* not found.
*/
static String parsePrimaryKeyConstraintName(final String createTableStatement) {
Pattern p = Pattern.compile(".*CONSTRAINT\\s+(\\w*)\\s+PRIMARY", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
Matcher m = p.matcher(createTableStatement);
if (! m.find()) {
return null;
}
return m.group(1);
}
/**
* Prepares an optional Oracle database write with CLOBs.
*
* @param sqlRecord The SQL record.
* @param chunksMap The chunks map to set for later use by
* {@link #completeOracleWriteClob}.
*
* @return The potentially modified SQL record.
*/
SQLRecord prepareOracleWriteCLOB(final SQLRecord sqlRecord, final AtomicReference