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

com.sap.cds.jdbc.generic.AbstractValueBinder Maven / Gradle / Ivy

The newest version!
/************************************************************************
 * © 2021-2024 SAP SE or an SAP affiliate company. All rights reserved. *
 ************************************************************************/
package com.sap.cds.jdbc.generic;

import static com.sap.cds.util.CdsTypeUtils.dateTime;
import static com.sap.cds.util.CdsTypeUtils.timestamp;
import static com.sap.cds.util.CqnStatementUtils.isMediaType;

import java.io.InputStream;
import java.io.Reader;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TimeZone;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sap.cds.CdsDataStoreException;
import com.sap.cds.CdsVector;
import com.sap.cds.impl.parser.StructDataParser;
import com.sap.cds.impl.parser.token.Jsonizer;
import com.sap.cds.jdbc.spi.ValueBinder;
import com.sap.cds.ql.cqn.CqnElementRef;
import com.sap.cds.ql.cqn.CqnSelectListValue;
import com.sap.cds.reflect.CdsArrayedType;
import com.sap.cds.reflect.CdsBaseType;
import com.sap.cds.reflect.CdsStructuredType;
import com.sap.cds.reflect.CdsType;
import com.sap.cds.util.CdsModelUtils;
import com.sap.cds.util.CqnStatementUtils;

public abstract class AbstractValueBinder implements ValueBinder {
	
	private static final Logger logger = LoggerFactory.getLogger(AbstractValueBinder.class);

	protected AbstractValueBinder(int timestampFractionalSeconds, TimeZone timeZone) {
		this.timestampFractionalSeconds = timestampFractionalSeconds;
		this.localCalendar = ThreadLocal.withInitial(() -> Calendar.getInstance(timeZone));
	}

	protected static final ThreadLocal UTC = // NOSONAR
			ThreadLocal.withInitial(() -> Calendar.getInstance(TimeZone.getTimeZone("UTC")));

	private final int timestampFractionalSeconds;
	private final ThreadLocal localCalendar;

	@Override
	@SuppressWarnings("unchecked")
	public  Getter getter(CdsBaseType cdsType, boolean isMediaType) {
		if (cdsType != null) {
			return (Getter) getValueFromResult(cdsType, isMediaType);
		}
		return (result, i) -> (T) result.getObject(i);
	}

	@Override
	@SuppressWarnings("unchecked")
	public  Getter getter(CdsStructuredType targetType, CqnSelectListValue slv) {
		if (slv.value().isRef()) {
			CqnElementRef ref = slv.asRef();
			CdsType type = CdsModelUtils.element(targetType, ref).getType();
			if (type.isArrayed()) {
				CdsType itemsType = type.as(CdsArrayedType.class).getItemsType();
				return (result, i) -> (T) parseListFromJson(result, i, itemsType);
			}
		}
		Optional cdsType = CqnStatementUtils.getCdsType(targetType, slv.value());
		if (cdsType.isEmpty()) {
			logger.debug("Cannot determine CDS type of {}", slv.value());
		}
		return getter(cdsType.orElse(null), isMediaType(targetType, slv));
	}

	@Override
	@SuppressWarnings("unchecked")
	public  T getValue(ResultSet result, int i, CdsBaseType cdsType, boolean isMediaType) throws SQLException {
		return (T) getter(cdsType, isMediaType).get(result, i);
	}

	@Override
	public void setValue(PreparedStatement pstmt, int i, CdsBaseType cdsType, Object value) throws SQLException {
		setter(cdsType).set(pstmt, i, value);
	}

	@Override
	public Setter setter(CdsBaseType cdsType) {
		if (cdsType == null) {
			return this::setValue;
		}
		switch (cdsType) {
			case DATE:
				return this::setLocalDate;
			case DATETIME:
				return (pstmt, i, val) -> setInstant(pstmt, i, dateTime(val));
			case TIME:
				return this::setLocalTime;
			case TIMESTAMP:
				return (pstmt, i, val) -> setInstant(pstmt, i,
						timestamp(val, timestampFractionalSeconds));
			case LARGE_BINARY:
				return this::setLargeBinary;
			case HANA_CLOB:
			case LARGE_STRING:
				return this::setLargeString;
			case VECTOR:
				return this::setRealVector;
			case MAP:
				return this::setMap;
			default:
				return PreparedStatement::setObject;
		}
	}

