Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.army.jdbd.JdbdStmtExecutor Maven / Gradle / Ivy
/*
* Copyright 2023-2043 the original author or authors.
*
* 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.
*/
package io.army.jdbd;
import io.army.ArmyException;
import io.army.bean.ObjectAccessor;
import io.army.bean.ObjectAccessorFactory;
import io.army.criteria.SQLParam;
import io.army.criteria.Selection;
import io.army.env.SqlLogMode;
import io.army.mapping.MappingEnv;
import io.army.mapping.MappingType;
import io.army.meta.FieldMeta;
import io.army.meta.PrimaryFieldMeta;
import io.army.meta.ServerMeta;
import io.army.meta.TypeMeta;
import io.army.reactive.ReactiveStmtOption;
import io.army.reactive.executor.ReactiveExecutor;
import io.army.reactive.executor.ReactiveLocalExecutor;
import io.army.reactive.executor.ReactiveRmExecutor;
import io.army.session.*;
import io.army.session.executor.ExecutorSupport;
import io.army.session.executor.StmtExecutor;
import io.army.session.record.CurrentRecord;
import io.army.session.record.ResultItem;
import io.army.session.record.ResultStates;
import io.army.sqltype.ArmyType;
import io.army.sqltype.DataType;
import io.army.sqltype.SqlType;
import io.army.stmt.*;
import io.army.type.BlobPath;
import io.army.type.ImmutableSpec;
import io.army.type.TextPath;
import io.army.util._Collections;
import io.army.util._Exceptions;
import io.army.util._StringUtils;
import io.army.util._TimeUtils;
import io.jdbd.JdbdException;
import io.jdbd.meta.JdbdType;
import io.jdbd.result.CurrentRow;
import io.jdbd.result.DataRow;
import io.jdbd.result.ResultRowMeta;
import io.jdbd.session.DatabaseSession;
import io.jdbd.session.LocalDatabaseSession;
import io.jdbd.session.RmDatabaseSession;
import io.jdbd.session.SavePoint;
import io.jdbd.statement.BindStatement;
import io.jdbd.statement.ParametrizedStatement;
import io.jdbd.util.SqlLogger;
import org.slf4j.Logger;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import javax.annotation.Nullable;
import java.lang.reflect.Constructor;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.*;
import java.time.temporal.Temporal;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* This class is a abstract implementation of {@link ReactiveExecutor} with jdbd spi.
*
This class is base class of following jdbd executor:
*
* {@link MySQLStmtExecutor}
* {@link PostgreStmtExecutor}
*
* Following is chinese signature:
* 当你在阅读这段代码时,我才真正在写这段代码,你阅读到哪里,我便写到哪里.
*
* @see JdbdStmtExecutorFactory
* @see jdbd-spi
*/
abstract class JdbdStmtExecutor extends JdbdExecutorSupport
implements ReactiveExecutor,
ReactiveExecutor.LocalTransactionSpec,
ReactiveExecutor.XaTransactionSpec,
Session.XaTransactionSupportSpec {
final JdbdStmtExecutorFactory factory;
final DatabaseSession session;
final String sessionName;
private final SqlLogger jdbdSqlLogger;
private Set> optionSet;
JdbdStmtExecutor(JdbdStmtExecutorFactory factory, DatabaseSession session, String sessionName) {
this.sessionName = sessionName;
this.factory = factory;
this.session = session;
if (readSqlLogMode(factory) == SqlLogMode.OFF) {
this.jdbdSqlLogger = null;
} else {
this.jdbdSqlLogger = this::jdbdSqlLogger;
}
}
@Override
public final long sessionIdentifier() throws DataAccessException {
try {
return this.session.sessionIdentifier();
} catch (Exception e) {
throw wrapExecutingError(e);
}
}
@Override
public final boolean inTransaction() throws DataAccessException {
try {
return this.session.inTransaction();
} catch (JdbdException e) {
throw wrapExecutingError(e);
}
}
@Override
public final boolean isSameFactory(StmtExecutor s) {
return s instanceof JdbdStmtExecutor && ((JdbdStmtExecutor) s).factory == this.factory;
}
@Override
public final boolean isDriverAssignableTo(Class> spiClass) {
return spiClass.isAssignableFrom(this.session.getClass());
}
@Override
public final T getDriverSpi(Class spiClass) {
return spiClass.cast(this.session);
}
@Override
public final Mono transactionInfo() {
final Function, ?> optionFunc;
optionFunc = addJdbdLogOptionIfNeed(io.jdbd.session.Option.EMPTY_OPTION_FUNC);
return Mono.from(this.session.transactionInfo(optionFunc))
.map(this::mapToArmyTransactionInfo)
.onErrorMap(this::wrapExecuteIfNeed);
}
@Override
public final Mono sessionTransactionCharacteristics(final Function, ?> optionFunc) {
return Mono.from(this.session.sessionTransactionCharacteristics(mapToJdbdOptionFunc(optionFunc)))
.map(this::mapToArmyTransactionInfo)
.onErrorMap(this::wrapExecuteIfNeed);
}
@Override
public final Mono setTransactionCharacteristics(TransactionOption option) {
final io.jdbd.session.TransactionOption jdbdOption;
try {
jdbdOption = mapToJdbdTransactionOption(option);
} catch (Throwable e) {
return Mono.error(wrapExecuteIfNeed(e));
}
return Mono.from(this.session.setTransactionCharacteristics(jdbdOption))
.onErrorMap(this::wrapExecuteIfNeed)
.then();
}
@Override
public final Mono> setSavePoint(Function, ?> optionFunc) {
return Mono.from(this.session.setSavePoint(mapToJdbdOptionFunc(optionFunc)))
.onErrorMap(this::wrapExecuteIfNeed);
}
@Override
public final Mono releaseSavePoint(Object savepoint, Function, ?> optionFunc) {
if (!(savepoint instanceof SavePoint)) {
return Mono.error(_Exceptions.unknownSavePoint(savepoint));
}
return Mono.from(this.session.releaseSavePoint((SavePoint) savepoint, mapToJdbdOptionFunc(optionFunc)))
.onErrorMap(this::wrapExecuteIfNeed)
.then();
}
@Override
public final Mono rollbackToSavePoint(Object savepoint, Function, ?> optionFunc) {
if (!(savepoint instanceof SavePoint)) {
return Mono.error(_Exceptions.unknownSavePoint(savepoint));
}
return Mono.from(this.session.rollbackToSavePoint((SavePoint) savepoint, mapToJdbdOptionFunc(optionFunc)))
.onErrorMap(this::wrapExecuteIfNeed)
.then();
}
@Override
public final Mono insert(final SimpleStmt stmt, final ReactiveStmtOption option) {
final List extends Selection> selectionList = stmt.selectionList();
final boolean returningId;
returningId = selectionList.size() == 1 && selectionList.get(0) instanceof PrimaryFieldMeta;
final AtomicReference jdbdStatesHolder;
final Function extractIdFunc;
final Supplier> monoSupplier;
if (returningId) {
final GeneratedKeyStmt keyStmt = (GeneratedKeyStmt) stmt;
final MappingType type = keyStmt.idField().mappingType();
final DataType dataType = type.map(this.factory.serverMeta);
jdbdStatesHolder = new AtomicReference<>(null);
final int rowSize = keyStmt.rowSize();
final int[] rowIndexHolder = new int[]{0};
extractIdFunc = row -> {
Object idValue;
idValue = get(row, 0, type, dataType);
final int rowIndex = rowIndexHolder[0]++;
if (idValue == null) {
throw _Exceptions.idValueIsNull(rowIndex, keyStmt.idField());
}
idValue = type.afterGet(dataType, this.factory.mappingEnv, idValue);
keyStmt.setGeneratedIdValue(rowIndex, idValue);
if (row.rowNumber() != rowIndexHolder[0]) {
String m = String.format("jdbd row index error,expected %s but %s", rowIndexHolder[0], row.rowNumber());
throw new DataAccessException(m);
}
return Boolean.TRUE;
};
monoSupplier = () -> {
if (rowSize != rowIndexHolder[0]) {
return Mono.error(_Exceptions.insertedRowsAndGenerateIdNotMatch(rowSize, rowIndexHolder[0]));
}
return Mono.just(mapToArmyResultStates(jdbdStatesHolder.get()));
};
} else {
extractIdFunc = null;
jdbdStatesHolder = null;
monoSupplier = null;
}
final BindStatement statement;
try {
statement = bindStatement(stmt, option);
} catch (Throwable e) {
return Mono.error(wrapExecuteIfNeed(e));
}
final Mono mono;
if (returningId) {
mono = Flux.from(statement.executeQuery(extractIdFunc, jdbdStatesHolder::set))
.then(Mono.defer(monoSupplier));
} else if (stmt instanceof GeneratedKeyStmt) {
mono = Mono.from(statement.executeUpdate())
.map(states -> handleInsertStates(states, (GeneratedKeyStmt) stmt));
} else {
mono = Mono.from(statement.executeUpdate())
.map(this::mapToArmyResultStates);
}
return mono.onErrorMap(this::wrapExecuteIfNeed);
}
@Override
public final Mono update(final SimpleStmt stmt, final ReactiveStmtOption option,
Function, ?> optionFunc) {
Mono mono;
try {
final BindStatement statement;
statement = bindStatement(stmt, option);
mono = Mono.from(statement.executeUpdate())
.map(this::mapToArmyResultStates)
.onErrorMap(this::wrapExecuteIfNeed);
} catch (Throwable e) {
mono = Mono.error(wrapExecuteIfNeed(e));
}
return mono;
}
@Override
public final Flux batchUpdate(final BatchStmt stmt, final ReactiveStmtOption option, Function, ?> optionFunc) {
Flux flux;
try {
final BindStatement statement;
statement = bindStatement(stmt, option);
flux = Flux.from(statement.executeBatchUpdate())
.map(this::mapToArmyResultStates)
.onErrorMap(this::wrapExecuteIfNeed);
} catch (Throwable e) {
flux = Flux.error(wrapExecuteIfNeed(e));
}
return flux;
}
@Override
public final Flux query(final SingleSqlStmt stmt, final Class resultClass, final ReactiveStmtOption option) {
Flux flux;
try {
flux = executeQuery(stmt, mapBeanFunc(stmt, resultClass), option);
} catch (Throwable e) {
flux = Flux.error(wrapExecuteIfNeed(e));
}
return flux;
}
@Override
public final Flux> queryOptional(SingleSqlStmt stmt, final Class resultClass, ReactiveStmtOption option) {
Flux> flux;
try {
final List extends Selection> selectionList;
selectionList = stmt.selectionList();
if (selectionList.size() != 1) {
return Flux.error(new IllegalArgumentException("queryOptional method support only single selection"));
}
final OptionalSingleColumnRowReader rowReader;
rowReader = new OptionalSingleColumnRowReader<>(this, selectionList, resultClass);
final Function> function;
if (stmt instanceof GeneratedKeyStmt) {
function = returnIdQueryRowFunc((GeneratedKeyStmt) stmt, rowReader);
} else {
function = rowReader::readOneRow;
}
flux = executeQuery(stmt, function, option);
} catch (Throwable e) {
flux = Flux.error(wrapExecuteIfNeed(e));
}
return flux;
}
@Override
public final Flux queryObject(SingleSqlStmt stmt, Supplier constructor, ReactiveStmtOption option) {
Flux flux;
try {
flux = executeQuery(stmt, mapObjectFunc(stmt, constructor), option);
} catch (Throwable e) {
flux = Flux.error(wrapExecuteIfNeed(e));
}
return flux;
}
@Override
public final Flux queryRecord(SingleSqlStmt stmt, Function function, ReactiveStmtOption option) {
Flux flux;
try {
final Consumer armyConsumer;
armyConsumer = option.stateConsumer();
final Consumer jdbdConsumer;
if (armyConsumer == ResultStates.IGNORE_STATES) {
jdbdConsumer = io.jdbd.result.ResultStates.IGNORE_STATES;
} else {
jdbdConsumer = states -> {
final ResultStates armyStates;
armyStates = new JdbdResultStates(states, this.factory, true);
try {
armyConsumer.accept(armyStates);
} catch (Exception e) {
String m = String.format("%s %s throw error, %s", ResultStates.class.getName(),
armyConsumer, e.getMessage());
throw new ArmyException(m);
}
};
}
flux = Flux.from(bindStatement(stmt, option).executeQuery(mapRecordFunc(stmt, function), jdbdConsumer))
.onErrorMap(this::wrapExecuteIfNeed);
} catch (Throwable e) {
flux = Flux.error(wrapExecuteIfNeed(e));
}
return flux;
}
@Override
public final Flux secondQuery(TwoStmtQueryStmt stmt, ReactiveStmtOption option, List resultList) {
Flux flux;
try {
final SecondRowReader rowReader;
rowReader = new SecondRowReader<>(this, stmt, resultList);
flux = executeQuery(stmt, rowReader::readOneRow, option);
} catch (Throwable e) {
flux = Flux.error(wrapExecuteIfNeed(e));
}
return flux;
}
/*-------------------below local transaction methods -------------------*/
@Override
public final Mono startTransaction(final TransactionOption option, final HandleMode mode) {
if (!(this instanceof ReactiveLocalExecutor)) {
return Mono.error(new UnsupportedOperationException());
}
final io.jdbd.session.HandleMode jdbdMode;
switch (mode) {
case ERROR_IF_EXISTS:
jdbdMode = io.jdbd.session.HandleMode.ERROR_IF_EXISTS;
break;
case ROLLBACK_IF_EXISTS:
jdbdMode = io.jdbd.session.HandleMode.ROLLBACK_IF_EXISTS;
break;
case COMMIT_IF_EXISTS:
jdbdMode = io.jdbd.session.HandleMode.COMMIT_IF_EXISTS;
break;
default:
throw _Exceptions.unexpectedEnum(mode);
}
final io.jdbd.session.TransactionOption jdbdOption;
try {
jdbdOption = mapToJdbdTransactionOption(option);
} catch (Throwable e) {
return Mono.error(wrapExecuteIfNeed(e));
}
return Mono.from(((LocalDatabaseSession) this.session).startTransaction(jdbdOption, jdbdMode))
.map(this::mapToArmyTransactionInfo)
.onErrorMap(this::wrapExecuteIfNeed);
}
@Override
public final Mono> commit(final Function, ?> optionFunc) {
if (!(this instanceof ReactiveLocalExecutor)) {
return Mono.error(new UnsupportedOperationException());
}
return Mono.from(((LocalDatabaseSession) this.session).commit(mapToJdbdOptionFunc(optionFunc)))
.map(this::mapToArmyOptionalTransactionInfo)
.onErrorMap(this::wrapExecuteIfNeed);
}
@Override
public final Mono> rollback(final Function, ?> optionFunc) {
if (!(this instanceof ReactiveLocalExecutor)) {
return Mono.error(new UnsupportedOperationException());
}
return Mono.from(((LocalDatabaseSession) this.session).rollback(mapToJdbdOptionFunc(optionFunc)))
.map(this::mapToArmyOptionalTransactionInfo)
.onErrorMap(this::wrapExecuteIfNeed);
}
/*-------------------below XA transaction methods -------------------*/
@Override
public final Mono start(Xid xid, int flags, TransactionOption option) {
if (!(this instanceof ReactiveRmExecutor)) {
return Mono.error(new UnsupportedOperationException());
}
final io.jdbd.session.Xid jdbdXid;
final io.jdbd.session.TransactionOption jdbdOption;
try {
jdbdXid = mapToJdbdXid(xid);
jdbdOption = mapToJdbdTransactionOption(option);
} catch (Throwable e) {
return Mono.error(wrapExecuteIfNeed(e));
}
return Mono.from(((RmDatabaseSession) this.session).start(jdbdXid, flags, jdbdOption))
.map(this::mapToArmyTransactionInfo)
.onErrorMap(this::wrapExecuteIfNeed);
}
@Override
public final Mono end(Xid xid, int flags, Function, ?> optionFunc) {
if (!(this instanceof ReactiveRmExecutor)) {
return Mono.error(new UnsupportedOperationException());
}
final io.jdbd.session.Xid jdbdXid;
try {
jdbdXid = mapToJdbdXid(xid);
} catch (Throwable e) {
return Mono.error(wrapExecuteIfNeed(e));
}
return Mono.from(((RmDatabaseSession) this.session).end(jdbdXid, flags, mapToJdbdOptionFunc(optionFunc)))
.map(this::mapToArmyTransactionInfo)
.onErrorMap(this::wrapExecuteIfNeed);
}
@Override
public final Mono prepare(Xid xid, Function, ?> optionFunc) {
if (!(this instanceof ReactiveRmExecutor)) {
return Mono.error(new UnsupportedOperationException());
}
final io.jdbd.session.Xid jdbdXid;
try {
jdbdXid = mapToJdbdXid(xid);
} catch (Throwable e) {
return Mono.error(wrapExecuteIfNeed(e));
}
return Mono.from(((RmDatabaseSession) this.session).prepare(jdbdXid, mapToJdbdOptionFunc(optionFunc)))
.onErrorMap(this::wrapExecuteIfNeed);
}
@Override
public final Mono commit(Xid xid, int flags, Function, ?> optionFunc) {
if (!(this instanceof ReactiveRmExecutor)) {
return Mono.error(new UnsupportedOperationException());
}
final io.jdbd.session.Xid jdbdXid;
try {
jdbdXid = mapToJdbdXid(xid);
} catch (Throwable e) {
return Mono.error(wrapExecuteIfNeed(e));
}
return Mono.from(((RmDatabaseSession) this.session).commit(jdbdXid, flags, mapToJdbdOptionFunc(optionFunc)))
.onErrorMap(this::wrapExecuteIfNeed)
.then();
}
@Override
public final Mono rollback(Xid xid, Function, ?> optionFunc) {
if (!(this instanceof ReactiveRmExecutor)) {
return Mono.error(new UnsupportedOperationException());
}
final io.jdbd.session.Xid jdbdXid;
try {
jdbdXid = mapToJdbdXid(xid);
} catch (Throwable e) {
return Mono.error(wrapExecuteIfNeed(e));
}
return Mono.from(((RmDatabaseSession) this.session).rollback(jdbdXid, mapToJdbdOptionFunc(optionFunc)))
.onErrorMap(this::wrapExecuteIfNeed)
.then();
}
@Override
public final Mono forget(Xid xid, Function, ?> optionFunc) {
if (!(this instanceof ReactiveRmExecutor)) {
return Mono.error(new UnsupportedOperationException());
}
final io.jdbd.session.Xid jdbdXid;
try {
jdbdXid = mapToJdbdXid(xid);
} catch (Throwable e) {
return Mono.error(wrapExecuteIfNeed(e));
}
return Mono.from(((RmDatabaseSession) this.session).forget(jdbdXid, mapToJdbdOptionFunc(optionFunc)))
.onErrorMap(this::wrapExecuteIfNeed)
.then();
}
@Override
public final Flux> recover(int flags, Function, ?> optionFunc) {
if (!(this instanceof ReactiveRmExecutor)) {
return Flux.error(new UnsupportedOperationException());
}
return Flux.from(((RmDatabaseSession) this.session).recover(flags, mapToJdbdOptionFunc(optionFunc)))
.map(this::mapToOptionalArmyXid)
.onErrorMap(this::wrapExecuteIfNeed);
}
@Override
public final boolean isSupportForget() {
if (!(this instanceof ReactiveRmExecutor)) {
throw new UnsupportedOperationException();
}
return ((RmDatabaseSession) this.session).isSupportForget();
}
@Override
public final int startSupportFlags() {
if (!(this instanceof ReactiveRmExecutor)) {
throw new UnsupportedOperationException();
}
return ((RmDatabaseSession) this.session).startSupportFlags();
}
@Override
public final int endSupportFlags() {
if (!(this instanceof ReactiveRmExecutor)) {
throw new UnsupportedOperationException();
}
return ((RmDatabaseSession) this.session).endSupportFlags();
}
@Override
public final int commitSupportFlags() {
if (!(this instanceof ReactiveRmExecutor)) {
throw new UnsupportedOperationException();
}
return ((RmDatabaseSession) this.session).commitSupportFlags();
}
@Override
public final int recoverSupportFlags() {
if (!(this instanceof ReactiveRmExecutor)) {
throw new UnsupportedOperationException();
}
return ((RmDatabaseSession) this.session).recoverSupportFlags();
}
@Override
public final boolean isSameRm(final Session.XaTransactionSupportSpec s) {
if (!(this instanceof ReactiveRmExecutor)) {
throw new UnsupportedOperationException();
}
final boolean match;
if (s == this) {
match = true;
} else if (s instanceof JdbdStmtExecutor) {
match = this.session.isSameFactory(((JdbdStmtExecutor) s).session);
} else {
match = false;
}
return match;
}
@SuppressWarnings("unchecked")
@Override
public final T valueOf(final @Nullable Option option) {
final Object v;
final T value;
final io.jdbd.session.Option> jdbdOption;
if (option == null) {
value = null;
} else if ((jdbdOption = this.factory.mapToJdbdOption(option)) == null) {
value = null;
} else if (option.javaType().isInstance(v = this.session.valueOf(jdbdOption))) {
value = (T) v;
} else {
value = null;
}
return value;
}
@Override
public final Set> optionSet() {
Set > optionSet = this.optionSet;
if (optionSet == null) {
this.optionSet = optionSet = this.factory.mapArmyOptionSet(this.session.optionSet());
}
return optionSet;
}
@Override
public final boolean isClosed() {
return this.session.isClosed();
}
@Override
public final Mono close() {
return Mono.from(this.session.close());
}
@Override
public final String toString() {
return _StringUtils.builder(70)
.append(getClass().getName())
.append("[sessionName:")
.append(this.sessionName)
.append(",executorHash:")
.append(System.identityHashCode(this))
.append(']')
.toString();
}
/*-------------------below package instance methods-------------------*/
abstract Logger getLogger();
abstract DataType getDataType(ResultRowMeta meta, int indexBasedZero);
@Nullable
abstract Object get(DataRow row, int indexBasedZero, MappingType type, DataType dataType);
abstract void bind(ParametrizedStatement statement, int indexBasedZero, MappingType type, DataType dataType, @Nullable Object value);
Isolation mapToArmyDialectIsolation(io.jdbd.session.Isolation jdbdIsolation) {
throw unknownJdbdIsolation(jdbdIsolation);
}
io.jdbd.session.Isolation mapToJdbdDialectIsolation(Isolation isolation) {
throw unsupportedIsolation(isolation);
}
/**
* @see #bindParameter(ParametrizedStatement, List)
*/
final void bindArmyType(ParametrizedStatement stmt, final int indexBasedZero, final MappingType type,
final DataType dataType, final ArmyType armyType, final @Nullable Object nullable) {
final JdbdType jdbdType;
try {
jdbdType = JdbdType.valueOf(armyType.name());
} catch (IllegalArgumentException e) {
throw mapMethodError(type, dataType);
}
final Object value;
if (nullable == null) {
value = null;
} else switch (armyType) {
case BOOLEAN: {
if (!(nullable instanceof Boolean)) {
throw beforeBindMethodError(type, dataType, nullable);
}
value = nullable;
}
break;
case TINYINT: {
if (!(nullable instanceof Byte)) {
throw beforeBindMethodError(type, dataType, nullable);
}
value = nullable;
}
break;
case TINYINT_UNSIGNED:
case SMALLINT: {
if (!(nullable instanceof Short)) {
throw beforeBindMethodError(type, dataType, nullable);
}
value = nullable;
}
break;
case SMALLINT_UNSIGNED:
case MEDIUMINT:
case MEDIUMINT_UNSIGNED:
case INTEGER: {
if (!(nullable instanceof Integer)) {
throw beforeBindMethodError(type, dataType, nullable);
}
value = nullable;
}
break;
case INTEGER_UNSIGNED:
case BIGINT: {
if (!(nullable instanceof Long)) {
throw beforeBindMethodError(type, dataType, nullable);
}
value = nullable;
}
break;
case BIGINT_UNSIGNED: {
if (!(nullable instanceof BigInteger || nullable instanceof BigDecimal)) {
throw beforeBindMethodError(type, dataType, nullable);
}
value = nullable;
}
break;
case DECIMAL:
case DECIMAL_UNSIGNED: {
if (!(nullable instanceof BigDecimal)) {
throw beforeBindMethodError(type, dataType, nullable);
}
value = nullable;
}
break;
case FLOAT: {
if (!(nullable instanceof Float)) {
throw beforeBindMethodError(type, dataType, nullable);
}
value = nullable;
}
break;
case DOUBLE: {
if (!(nullable instanceof Double)) {
throw beforeBindMethodError(type, dataType, nullable);
}
value = nullable;
}
break;
case TIME: {
if (!(nullable instanceof LocalTime)) {
throw beforeBindMethodError(type, dataType, nullable);
}
value = nullable;
}
break;
case YEAR_MONTH: {
if (!(nullable instanceof LocalDate || nullable instanceof YearMonth)) {
throw beforeBindMethodError(type, dataType, nullable);
}
value = nullable;
}
break;
case MONTH_DAY: {
if (!(nullable instanceof LocalDate || nullable instanceof MonthDay)) {
throw beforeBindMethodError(type, dataType, nullable);
}
value = nullable;
}
break;
case DATE: {
if (!(nullable instanceof LocalDate)) {
throw beforeBindMethodError(type, dataType, nullable);
}
value = nullable;
}
break;
case TIMESTAMP: {
if (!(nullable instanceof LocalDateTime)) {
throw beforeBindMethodError(type, dataType, nullable);
}
value = nullable;
}
break;
case TIME_WITH_TIMEZONE: {
if (!(nullable instanceof OffsetTime)) {
throw beforeBindMethodError(type, dataType, nullable);
}
value = nullable;
}
break;
case TIMESTAMP_WITH_TIMEZONE: {
if (!(nullable instanceof OffsetDateTime)) {
throw beforeBindMethodError(type, dataType, nullable);
}
value = nullable;
}
break;
case CHAR:
case VARCHAR:
case ENUM:
case TINYTEXT:
case TEXT:
case MEDIUMTEXT: {
if (!(nullable instanceof String)) {
throw beforeBindMethodError(type, dataType, nullable);
}
value = nullable;
}
break;
case JSON:
case JSONB:
case LONGTEXT:
value = toJdbdLongTextValue(type, dataType, nullable);
break;
case BINARY:
case VARBINARY:
case TINYBLOB:
case BLOB:
case MEDIUMBLOB: {
if (!(nullable instanceof byte[])) {
throw beforeBindMethodError(type, dataType, nullable);
}
value = nullable;
}
break;
case LONGBLOB:
value = toJdbdLongBinaryValue(type, dataType, nullable);
break;
case GEOMETRY:
value = toJdbdGeometry(type, dataType, nullable);
break;
default:
throw mapMethodError(type, dataType);
}
stmt.bind(indexBasedZero, jdbdType, value);
}
@Nullable
final Object toJdbdGeometry(final MappingType type, final DataType dataType, final @Nullable Object nullable) {
final Object value;
if (nullable == null || nullable instanceof byte[] || nullable instanceof String) {
value = nullable;
} else if (nullable instanceof io.army.reactive.type.Blob) {
final io.army.reactive.type.Blob blob = (io.army.reactive.type.Blob) nullable;
value = io.jdbd.type.Blob.from(blob.value());
} else if (nullable instanceof io.army.reactive.type.Clob) {
final io.army.reactive.type.Clob clob = (io.army.reactive.type.Clob) nullable;
value = io.jdbd.type.Clob.from(clob.value());
} else if (nullable instanceof io.army.type.BlobPath) {
final io.army.type.BlobPath path = (io.army.type.BlobPath) nullable;
value = io.jdbd.type.BlobPath.from(path.isDeleteOnClose(), path.value());
} else if (nullable instanceof io.army.type.TextPath) {
final io.army.type.TextPath armyPath = (io.army.type.TextPath) nullable;
value = io.jdbd.type.TextPath.from(armyPath.isDeleteOnClose(), armyPath.charset(), armyPath.value());
} else {
throw beforeBindMethodError(type, dataType, nullable);
}
return value;
}
@Nullable
final Object toJdbdLongTextValue(final MappingType type, final DataType dataType, final @Nullable Object nullable) {
final Object value;
if (nullable == null || nullable instanceof String) {
value = nullable;
} else if (nullable instanceof io.army.type.TextPath) {
final io.army.type.TextPath armyPath = (io.army.type.TextPath) nullable;
value = io.jdbd.type.TextPath.from(armyPath.isDeleteOnClose(), armyPath.charset(), armyPath.value());
} else if (nullable instanceof io.army.reactive.type.Clob) {
final io.army.reactive.type.Clob clob = (io.army.reactive.type.Clob) nullable;
value = io.jdbd.type.Clob.from(clob.value());
} else {
throw beforeBindMethodError(type, dataType, nullable);
}
return value;
}
@Nullable
final Object toJdbdLongBinaryValue(final MappingType type, final DataType dataType, final @Nullable Object nullable) {
final Object value;
if (nullable == null || nullable instanceof byte[]) {
value = nullable;
} else if (nullable instanceof io.army.type.BlobPath) {
final io.army.type.BlobPath path = (io.army.type.BlobPath) nullable;
value = io.jdbd.type.BlobPath.from(path.isDeleteOnClose(), path.value());
} else if (nullable instanceof io.army.reactive.type.Blob) {
final io.army.reactive.type.Blob blob = (io.army.reactive.type.Blob) nullable;
value = io.jdbd.type.Blob.from(blob.value());
} else {
throw beforeBindMethodError(type, dataType, nullable);
}
return value;
}
@Nullable
final Object getLongText(final DataRow row, final int indexBasedZero) {
final Object value;
if (row.isNull(indexBasedZero)) {
value = null;
} else if (row.isBigColumn(indexBasedZero)) {
final io.jdbd.type.TextPath path = row.getNonNull(indexBasedZero, io.jdbd.type.TextPath.class);
value = TextPath.from(path.isDeleteOnClose(), path.charset(), path.value());
} else {
value = row.get(indexBasedZero, String.class);
}
return value;
}
@Nullable
final Object getLongBinary(final DataRow row, final int indexBasedZero, final MappingType type) {
final Object value;
if (row.isNull(indexBasedZero)) {
value = null;
} else if (row.isBigColumn(indexBasedZero)) {
final io.jdbd.type.BlobPath path = row.getNonNull(indexBasedZero, io.jdbd.type.BlobPath.class);
value = BlobPath.from(path.isDeleteOnClose(), path.value());
} else if (type instanceof MappingType.SqlStringType) {
value = row.get(indexBasedZero, String.class);
} else {
value = row.get(indexBasedZero, byte[].class);
}
return value;
}
final ArmyException wrapExecutingError(final Exception cause) {
return this.factory.wrapExecuteError(cause);
}
final Function, ?> mapToJdbdOptionFunc(final Function, ?> optionFunc) {
return addJdbdLogOptionIfNeed(this.factory.mapToJdbdOptionFunc(optionFunc));
}
final Function, ?> addJdbdLogOptionIfNeed(final Function, ?> func) {
final Function, ?> finalJdbdFunc;
if (this.jdbdSqlLogger == null) {
finalJdbdFunc = func;
} else {
finalJdbdFunc = jdbdOption -> {
if (io.jdbd.session.Option.SQL_LOGGER == jdbdOption) {
return this.jdbdSqlLogger;
}
return func.apply(jdbdOption);
};
}
return finalJdbdFunc;
}
final Throwable wrapExecuteIfNeed(final Throwable cause) {
if (!(cause instanceof Exception)) {
return cause;
}
return wrapExecutingError((Exception) cause);
}
/*-------------------below private instance methods-------------------*/
private void jdbdSqlLogger(final String sql) {
printSqlIfNeed(this.factory, this.sessionName, getLogger(), sql);
}
/**
* @see #start(Xid, int, TransactionOption)
*/
private io.jdbd.session.Xid mapToJdbdXid(final Xid xid) {
return io.jdbd.session.Xid.from(xid.getGtrid(), xid.getBqual(), xid.getFormatId());
}
private Xid mapToArmyXid(io.jdbd.session.Xid xid) {
return Xid.from(xid.getGtrid(), xid.getBqual(), xid.getFormatId(), this.factory.mapToArmyOptionFunc(xid::valueOf));
}
/**
* @see #transactionInfo()
*/
@SuppressWarnings("unchecked")
private TransactionInfo mapToArmyTransactionInfo(final io.jdbd.session.TransactionInfo info) {
final TransactionInfo.InfoBuilder builder;
builder = TransactionInfo.builder(info.inTransaction(), mapToArmyIsolation(info.isolation()), info.isReadOnly());
Option> armyOption;
for (io.jdbd.session.Option> jdbdOption : info.optionSet()) {
armyOption = this.factory.mapToArmyOption(jdbdOption);
if (armyOption != null) {
builder.option((Option) armyOption, info.valueOf(jdbdOption));
}
}
return builder.build();
}
/**
* @throws ArmyException throw when isolation is unsupported by driver.
* @see #setTransactionCharacteristics(TransactionOption)
* @see #startTransaction(TransactionOption, HandleMode)
* @see #start(Xid, int, TransactionOption)
*/
@SuppressWarnings("unchecked")
private io.jdbd.session.TransactionOption mapToJdbdTransactionOption(final TransactionOption option)
throws ArmyException {
final Set> armyOptionSet = option.optionSet();
final io.jdbd.session.Isolation jdbdIsolation = mapToJdbdIsolation(option.isolation());
final io.jdbd.session.TransactionOption jdbdTransactionOption;
final SqlLogMode sqlLogMode;
sqlLogMode = readSqlLogMode(this.factory);
if (armyOptionSet.size() == 0 && sqlLogMode == SqlLogMode.OFF) {
jdbdTransactionOption = io.jdbd.session.TransactionOption.option(jdbdIsolation, option.isReadOnly());
} else {
final io.jdbd.session.TransactionOption.Builder builder;
builder = io.jdbd.session.TransactionOption.builder();
io.jdbd.session.Option> jdbdOption;
for (Option> armyOption : armyOptionSet) {
jdbdOption = this.factory.mapToJdbdOption(armyOption);
if (jdbdOption == null) {
continue;
}
builder.option((io.jdbd.session.Option) jdbdOption, option.valueOf(armyOption));
}
builder.option(io.jdbd.session.Option.ISOLATION, jdbdIsolation)
.option(io.jdbd.session.Option.READ_ONLY, option.isReadOnly());
if (this.jdbdSqlLogger != null) {
builder.option(io.jdbd.session.Option.SQL_LOGGER, this.jdbdSqlLogger);
}
jdbdTransactionOption = builder.build();
}
return jdbdTransactionOption;
}
@SuppressWarnings("all")
private Optional mapToOptionalArmyXid(Optional optional) {
if (optional.isPresent()) {
return Optional.of(mapToArmyXid(optional.get()));
}
return Optional.empty();
}
/**
* @throws ArmyException throw when isolation is unknown.
*/
private Isolation mapToArmyIsolation(final io.jdbd.session.Isolation isolation) throws ArmyException {
final Isolation armyIsolation;
if (isolation == io.jdbd.session.Isolation.READ_COMMITTED) {
armyIsolation = Isolation.READ_COMMITTED;
} else if (isolation == io.jdbd.session.Isolation.REPEATABLE_READ) {
armyIsolation = Isolation.REPEATABLE_READ;
} else if (isolation == io.jdbd.session.Isolation.SERIALIZABLE) {
armyIsolation = Isolation.SERIALIZABLE;
} else if (isolation == io.jdbd.session.Isolation.READ_UNCOMMITTED) {
armyIsolation = Isolation.READ_UNCOMMITTED;
} else {
armyIsolation = mapToArmyDialectIsolation(isolation);
}
return armyIsolation;
}
/**
* @throws ArmyException throw when isolation is unsupported by driver.
* @see #mapToJdbdTransactionOption(TransactionOption)
*/
@Nullable
private io.jdbd.session.Isolation mapToJdbdIsolation(final @Nullable Isolation isolation) throws ArmyException {
final io.jdbd.session.Isolation jdbdIsolation;
if (isolation == null) {
jdbdIsolation = null;
} else if (isolation == Isolation.READ_COMMITTED) {
jdbdIsolation = io.jdbd.session.Isolation.READ_COMMITTED;
} else if (isolation == Isolation.REPEATABLE_READ) {
jdbdIsolation = io.jdbd.session.Isolation.REPEATABLE_READ;
} else if (isolation == Isolation.SERIALIZABLE) {
jdbdIsolation = io.jdbd.session.Isolation.SERIALIZABLE;
} else if (isolation == Isolation.READ_UNCOMMITTED) {
jdbdIsolation = io.jdbd.session.Isolation.READ_UNCOMMITTED;
} else {
jdbdIsolation = mapToJdbdDialectIsolation(isolation);
}
return jdbdIsolation;
}
/**
* @see #commit(Function)
* @see #rollback(Function)
*/
@SuppressWarnings("all")
private Optional mapToArmyOptionalTransactionInfo(Optional jdbdOptional) {
if (jdbdOptional.isPresent()) {
return Optional.of(mapToArmyTransactionInfo(jdbdOptional.get()));
}
return Optional.empty();
}
private ResultStates mapToArmyResultStates(io.jdbd.result.ResultStates jdbdStates) {
return new JdbdResultStates(jdbdStates, this.factory, false);
}
/**
* @see #query(SingleSqlStmt, Class, ReactiveStmtOption)
* @see #queryObject(SingleSqlStmt, Supplier, ReactiveStmtOption)
* @see #queryRecord(SingleSqlStmt, Function, ReactiveStmtOption)
*/
private Flux executeQuery(final SingleSqlStmt stmt, final Function func,
final ReactiveStmtOption option) throws JdbdException, TimeoutException {
return Flux.from(bindStatement(stmt, option).executeQuery(func, createStatesConsumer(option)))
.onErrorMap(this::wrapExecuteIfNeed);
}
/**
* @see #query(SingleSqlStmt, Class, ReactiveStmtOption)
* @see #queryObject(SingleSqlStmt, Supplier, ReactiveStmtOption)
* @see #queryRecord(SingleSqlStmt, Function, ReactiveStmtOption)
*/
private Function mapBeanFunc(final SingleSqlStmt stmt, final Class resultClass) {
final List extends Selection> selectionList;
selectionList = stmt.selectionList();
final RowReader rowReader;
if ((stmt instanceof TwoStmtQueryStmt && ((TwoStmtQueryStmt) stmt).maxColumnSize() == 1)
|| selectionList.size() == 1) {
rowReader = new SingleColumnRowReader<>(this, selectionList, resultClass);
} else {
rowReader = new BeanReader<>(this, selectionList, resultClass);
}
final Function function;
if (stmt instanceof GeneratedKeyStmt) {
function = returnIdQueryRowFunc((GeneratedKeyStmt) stmt, rowReader);
} else {
function = rowReader::readOneRow;
}
return function;
}
/**
* @see #query(SingleSqlStmt, Class, ReactiveStmtOption)
* @see #queryObject(SingleSqlStmt, Supplier, ReactiveStmtOption)
* @see #queryRecord(SingleSqlStmt, Function, ReactiveStmtOption)
*/
private Function mapObjectFunc(final SingleSqlStmt stmt, final Supplier constructor) {
final RowReader rowReader;
rowReader = new ObjectRowReader<>(this, stmt.selectionList(), constructor, stmt instanceof TwoStmtModeQuerySpec);
final Function function;
if (stmt instanceof GeneratedKeyStmt) {
function = returnIdQueryRowFunc((GeneratedKeyStmt) stmt, rowReader);
} else {
function = rowReader::readOneRow;
}
return function;
}
/**
* @see #query(SingleSqlStmt, Class, ReactiveStmtOption)
* @see #queryObject(SingleSqlStmt, Supplier, ReactiveStmtOption)
* @see #queryRecord(SingleSqlStmt, Function, ReactiveStmtOption)
*/
private Function mapRecordFunc(final SingleSqlStmt stmt, final Function recordFunc) {
final RowReader rowReader;
rowReader = new CurrentRecordRowReader<>(this, stmt.selectionList(), recordFunc);
final Function function;
if (stmt instanceof GeneratedKeyStmt) {
function = returnIdQueryRowFunc((GeneratedKeyStmt) stmt, rowReader);
} else {
function = rowReader::readOneRow;
}
return function;
}
/**
* @see #query(SingleSqlStmt, Class, ReactiveStmtOption)
* @see #queryObject(SingleSqlStmt, Supplier, ReactiveStmtOption)
* @see #queryRecord(SingleSqlStmt, Function, ReactiveStmtOption)
*/
private Consumer createStatesConsumer(final ReactiveStmtOption option) {
final Consumer armyConsumer;
armyConsumer = option.stateConsumer();
if (armyConsumer == ResultStates.IGNORE_STATES) {
return io.jdbd.result.ResultStates.IGNORE_STATES;
}
return states -> {
final ResultStates armyStates;
armyStates = mapToArmyResultStates(states);
try {
armyConsumer.accept(armyStates);
} catch (Exception e) {
String m = String.format("%s %s throw error, %s", ResultStates.class.getName(),
armyConsumer, e.getMessage());
throw new ArmyException(m);
}
};
}
/**
* @see #mapBeanFunc(SingleSqlStmt, Class)
*/
private Function returnIdQueryRowFunc(final GeneratedKeyStmt keyStmt,
final RowReader rowReader) {
final int indexBasedZero = keyStmt.idSelectionIndex();
final MappingType type = keyStmt.idField().mappingType();
final DataType dataType = type.map(this.factory.serverMeta);
final MappingEnv env = this.factory.mappingEnv;
final int[] rowIndexHolder = new int[]{0};
return dataRow -> {
final int rowIndex = rowIndexHolder[0]++;
if (dataRow.rowNumber() != rowIndexHolder[0]) {
throw jdbdRowNumberNotMatch(rowIndex, dataRow.rowNumber());
}
Object idValue;
idValue = get(dataRow, indexBasedZero, type, dataType);
if (idValue == null) {
throw _Exceptions.idValueIsNull(rowIndex, keyStmt.idField());
}
idValue = type.afterGet(dataType, env, idValue);
keyStmt.setGeneratedIdValue(rowIndex, idValue);
return rowReader.readOneRow(dataRow);
};
}
/**
* @see #insert(SimpleStmt, ReactiveStmtOption)
*/
private ResultStates handleInsertStates(final io.jdbd.result.ResultStates jdbdStates,
final GeneratedKeyStmt stmt) {
final int rowSize = stmt.rowSize();
if (jdbdStates.affectedRows() != rowSize) {
if (!(rowSize == 1 && stmt.hasConflictClause()) || jdbdStates.affectedRows() != 2) {
throw _Exceptions.insertedRowsAndGenerateIdNotMatch(rowSize, jdbdStates.affectedRows());
}
} else if (!jdbdStates.isSupportInsertId()) {
String m = String.format("error ,%s don't support lastInsertId() method", jdbdStates.getClass().getName());
throw new DataAccessException(m);
}
final PrimaryFieldMeta> idField = stmt.idField();
final MappingType type = idField.mappingType();
final DataType dataType = type.map(this.factory.serverMeta);
final MappingEnv env = this.factory.mappingEnv;
final int lastRowIndex = rowSize - 1;
long lastInsertedId = jdbdStates.lastInsertedId();
BigInteger bigId = null;
if (lastInsertedId < 0 || (lastInsertedId + rowSize) < 0) {
bigId = new BigInteger(Long.toUnsignedString(lastInsertedId));
}
Object idValue;
for (int i = 0; i < rowSize; i++) {
if (bigId == null) {
idValue = lastInsertedId++;
} else {
idValue = bigId;
if (i < lastRowIndex) {
bigId = bigId.add(BigInteger.ONE);
}
}
idValue = type.afterGet(dataType, env, idValue);
stmt.setGeneratedIdValue(i, idValue);
}
return mapToArmyResultStates(jdbdStates);
}
private BindStatement bindStatement(final SingleSqlStmt stmt, final ReactiveStmtOption option)
throws TimeoutException, JdbdException {
final BindStatement statement;
statement = this.session.bindStatement(stmt.sqlText(), option.isPreferServerPrepare());
if (stmt instanceof SimpleStmt) {
bindParameter(statement, ((SimpleStmt) stmt).paramGroup());
} else if (stmt instanceof BatchStmt) {
final List> groupList = ((BatchStmt) stmt).groupList();
final int groupSize = groupList.size();
for (int i = 0; i < groupSize; i++) {
bindParameter(statement, groupList.get(i));
statement.addBatch();
}
} else {
throw _Exceptions.unexpectedStmt(stmt);
}
if (option.isSupportTimeout()) {
statement.setTimeout(option.restMillSeconds());
}
final int fetchSize, frequency;
fetchSize = option.fetchSize();
if (fetchSize > 0) {
statement.setFetchSize(fetchSize);
}
frequency = option.frequency();
if (frequency > -1) {
statement.setFrequency(frequency);
}
return statement;
}
private void bindParameter(final ParametrizedStatement statement, final List paramList) {
final ServerMeta serverMeta = this.factory.serverMeta;
final MappingEnv mappingEnv = this.factory.mappingEnv;
final boolean truncatedTimeType = this.factory.truncatedTimeType;
final int paramSize = paramList.size();
SQLParam sqlParam;
Object value;
MappingType type;
TypeMeta typeMeta;
DataType dataType;
Iterator> iterator;
List> list;
boolean hasMore;
for (int itemIndex = 0, paramIndex = 0, columnItemSize = 0; itemIndex < paramSize; itemIndex++) {
sqlParam = paramList.get(itemIndex);
typeMeta = sqlParam.typeMeta();
if (typeMeta instanceof MappingType) {
type = (MappingType) typeMeta;
} else {
type = typeMeta.mappingType();
}
dataType = type.map(serverMeta);
if (sqlParam instanceof SingleParam) {
list = null;
iterator = null;
} else if ((list = ((MultiParam) sqlParam).valueList()) instanceof ArrayList) {
columnItemSize = list.size();
iterator = null;
} else {
iterator = list.iterator();
}
hasMore = true;
for (int columnItemIndex = 0; hasMore; columnItemIndex++) {
if (list == null) {
value = ((SingleParam) sqlParam).value();
hasMore = false;
} else if (iterator == null) {
if (columnItemIndex < columnItemSize) {
value = list.get(columnItemIndex);
} else {
break;
}
} else if (iterator.hasNext()) {
value = iterator.next();
} else {
break;
}
if (value == null) { // jdbd client-prepared support dialect type null ,for example postgre : null::text
bind(statement, paramIndex++, type, dataType, null);
continue;
}
value = type.beforeBind(dataType, mappingEnv, value);
//TODO field codec
if (truncatedTimeType && value instanceof Temporal && typeMeta instanceof FieldMeta) {
value = _TimeUtils.truncatedIfNeed(((FieldMeta>) typeMeta).scale(), (Temporal) value);
}
bind(statement, paramIndex++, type, dataType, value);
} // while loop
}// for loop
}
/*-------------------below package static methods -------------------*/
/**
* @param cause not {@link io.jdbd.result.ServerException}
*/
static ArmyException wrapException(final Exception cause) {
final ArmyException e;
if (cause instanceof ArmyException) {
e = (ArmyException) cause;
} else if (!(cause instanceof JdbdException)) {
e = _Exceptions.unknownError(cause);
} else {
final JdbdException je = (JdbdException) cause;
e = new DriverException(cause, je.getSqlState(), je.getVendorCode());
}
return e;
}
static ArmyException unknownJdbdIsolation(io.jdbd.session.Isolation isolation) {
return new ArmyException(String.format("unknown %s", isolation));
}
/*-------------------below private static methods -------------------*/
private static DataAccessException jdbdRowNumberNotMatch(int rowIndex, long jdbdRowNumber) {
String m = String.format("jdbd row index error,expected %s but %s", rowIndex, jdbdRowNumber);
return new DataAccessException(m);
}
/*-------------------below static class -------------------*/
private static abstract class RowReader extends ArmyStmtCurrentRecord {
final JdbdStmtExecutor executor;
final List extends Selection> selectionList;
final DataType[] dataTypeArray;
private final MappingType[] compatibleTypeArray;
private final Class> resultClass;
private RowReader(JdbdStmtExecutor executor, List extends Selection> selectionList,
@Nullable Class> resultClass) {
this.executor = executor;
this.selectionList = selectionList;
this.dataTypeArray = new SqlType[selectionList.size()];
this.compatibleTypeArray = new MappingType[this.dataTypeArray.length];
this.resultClass = resultClass;
}
@Override
public ArmyResultRecordMeta getRecordMeta() {
throw new UnsupportedOperationException();
}
@Override
protected Object[] copyValueArray() {
throw new UnsupportedOperationException();
}
@Override
public long rowNumber() {
throw new UnsupportedOperationException();
}
@Nullable
@Override
public Object get(int indexBasedZero) {
throw new UnsupportedOperationException();
}
@Nullable
final R readOneRow(final DataRow dataRow) {
final JdbdStmtExecutor executor = this.executor;
final MappingEnv env = executor.factory.mappingEnv;
final DataType[] dataTypeArray = this.dataTypeArray;
final List extends Selection> selectionList = this.selectionList;
final MappingType[] compatibleTypeArray = this.compatibleTypeArray;
final Object documentNullValue = MappingType.DOCUMENT_NULL_VALUE;
final int columnCount;
if (dataTypeArray[0] == null
|| (this instanceof CurrentRecordRowReader
&& resultNo() < dataRow.resultNo())) {
columnCount = dataRow.getColumnCount();
if (columnCount != dataTypeArray.length) {
throw _Exceptions.columnCountAndSelectionCountNotMatch(columnCount, dataTypeArray.length);
}
final ResultRowMeta meta = dataRow.getRowMeta();
for (int i = 0; i < columnCount; i++) {
dataTypeArray[i] = executor.getDataType(meta, i);
}
if (this instanceof CurrentRecordRowReader) {
((CurrentRecordRowReader>) this).acceptRowMeta(dataRow.getRowMeta(), dataTypeArray);
}
} else {
columnCount = dataTypeArray.length;
}
final ObjectAccessor accessor;
accessor = createRow();
TypeMeta typeMeta;
MappingType type;
Selection selection;
Object columnValue;
DataType dataType;
String fieldName;
for (int i = 0; i < columnCount; i++) {
selection = selectionList.get(i);
fieldName = selection.label();
dataType = dataTypeArray[i];
if ((type = compatibleTypeArray[i]) == null) {
if (!(this instanceof CurrentRecordRowReader)) {
type = compatibleTypeFrom(selection, dataType, this.resultClass, accessor, fieldName);
} else if ((typeMeta = selection.typeMeta()) instanceof MappingType) {
type = (MappingType) typeMeta;
} else {
type = typeMeta.mappingType();
}
compatibleTypeArray[i] = type;
}
columnValue = executor.get(dataRow, i, type, dataType);
if (columnValue == null) {
acceptColumn(i, fieldName, null);
continue;
}
columnValue = type.afterGet(dataType, env, columnValue);
if ((columnValue == documentNullValue)) {
if (!(type instanceof MappingType.SqlDocumentType)) {
throw afterGetMethodError(type, dataType, columnValue);
}
acceptColumn(i, fieldName, null);
continue;
}
//TODO field codec
acceptColumn(i, fieldName, columnValue);
} // for loop
return endOneRow();
}
abstract ObjectAccessor createRow();
abstract void acceptColumn(int indexBasedZero, String fieldName, @Nullable Object value);
@Nullable
abstract R endOneRow();
private DataType getDataType(int index) {
return this.dataTypeArray[index];
}
}// RowReader
private static final class SingleColumnRowReader extends RowReader {
private R row;
private SingleColumnRowReader(JdbdStmtExecutor executor, List extends Selection> selectionList,
Class resultClass) {
super(executor, selectionList, resultClass);
}
@Override
ObjectAccessor createRow() {
this.row = null;
return ExecutorSupport.SINGLE_COLUMN_PSEUDO_ACCESSOR;
}
@SuppressWarnings("unchecked")
@Override
void acceptColumn(int indexBasedZero, String fieldName, @Nullable Object value) {
assert indexBasedZero == 0;
this.row = (R) value;
}
@Override
R endOneRow() {
final R row = this.row;
this.row = null;
return row;
}
}// SingleColumnRowReader
private static final class OptionalSingleColumnRowReader extends RowReader> {
private R row;
private OptionalSingleColumnRowReader(JdbdStmtExecutor executor, List extends Selection> selectionList,
Class resultClass) {
super(executor, selectionList, resultClass);
}
@Override
ObjectAccessor createRow() {
this.row = null;
return ExecutorSupport.SINGLE_COLUMN_PSEUDO_ACCESSOR;
}
@SuppressWarnings("unchecked")
@Override
void acceptColumn(int indexBasedZero, String fieldName, @Nullable Object value) {
assert indexBasedZero == 0;
this.row = (R) value;
}
@Override
Optional endOneRow() {
final R row = this.row;
this.row = null;
return Optional.ofNullable(row);
}
}// OptionalSingleColumnRowReader
private static final class BeanReader extends RowReader {
private final ObjectAccessor accessor;
private final Constructor constructor;
private R row;
private BeanReader(JdbdStmtExecutor executor, List extends Selection> selectionList,
Class resultClass) {
super(executor, selectionList, resultClass);
this.accessor = ObjectAccessorFactory.forBean(resultClass);
this.constructor = ObjectAccessorFactory.getConstructor(resultClass);
}
@Override
ObjectAccessor createRow() {
this.row = ObjectAccessorFactory.createBean(this.constructor);
return this.accessor;
}
@Override
void acceptColumn(int indexBasedZero, String fieldName, @Nullable Object value) {
this.accessor.set(this.row, fieldName, value);
}
@Override
R endOneRow() {
final R row = this.row;
this.row = null;
return row;
}
}// BeanReader
private static final class ObjectRowReader extends RowReader {
private final Supplier constructor;
private final boolean twoStmtMode;
private R row;
private Class> rowJavaClass;
private ObjectAccessor accessor;
private ObjectRowReader(JdbdStmtExecutor executor, List extends Selection> selectionList,
Supplier constructor, boolean twoStmtMode) {
super(executor, selectionList, null);
this.constructor = constructor;
this.twoStmtMode = twoStmtMode;
}
@Override
ObjectAccessor createRow() {
assert this.row == null;
final R row;
this.row = row = this.constructor.get();
if (row == null) {
throw _Exceptions.objectConstructorError();
}
Class> rowJavaClass = this.rowJavaClass;
final ObjectAccessor accessor;
if (rowJavaClass == null || rowJavaClass != row.getClass()) {
this.rowJavaClass = row.getClass();
this.accessor = accessor = ObjectAccessorFactory.fromInstance(row);
} else {
accessor = this.accessor;
assert accessor != null;
}
return accessor;
}
@Override
void acceptColumn(int indexBasedZero, String fieldName, @Nullable Object value) {
this.accessor.set(this.row, fieldName, value);
}
@SuppressWarnings("unchecked")
@Override
R endOneRow() {
R row = this.row;
assert row != null;
this.row = null;
if (row instanceof Map && row instanceof ImmutableSpec && !this.twoStmtMode) {
row = (R) _Collections.unmodifiableMapForDeveloper((Map, ?>) row);
}
return row;
}
}// ObjectReader
private static final class SecondRowReader extends RowReader {
private final List rowList;
private final int rowSize;
private final boolean singleColumn;
private ObjectAccessor accessor;
private R row;
private Class> rowJavaClass;
/**
* from -1 not 0
*/
private int rowIndex = -1;
private SecondRowReader(JdbdStmtExecutor executor, TwoStmtQueryStmt stmt, List rowList) {
super(executor, stmt.selectionList(), rowResultClass(rowList.get(0)));
this.rowList = rowList;
this.rowSize = rowList.size();
if (stmt.maxColumnSize() == 1) {
this.singleColumn = true;
this.accessor = ExecutorSupport.SINGLE_COLUMN_PSEUDO_ACCESSOR;
} else {
this.singleColumn = false;
}
}
@Override
ObjectAccessor createRow() {
final int rowIndex = ++this.rowIndex;
if (rowIndex >= this.rowSize) {
throw secondQueryRowCountNotMatch(this.rowSize, rowIndex + 1);
}
final R row;
this.row = row = this.rowList.get(rowIndex);
final ObjectAccessor accessor;
if (this.singleColumn) {
accessor = this.accessor;
assert accessor != null;
} else if (this.rowJavaClass != row.getClass()) {
this.rowJavaClass = row.getClass();
this.accessor = accessor = ObjectAccessorFactory.fromInstance(row);
} else {
accessor = this.accessor;
assert accessor != null;
}
return accessor;
}
@Override
void acceptColumn(final int indexBasedZero, String fieldName, @Nullable Object value) {
if (!this.singleColumn) {
this.accessor.set(this.row, fieldName, value);
} else if (Objects.equals(value, this.row)) {
assert indexBasedZero == 0;
} else {
String m = String.format("error , single column row[rowIndexBasedZero : %s ,indexBasedZero : %s , selection label : %s] and first query not match.",
this.rowIndex, indexBasedZero, fieldName);
throw new DataAccessException(m);
}
}
@SuppressWarnings("unchecked")
@Override
R endOneRow() {
R row = this.row;
if (row instanceof Map && row instanceof ImmutableSpec) {
row = (R) _Collections.unmodifiableMapForDeveloper((Map, ?>) row);
}
this.row = null;
return row;
}
}// SecondRowReader
private static final class CurrentRecordRowReader extends RowReader {
private final Function function;
private final Object[] valueArray;
private JdbdStmtRowMeta meta;
private int columnIndex;
private long rowCount;
private CurrentRecordRowReader(JdbdStmtExecutor executor, List extends Selection> selectionList,
Function function) {
super(executor, selectionList, null);
this.function = function;
this.valueArray = new Object[selectionList.size()];
}
@Override
public ArmyResultRecordMeta getRecordMeta() {
final JdbdStmtRowMeta meta = this.meta;
assert meta != null;
return meta;
}
@Override
protected Object[] copyValueArray() {
final Object[] array = new Object[this.valueArray.length];
System.arraycopy(this.valueArray, 0, array, 0, array.length);
return array;
}
@Override
public long rowNumber() {
return this.rowCount;
}
@Nullable
@Override
public Object get(int indexBasedZero) {
final JdbdStmtRowMeta meta = this.meta;
assert meta != null;
return this.valueArray[meta.checkIndex(indexBasedZero)];
}
@Override
ObjectAccessor createRow() {
assert this.columnIndex == this.valueArray.length;
this.columnIndex = 0;
return ExecutorSupport.SINGLE_COLUMN_PSEUDO_ACCESSOR;
}
@Override
void acceptColumn(final int indexBasedZero, String fieldName, @Nullable Object value) {
final int currentIndex = this.columnIndex++;
assert indexBasedZero == currentIndex;
this.valueArray[indexBasedZero] = value;
}
@Override
R endOneRow() {
assert this.columnIndex == this.valueArray.length;
this.rowCount++; // firstly ,++
final R row;
row = this.function.apply(this); // secondly , invoke user function
if (row instanceof CurrentRecord) {
throw _Exceptions.recordMapFuncReturnError(this.function);
}
return row;
}
private void acceptRowMeta(final ResultRowMeta rowMeta, final DataType[] dataTypeArray) {
final JdbdStmtRowMeta meta = this.meta;
final int resultNo;
if (meta == null) {
resultNo = 1;
} else {
resultNo = meta.resultNo() + 1;
}
if (resultNo != rowMeta.resultNo()) {
throw driverError();
}
this.meta = new JdbdStmtRowMeta(resultNo, dataTypeArray, this.selectionList, this.executor, rowMeta);
this.rowCount = 0L; // reset
}
}//CurrentRecordRowReader
private static abstract class JdbdBatchQueryResults extends ArmyReactiveMultiResultSpec {
private final JdbdStmtExecutor executor;
private final List extends Selection> selectionList;
private final io.jdbd.result.QueryResults jdbdResults;
private JdbdBatchQueryResults(JdbdStmtExecutor executor, List extends Selection> selectionList,
io.jdbd.result.QueryResults jdbdResults) {
this.executor = executor;
this.selectionList = selectionList;
this.jdbdResults = jdbdResults;
}
@Override
public final Flux nextQuery(Class resultClass, Consumer consumer) {
final RowReader reader;
final List extends Selection> selectionList = this.selectionList;
if (selectionList.size() == 1) {
reader = new SingleColumnRowReader<>(this.executor, selectionList, resultClass);
} else {
reader = new BeanReader<>(this.executor, selectionList, resultClass);
}
return Flux.from(this.jdbdResults.nextQuery(reader::readOneRow, getJdbdStatesConsumer(consumer)));
}
@Override
public final Flux> nextQueryOptional(Class resultClass, Consumer consumer) {
return Flux.empty();
}
@Override
public final Flux nextQueryObject(Supplier constructor, Consumer consumer) {
final RowReader reader;
reader = new ObjectRowReader<>(this.executor, this.selectionList, constructor, false);
return Flux.from(this.jdbdResults.nextQuery(reader::readOneRow, getJdbdStatesConsumer(consumer)));
}
@Override
public final Flux nextQueryRecord(Function function, Consumer consumer) {
//TODO
return Flux.empty();
}
@Override
public final Flux nextQueryAsFlux() {
return Flux.empty();
}
Consumer getJdbdStatesConsumer(Consumer armyConsumer) {
throw new UnsupportedOperationException();
}
}// JdbdMultiResultSpec
private static final class JdbdResultStates implements ResultStates {
private final io.jdbd.result.ResultStates jdbdStates;
private final JdbdStmtExecutorFactory executorFactory;
private final boolean secondDmlQuery;
private Warning warning;
private Set> optionSet;
private JdbdResultStates(io.jdbd.result.ResultStates jdbdStates,
JdbdStmtExecutorFactory executorFactory, boolean secondDmlQuery) {
this.jdbdStates = jdbdStates;
this.executorFactory = executorFactory;
this.secondDmlQuery = secondDmlQuery;
}
@SuppressWarnings("unchecked")
@Override
public T valueOf(Option option) {
final io.jdbd.session.Option> jdbdOption;
jdbdOption = this.executorFactory.mapToJdbdOption(option);
final Object value;
if (jdbdOption == null) {
value = null;
} else {
value = this.jdbdStates.valueOf(jdbdOption);
}
return (T) value;
}
@Override
public Set> optionSet() {
Set > armyOptionSet = this.optionSet;
if (armyOptionSet == null) {
this.optionSet = armyOptionSet = this.executorFactory.mapArmyOptionSet(this.jdbdStates.optionSet());
}
return armyOptionSet;
}
@Override
public int batchSize() {
return this.jdbdStates.batchSize();
}
@Override
public int batchNo() {
return this.jdbdStates.batchNo();
}
@Override
public int resultNo() {
return this.jdbdStates.resultNo();
}
@Override
public boolean isSupportInsertId() {
return this.jdbdStates.isSupportInsertId();
}
@Override
public long lastInsertedId() throws DataAccessException {
try {
return this.jdbdStates.lastInsertedId();
} catch (JdbdException e) {
throw new DataAccessException(e);
}
}
@Override
public boolean inTransaction() {
try {
return this.jdbdStates.inTransaction();
} catch (Exception e) {
throw wrapException(e);
}
}
@Override
public String message() {
return this.jdbdStates.message();
}
@Override
public boolean hasMoreResult() {
return this.jdbdStates.hasMoreResult();
}
@Override
public boolean hasMoreFetch() {
return this.jdbdStates.hasMoreFetch();
}
@Override
public Warning warning() {
Warning w = this.warning;
if (w != null) {
return w;
}
final io.jdbd.result.Warning jdbdWarning;
jdbdWarning = this.jdbdStates.warning();
if (jdbdWarning != null) {
this.warning = w = new ArmyWarning(jdbdWarning, this.executorFactory);
}
return w;
}
@Override
public long affectedRows() {
return this.jdbdStates.affectedRows();
}
@Override
public boolean hasColumn() {
return this.jdbdStates.hasColumn();
}
@Override
public long rowCount() {
return this.jdbdStates.rowCount();
}
@Override
public boolean isStatesOfSecondDmlQuery() {
return this.secondDmlQuery;
}
}// ArmyResultStates
private static final class ArmyWarning implements Warning {
private final io.jdbd.result.Warning jdbdWarning;
private final JdbdStmtExecutorFactory executorFactory;
private Set > optionSet;
private ArmyWarning(io.jdbd.result.Warning jdbdWarning, JdbdStmtExecutorFactory executorFactory) {
this.jdbdWarning = jdbdWarning;
this.executorFactory = executorFactory;
}
@SuppressWarnings("unchecked")
@Override
public T valueOf(Option option) {
final io.jdbd.session.Option> jdbdOption;
jdbdOption = this.executorFactory.mapToJdbdOption(option);
final Object value;
if (jdbdOption == null) {
value = null;
} else {
value = this.jdbdWarning.valueOf(jdbdOption);
}
return (T) value;
}
@Override
public Set> optionSet() {
Set > armyOptionSet = this.optionSet;
if (armyOptionSet == null) {
this.optionSet = armyOptionSet = this.executorFactory.mapArmyOptionSet(this.jdbdWarning.optionSet());
}
return armyOptionSet;
}
@Override
public String message() {
return this.jdbdWarning.message();
}
} // ArmyWarning
}