package com.microsoft.sqlserver.jdbc;
import java.text.MessageFormat;
import java.util.Calendar;
final class Column {
private TypeInfo typeInfo;
private CryptoMetadata cryptoMetadata;
private SqlVariant internalVariant;
final void setInternalVariant(SqlVariant type) {
this.internalVariant = type;
}
final SqlVariant getInternalVariant() {
return this.internalVariant;
}
final TypeInfo getTypeInfo() {
return typeInfo;
}
private DTV updaterDTV;
private final DTV getterDTV = new DTV();
private JDBCType jdbcTypeSetByUser = null;
private int valueLength = 0;
private String columnName;
final void setColumnName(String name) {
columnName = name;
}
final String getColumnName() {
return columnName;
}
private String baseColumnName;
final void setBaseColumnName(String name) {
baseColumnName = name;
}
final String getBaseColumnName() {
return baseColumnName;
}
private int tableNum;
final void setTableNum(int num) {
tableNum = num;
}
final int getTableNum() {
return tableNum;
}
private int infoStatus;
final void setInfoStatus(int status) {
infoStatus = status;
}
final boolean hasDifferentName() {
return 0 != (infoStatus & TDS.COLINFO_STATUS_DIFFERENT_NAME);
}
final boolean isHidden() {
return 0 != (infoStatus & TDS.COLINFO_STATUS_HIDDEN);
}
final boolean isKey() {
return 0 != (infoStatus & TDS.COLINFO_STATUS_KEY);
}
final boolean isExpression() {
return 0 != (infoStatus & TDS.COLINFO_STATUS_EXPRESSION);
}
final boolean isUpdatable() {
return !isExpression() && !isHidden() && tableName.getObjectName().length() > 0;
}
private SQLIdentifier tableName;
final void setTableName(SQLIdentifier name) {
tableName = name;
}
final SQLIdentifier getTableName() {
return tableName;
}
ColumnFilter filter;
Column(TypeInfo typeInfo, String columnName, SQLIdentifier tableName, CryptoMetadata cryptoMeta) {
this.typeInfo = typeInfo;
this.columnName = columnName;
this.baseColumnName = columnName;
this.tableName = tableName;
this.cryptoMetadata = cryptoMeta;
}
CryptoMetadata getCryptoMetadata() {
return cryptoMetadata;
}
final void clear() {
getterDTV.clear();
}
final void skipValue(TDSReader tdsReader, boolean isDiscard) throws SQLServerException {
getterDTV.skipValue(typeInfo, tdsReader, isDiscard);
}
final void initFromCompressedNull() {
getterDTV.initFromCompressedNull();
}
void setFilter(ColumnFilter filter) {
this.filter = filter;
}
final boolean isNull() {
return getterDTV.isNull();
}
final boolean isInitialized() {
return getterDTV.isInitialized();
}
Object getValue(JDBCType jdbcType, InputStreamGetterArgs getterArgs, Calendar cal,
TDSReader tdsReader) throws SQLServerException {
Object value = getterDTV.getValue(jdbcType, typeInfo.getScale(), getterArgs, cal, typeInfo, cryptoMetadata,
tdsReader);
setInternalVariant(getterDTV.getInternalVariant());
return (null != filter) ? filter.apply(value, jdbcType) : value;
}
int getInt(TDSReader tdsReader) throws SQLServerException {
return (Integer) getValue(JDBCType.INTEGER, null, null, tdsReader);
}
void updateValue(JDBCType jdbcType, Object value, JavaType javaType, StreamSetterArgs streamSetterArgs,
Calendar cal, Integer scale, SQLServerConnection con,
SQLServerStatementColumnEncryptionSetting stmtColumnEncriptionSetting, Integer precision,
boolean forceEncrypt, int parameterIndex) throws SQLServerException {
SSType ssType = typeInfo.getSSType();
if (null != cryptoMetadata) {
if (SSType.VARBINARYMAX == cryptoMetadata.baseTypeInfo.getSSType() && JDBCType.BINARY == jdbcType) {
jdbcType = cryptoMetadata.baseTypeInfo.getSSType().getJDBCType();
}
if (null != value) {
if (JDBCType.TINYINT == cryptoMetadata.getBaseTypeInfo().getSSType().getJDBCType()
&& javaType == JavaType.SHORT) {
if (value instanceof Boolean) {
if ((boolean) value) {
value = 1;
} else {
value = 0;
}
}
String stringValue = "" + value;
Short shortValue = Short.valueOf(stringValue);
if (shortValue >= 0 && shortValue <= 255) {
value = shortValue.byteValue();
javaType = JavaType.BYTE;
jdbcType = JDBCType.TINYINT;
}
}
}
else if (jdbcType.isBinary()) {
jdbcType = cryptoMetadata.getBaseTypeInfo().getSSType().getJDBCType();
}
}
if (null == scale && null != cryptoMetadata) {
scale = cryptoMetadata.getBaseTypeInfo().getScale();
}
if (null != cryptoMetadata && (JDBCType.CHAR == jdbcType || JDBCType.VARCHAR == jdbcType)) {
if (JDBCType.NVARCHAR == cryptoMetadata.getBaseTypeInfo().getSSType().getJDBCType()
|| JDBCType.NCHAR == cryptoMetadata.getBaseTypeInfo().getSSType().getJDBCType()
|| JDBCType.LONGNVARCHAR == cryptoMetadata.getBaseTypeInfo().getSSType().getJDBCType()) {
jdbcType = cryptoMetadata.getBaseTypeInfo().getSSType().getJDBCType();
}
}
if (Util.shouldHonorAEForParameters(stmtColumnEncriptionSetting, con)) {
if ((null == cryptoMetadata) && forceEncrypt) {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_ForceEncryptionTrue_HonorAETrue_UnencryptedColumnRS"));
Object[] msgArgs = {parameterIndex};
throw new SQLServerException(null, form.format(msgArgs), null, 0, false);
} else {
setJdbcTypeSetByUser(jdbcType);
this.valueLength = Util.getValueLengthBaseOnJavaType(value, javaType, precision, scale, jdbcType);
if (null != cryptoMetadata) {
if (JDBCType.NCHAR == cryptoMetadata.getBaseTypeInfo().getSSType().getJDBCType()
|| JDBCType.NVARCHAR == cryptoMetadata.getBaseTypeInfo().getSSType().getJDBCType()
|| JDBCType.LONGNVARCHAR == cryptoMetadata.getBaseTypeInfo().getSSType().getJDBCType()) {
this.valueLength = valueLength * 2;
}
}
}
} else {
if (forceEncrypt) {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_ForceEncryptionTrue_HonorAEFalseRS"));
Object[] msgArgs = {parameterIndex};
throw new SQLServerException(null, form.format(msgArgs), null, 0, false);
}
}
if (null != streamSetterArgs) {
if (!streamSetterArgs.streamType.convertsTo(typeInfo))
DataTypes.throwConversionError(streamSetterArgs.streamType.toString(), ssType.toString());
} else {
if (null != cryptoMetadata) {
if ((JDBCType.UNKNOWN == jdbcType) && (value instanceof java.util.UUID)) {
javaType = JavaType.STRING;
jdbcType = JDBCType.GUID;
setJdbcTypeSetByUser(jdbcType);
}
SSType basicSSType = cryptoMetadata.baseTypeInfo.getSSType();
if (!jdbcType.convertsTo(basicSSType))
DataTypes.throwConversionError(jdbcType.toString(), ssType.toString());
JDBCType jdbcTypeFromSSType = getJDBCTypeFromBaseSSType(basicSSType, jdbcType);
if (jdbcTypeFromSSType != jdbcType) {
setJdbcTypeSetByUser(jdbcTypeFromSSType);
jdbcType = jdbcTypeFromSSType;
this.valueLength = Util.getValueLengthBaseOnJavaType(value, javaType, precision, scale, jdbcType);
}
} else {
if (!jdbcType.convertsTo(ssType))
DataTypes.throwConversionError(jdbcType.toString(), ssType.toString());
}
}
if ((JDBCType.DATETIMEOFFSET == jdbcType || JavaType.DATETIMEOFFSET == javaType) && !con.isKatmaiOrLater()) {
throw new SQLServerException(SQLServerException.getErrString("R_notSupported"),
SQLState.DATA_EXCEPTION_NOT_SPECIFIC, DriverError.NOT_SET, null);
}
if ((null != cryptoMetadata) && (con.sendStringParametersAsUnicode()) && (JavaType.STRING == javaType
|| JavaType.READER == javaType || JavaType.CLOB == javaType || JavaType.OBJECT == javaType)) {
jdbcType = getSSPAUJDBCType(jdbcType);
}
if ((SSType.NCHAR == ssType || SSType.NVARCHAR == ssType || SSType.NVARCHARMAX == ssType
|| SSType.NTEXT == ssType || SSType.XML == ssType) &&
(JDBCType.CHAR == jdbcType || JDBCType.VARCHAR == jdbcType || JDBCType.LONGVARCHAR == jdbcType
|| JDBCType.CLOB == jdbcType)) {
jdbcType = (JDBCType.CLOB == jdbcType) ? JDBCType.NCLOB : JDBCType.NVARCHAR;
}
else if ((SSType.BINARY == ssType || SSType.VARBINARY == ssType || SSType.VARBINARYMAX == ssType
|| SSType.IMAGE == ssType || SSType.UDT == ssType) &&
(JDBCType.CHAR == jdbcType || JDBCType.VARCHAR == jdbcType || JDBCType.LONGVARCHAR == jdbcType)) {
jdbcType = JDBCType.VARBINARY;
}
else if ((JDBCType.TIMESTAMP == jdbcType || JDBCType.DATE == jdbcType || JDBCType.TIME == jdbcType
|| JDBCType.DATETIMEOFFSET == jdbcType) &&
(SSType.CHAR == ssType || SSType.VARCHAR == ssType || SSType.VARCHARMAX == ssType
|| SSType.TEXT == ssType || SSType.NCHAR == ssType || SSType.NVARCHAR == ssType
|| SSType.NVARCHARMAX == ssType || SSType.NTEXT == ssType)) {
jdbcType = JDBCType.NCHAR;
}
if (null == updaterDTV)
updaterDTV = new DTV();
updaterDTV.setValue(typeInfo.getSQLCollation(), jdbcType, value, javaType, streamSetterArgs, cal, scale, con,
false);
}
private static JDBCType getSSPAUJDBCType(JDBCType jdbcType) {
switch (jdbcType) {
case CHAR:
return JDBCType.NCHAR;
case VARCHAR:
return JDBCType.NVARCHAR;
case LONGVARCHAR:
return JDBCType.LONGNVARCHAR;
case CLOB:
return JDBCType.NCLOB;
default:
return jdbcType;
}
}
private static JDBCType getJDBCTypeFromBaseSSType(SSType basicSSType, JDBCType jdbcType) {
switch (jdbcType) {
case TIMESTAMP:
if (SSType.DATETIME == basicSSType)
return JDBCType.DATETIME;
else if (SSType.SMALLDATETIME == basicSSType)
return JDBCType.SMALLDATETIME;
return jdbcType;
case NUMERIC:
case DECIMAL:
if (SSType.MONEY == basicSSType)
return JDBCType.MONEY;
if (SSType.SMALLMONEY == basicSSType)
return JDBCType.SMALLMONEY;
return jdbcType;
case CHAR:
if (SSType.GUID == basicSSType)
return JDBCType.GUID;
if (SSType.VARCHARMAX == basicSSType)
return JDBCType.LONGVARCHAR;
return jdbcType;
default:
return jdbcType;
}
}
boolean hasUpdates() {
return null != updaterDTV;
}
void cancelUpdates() {
updaterDTV = null;
}
void sendByRPC(TDSWriter tdsWriter, SQLServerConnection conn) throws SQLServerException {
if (null == updaterDTV)
return;
try {
updaterDTV.sendCryptoMetaData(cryptoMetadata, tdsWriter);
updaterDTV.setJdbcTypeSetByUser(getJdbcTypeSetByUser(), getValueLength());
updaterDTV.sendByRPC(baseColumnName, typeInfo,
null != cryptoMetadata ? cryptoMetadata.getBaseTypeInfo().getSQLCollation()
: typeInfo.getSQLCollation(),
null != cryptoMetadata ? cryptoMetadata.getBaseTypeInfo().getPrecision() : typeInfo.getPrecision(),
null != cryptoMetadata ? cryptoMetadata.getBaseTypeInfo().getScale() : typeInfo.getScale(), false,
tdsWriter, conn);
} finally {
updaterDTV.sendCryptoMetaData(null, tdsWriter);
}
}
JDBCType getJdbcTypeSetByUser() {
return jdbcTypeSetByUser;
}
void setJdbcTypeSetByUser(JDBCType jdbcTypeSetByUser) {
this.jdbcTypeSetByUser = jdbcTypeSetByUser;
}
int getValueLength() {
return valueLength;
}
}
abstract class ColumnFilter {
abstract Object apply(Object value, JDBCType jdbcType) throws SQLServerException;
}