	protected void setValue(PreparedStatement pstmt, int i, Object value) throws SQLException {
		if (value instanceof Instant instant) {
			setInstant(pstmt, i, instant);
		} else if (value instanceof LocalDate date) {
			setLocalDate(pstmt, i, date);
		} else if (value instanceof LocalTime time) {
			setLocalTime(pstmt, i, time);
		} else if (value instanceof ZonedDateTime time) {
			setZonedDateTime(pstmt, i, time);
		} else if (value instanceof Timestamp timestamp) {
			setInstant(pstmt, i, timestamp.toInstant());
		} else if (value instanceof java.sql.Time time) {
			setLocalTime(pstmt, i, time.toLocalTime());
		} else if (value instanceof java.sql.Date date) {
			setLocalDate(pstmt, i, date.toLocalDate());
		} else if (value instanceof byte[] bytes) {
			pstmt.setBytes(i, bytes);
		} else if (value instanceof Reader reader) {
			setLargeString(pstmt, i, reader);
		} else if (value instanceof InputStream stream) {
			setLargeBinary(pstmt, i, stream);
		} else {
			pstmt.setObject(i, value);
		}
	}

	protected void setRealVector(PreparedStatement pstmt, int i, Object vector) throws SQLException {
		throw new UnsupportedOperationException("cds.Vector is not supported on this data store");
	}

	protected CdsVector getRealVector(ResultSet result, int i) throws SQLException {
		throw new UnsupportedOperationException("cds.Vector is not supported on this data store");
	}

	protected void setLocalTime(PreparedStatement pstmt, int i, Object localTime) throws SQLException {
		if (localTime instanceof LocalTime time) {
			setLocalTime(pstmt, i, time);
		} else if (localTime instanceof String string) {
			setLocalTime(pstmt, i, LocalTime.parse(string));
		} else if (localTime instanceof java.sql.Time time) {
			setLocalTime(pstmt, i, time.toLocalTime());
		} else {
			pstmt.setObject(i, localTime);
		}
	}

	protected abstract void setLocalTime(PreparedStatement pstmt, int i, LocalTime localTime) throws SQLException;

	protected void setLocalDate(PreparedStatement pstmt, int i, Object localDate) throws SQLException {
		if (localDate instanceof LocalDate date) {
			setLocalDate(pstmt, i, date);
		} else if (localDate instanceof String string) {
			setLocalDate(pstmt, i, LocalDate.parse(string));
		} else if (localDate instanceof java.sql.Date date) {
			setLocalDate(pstmt, i, date.toLocalDate());
		} else {
			pstmt.setObject(i, localDate);
		}
	}

	protected abstract void setLocalDate(PreparedStatement pstmt, int i, LocalDate localDate) throws SQLException;

	protected abstract void setInstant(PreparedStatement pstmt, int i, Instant instant) throws SQLException;

	private void setLargeBinary(PreparedStatement pstmt, int i, Object largeBin) throws SQLException {
		if (largeBin instanceof InputStream stream) {
			setLargeBinary(pstmt, i, stream);
		} else {
			pstmt.setObject(i, largeBin);
		}
	}

	protected abstract void setLargeBinary(PreparedStatement result, int i, InputStream stream) throws SQLException;

	private void setZonedDateTime(PreparedStatement pstmt, int i, ZonedDateTime zonedDateTime) throws SQLException {
		setInstant(pstmt, i, dateTime(zonedDateTime.toInstant()));
	}

	protected abstract LocalTime getLocalTime(ResultSet result, int i) throws SQLException;

	protected abstract LocalDate getLocalDate(ResultSet result, int i) throws SQLException;

	protected abstract Instant getInstant(ResultSet result, int i) throws SQLException;

	private void setLargeString(PreparedStatement pstmt, int i, Object largeStr) throws SQLException {
		if (largeStr instanceof Reader reader) {
			setLargeString(pstmt, i, reader);
		} else {
			setString(pstmt, i, largeStr);
		}
	}

	protected abstract void setLargeString(PreparedStatement result, int i, Reader reader) throws SQLException;

	protected abstract Reader getLargeString(ResultSet result, int i) throws SQLException;

