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.
/*
* Microsoft JDBC Driver for SQL Server Copyright(c) Microsoft Corporation All rights reserved. This program is made
* available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
package com.microsoft.sqlserver.jdbc;
import java.text.MessageFormat;
import java.util.Calendar;
/**
* Column represents a database column definition (meta data) within a result set.
*/
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();
// updated if sendStringParametersAsUnicode=true for setNString, setNCharacterStream, and setNClob methods
private JDBCType jdbcTypeSetByUser = null;
// set length of value for variable length type (String)
private int valueLength = 0;
// The column name, which may be an alias, that is used with value setters and getters.
private String columnName;
final void setColumnName(String name) {
columnName = name;
}
final String getColumnName() {
return columnName;
}
// The base column name which is the actual column name in an underlying table.
// This name must be used, rather than the column name above, when inserting or
// updating rows in the table.
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;
/**
* Create a new column
*
* @param typeInfo
* the column TYPE_INFO
* @param columnName
* the column name
* @param tableName
* the column's table name
* @param cryptoMeta
* the column's crypto metadata
*/
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;
}
/**
* Clears the values associated with this column.
*/
final void clear() {
getterDTV.clear();
}
/**
* Skip this column.
*
* The column's value may or may not already be marked. If this column's value has not yet been marked, this
* function assumes that the value is located at the current position in the response.
*/
final void skipValue(TDSReader tdsReader, boolean isDiscard) throws SQLServerException {
getterDTV.skipValue(typeInfo, tdsReader, isDiscard);
}
/**
* Sets Null value on the getterDTV of a column
*/
final void initFromCompressedNull() {
getterDTV.initFromCompressedNull();
}
void setFilter(ColumnFilter filter) {
this.filter = filter;
}
/**
* Returns whether the value of this column is SQL NULL.
*
* If the column has not yet been read from the response then this method returns false.
*/
final boolean isNull() {
return getterDTV.isNull();
}
/**
* Returns true if the column value is initialized to some value by reading the stream from server i.e. it returns
* true, if impl of getterDTV is not set to null
*/
final boolean isInitialized() {
return getterDTV.isInitialized();
}
/**
* Retrieves this colum's value.
*
* If the column has not yet been read from the response then this method reads it.
*/
Object getValue(JDBCType jdbcType, InputStreamGetterArgs getterArgs, Calendar cal, TDSReader tdsReader,
SQLServerStatement statement) throws SQLServerException {
Object value = getterDTV.getValue(jdbcType, typeInfo.getScale(), getterArgs, cal, typeInfo, cryptoMetadata,
tdsReader, statement);
setInternalVariant(getterDTV.getInternalVariant());
return (null != filter) ? filter.apply(value, jdbcType) : value;
}
int getInt(TDSReader tdsReader, SQLServerStatement statement) throws SQLServerException {
return (Integer) getValue(JDBCType.INTEGER, null, null, tdsReader, statement);
}
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) {
// for encrypted tinyint, we need to convert short value to byte value, otherwise it would be sent as
// smallint
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;
}
}
}
// if the column is encrypted and value is null, get the real column type instead of binary types
else if (jdbcType.isBinary()) {
jdbcType = cryptoMetadata.getBaseTypeInfo().getSSType().getJDBCType();
}
}
if (null == scale && null != cryptoMetadata) {
scale = cryptoMetadata.getBaseTypeInfo().getScale();
}
// if jdbcType is char or varchar, check if the column is actually char/varchar or nchar/nvarchar
// in order to make updateString() work with encrypted Nchar typpes
if (null != cryptoMetadata && (JDBCType.CHAR == jdbcType || JDBCType.VARCHAR == jdbcType)
&& (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);
// for update encrypted nchar or nvarchar value on result set, must double the value length,
// otherwise, the data is truncated.
if (null != cryptoMetadata
&& (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) {
// For GUID, set the JDBCType before checking for conversion
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());
}
}
// DateTimeOffset is not supported with SQL Server versions earlier than Katmai
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);
}
// sendStringParametersAsUnicode
// If set to true, this connection property tells the driver to send textual parameters
// to the server as Unicode rather than MBCS. This is accomplished here by re-tagging
// the value with the appropriate corresponding Unicode type.
if ((null != cryptoMetadata) && (con.sendStringParametersAsUnicode()) && (JavaType.STRING == javaType
|| JavaType.READER == javaType || JavaType.CLOB == javaType || JavaType.OBJECT == javaType)) {
jdbcType = getSSPAUJDBCType(jdbcType);
}
// Cheesy checks determine whether updating is allowed, but do not determine HOW to do
// the update (i.e. what JDBC type to use for the update). The JDBC type to use depends
// on the SQL Server type of the column and the JDBC type requested.
//
// In most cases the JDBCType to use is just the requested JDBCType. But in some cases
// a client side type conversion is necessary because SQL Server does not directly support
// conversion from the requested JDBCType to the column SSType, or the driver needs to
// provide special data conversion.
// Update of Unicode SSType from textual JDBCType: Use Unicode.
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;
}
// Update of binary SSType from textual JDBCType: Convert hex to binary.
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;
}
// Update of textual SSType from temporal JDBCType requires
// client-side conversion from temporal to textual.
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;
}
// Lazily create the updater DTV on first update of the column
if (null == updaterDTV)
updaterDTV = new DTV();
// Set the column's value
updaterDTV.setValue(typeInfo.getSQLCollation(), jdbcType, value, javaType, streamSetterArgs, cal, scale, con,
false);
}
/**
* Used when sendStringParametersAsUnicode=true to derive the appropriate National Character Set JDBC type
* corresponding to the specified JDBC type.
*/
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, SQLServerStatement statement) throws SQLServerException {
// If the column has had no updates then there is nothing to send
if (null == updaterDTV)
return;
try {
// this is for updateRow() stuff
updaterDTV.sendCryptoMetaData(cryptoMetadata, tdsWriter);
updaterDTV.setJdbcTypeSetByUser(getJdbcTypeSetByUser(), getValueLength());
// Otherwise, send the updated value via RPC
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, // isOutParameter
// (always
// false
// for
// column
// updates)
tdsWriter, statement);
} finally {
// this is for updateRow() stuff
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;
}