	protected abstract InputStream getLargeBinary(ResultSet result, int i) throws SQLException;

	protected Boolean getBoolean(ResultSet result, int i) throws SQLException {
		boolean bool = result.getBoolean(i);
		return result.wasNull() ? null : bool;
	}

	protected Double getDouble(ResultSet result, int i) throws SQLException {
		double dbl = result.getDouble(i);
		return result.wasNull() ? null : dbl;
	}

	protected Short getShort(ResultSet result, int i) throws SQLException {
		short int16 = result.getShort(i);
		return result.wasNull() ? null : int16;
	}

	protected Integer getInt(ResultSet result, int i) throws SQLException {
		int int32 = result.getInt(i);
		return result.wasNull() ? null : int32;
	}

	protected Long getLong(ResultSet result, int i) throws SQLException {
		long int64 = result.getLong(i);
		return result.wasNull() ? null : int64;
	}

	private void setString(PreparedStatement pstmt, int i, Object string) throws SQLException {
		pstmt.setString(i, (String) string);
	}

	protected Float getFloat(ResultSet result, int i) throws SQLException {
		float real = result.getFloat(i);
		return result.wasNull() ? null : real;
	}

	protected void setMap(PreparedStatement pstmt, int i, Object value) throws SQLException {
		if (value == null) {
			setJson(pstmt, i, null);
		} else if (value instanceof Map) {
			byte[] bytes = Jsonizer.bytes(value);
			setJson(pstmt, i, bytes);
		} else {
			throw new CdsDataStoreException("Unsupported data format for cds.Map type: " + value.getClass());
		}
	}

	protected void setJson(PreparedStatement pstmt, int i, Object json) throws SQLException {
		if (json == null) {
			pstmt.setNull(i, java.sql.Types.OTHER);
		} else if (json instanceof byte[] bytes) {
			pstmt.setBytes(i, bytes);
		} else if (json instanceof String str) {
			pstmt.setString(i, str);
		} else {
			throw new CdsDataStoreException("Unsupported data format for JSON: " + json.getClass());
		}
	}

	protected List parseListFromJson(ResultSet result, int i, CdsType rowType) throws SQLException {
		String json = getJson(result, i);
		if (json == null) {
			return null;
		}

		return StructDataParser.parseArrayOf(rowType, json);
	}

	protected Object parseObjectFromJson(ResultSet result, int i) throws SQLException {
		String json = getJson(result, i);
		if (json == null) {
			return null;
		}

		return StructDataParser.parse(json);
	}

	protected String getJson(ResultSet result, int i) throws SQLException {
		return result.getString(i);
	}

	@SuppressWarnings("deprecation")
	private Getter getValueFromResult(CdsBaseType cdsType, boolean isMediaType) {
		switch (cdsType) {
			case BOOLEAN:
				return this::getBoolean;
			case DATE:
				return this::getLocalDate;
			case DATETIME:
				return (result, i) -> dateTime(getInstant(result, i));
			case DECIMAL:
			case HANA_SMALLDECIMAL:
			case DECIMAL_FLOAT:
				return ResultSet::getBigDecimal;
			case DOUBLE:
				return this::getDouble;
			case INTEGER:
			case INT32:
				return this::getInt;
			case INTEGER64:
			case INT64:
				return this::getLong;
			case LARGE_BINARY:
				if (isMediaType) {
					return this::getLargeBinary;
				}
			case HANA_BINARY:
			case BINARY:
				return ResultSet::getBytes;
			case HANA_CLOB:
			case LARGE_STRING:
				if (isMediaType) {
					return this::getLargeString;
				}
			case STRING:
				return ResultSet::getString;
			case TIME:
				return this::getLocalTime;
			case TIMESTAMP:
				return (result, i) -> timestamp(getInstant(result, i), timestampFractionalSeconds);
			case UINT8:
			case INT16:
			case HANA_TINYINT:
			case HANA_SMALLINT:
				return this::getShort;
			case HANA_REAL:
				return this::getFloat;
			case VECTOR:
				return this::getRealVector;
			case MAP:
				return this::parseObjectFromJson;
			default:
				return (result, i) -> result.getObject(i);
		}
	}

	protected Calendar getCalendarForDefaultTimeZone() {
		return localCalendar.get();
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy