in src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java [435:903]
private void setTypeDefinition(DTV dtv) {
switch (dtv.getJdbcType()) {
case TINYINT:
param.typeDefinition = SSType.TINYINT.toString();
break;
case SMALLINT:
param.typeDefinition = SSType.SMALLINT.toString();
break;
case INTEGER:
param.typeDefinition = SSType.INTEGER.toString();
break;
case BIGINT:
param.typeDefinition = SSType.BIGINT.toString();
break;
case REAL:
// sp_describe_parameter_encryption must be queried as real for AE
if (param.shouldHonorAEForParameter && (null != jdbcTypeSetByUser)
&& !(null == param.getCryptoMetadata() && param.renewDefinition)) {
/*
* This means AE is ON in the connection, and (1) this is either the first round to SQL Server
* to get encryption meta data, or (2) this is the second round of renewing meta data and
* parameter is encrypted In both of these cases we need to send specific type info, otherwise
* generic type info can be used as before.
*/
param.typeDefinition = SSType.REAL.toString();
} else {
// use FLOAT if column is not encrypted
param.typeDefinition = SSType.FLOAT.toString();
}
break;
case FLOAT:
case DOUBLE:
param.typeDefinition = SSType.FLOAT.toString();
break;
case DECIMAL:
case NUMERIC:
// First, bound the scale by the maximum allowed by SQL Server
if (scale > SQLServerConnection.maxDecimalPrecision)
scale = SQLServerConnection.maxDecimalPrecision;
// Next, prepare with the largest of:
// - the value's scale (initial value, as limited above)
// - the specified input scale (if any)
// - the registered output scale
Integer inScale = dtv.getScale();
if (null != inScale && scale < inScale)
scale = inScale;
if (param.isOutput() && scale < param.getOutScale())
scale = param.getOutScale();
if (param.shouldHonorAEForParameter && (null != jdbcTypeSetByUser)
&& !(null == param.getCryptoMetadata() && param.renewDefinition)) {
/*
* This means AE is ON in the connection, and (1) this is either the first round to SQL Server
* to get encryption meta data, or (2) this is the second round of renewing meta data and
* parameter is encrypted In both of these cases we need to send specific type info, otherwise
* generic type info can be used as before.
*/
if (0 == valueLength) {
// for prepared statement and callable statement, There are only two cases where valueLength
// is 0:
// 1. when the parameter is output parameter
// 2. for input parameter, the value is null
// so, here, if the decimal parameter is encrypted and it is null and it is not outparameter
// then we set precision as the default precision instead of max precision
if (!isOutput()) {
param.typeDefinition = "decimal(" + SQLServerConnection.defaultDecimalPrecision + ", "
+ scale + ")";
}
} else {
if (SQLServerConnection.defaultDecimalPrecision >= valueLength) {
param.typeDefinition = "decimal(" + SQLServerConnection.defaultDecimalPrecision + ","
+ scale + ")";
if (SQLServerConnection.defaultDecimalPrecision < (valueLength + scale)) {
param.typeDefinition = "decimal("
+ (SQLServerConnection.defaultDecimalPrecision + scale) + "," + scale + ")";
}
} else {
param.typeDefinition = "decimal(" + SQLServerConnection.maxDecimalPrecision + ","
+ scale + ")";
}
}
if (isOutput()) {
param.typeDefinition = "decimal(" + SQLServerConnection.maxDecimalPrecision + ", " + scale
+ ")";
}
if (userProvidesPrecision) {
param.typeDefinition = "decimal(" + valueLength + "," + scale + ")";
}
} else
param.typeDefinition = "decimal(" + SQLServerConnection.maxDecimalPrecision + "," + scale + ")";
break;
case MONEY:
param.typeDefinition = SSType.MONEY.toString();
break;
case SMALLMONEY:
param.typeDefinition = SSType.MONEY.toString();
if (param.shouldHonorAEForParameter
&& !(null == param.getCryptoMetadata() && param.renewDefinition)) {
param.typeDefinition = SSType.SMALLMONEY.toString();
}
break;
case BIT:
case BOOLEAN:
param.typeDefinition = SSType.BIT.toString();
break;
case LONGVARBINARY:
case BLOB:
param.typeDefinition = VARBINARY_MAX;
break;
case BINARY:
case VARBINARY:
// To avoid the server side cost of re-preparing, once a "long" type, always a "long" type...
if (VARBINARY_MAX.equals(param.typeDefinition) || IMAGE.equals(param.typeDefinition))
break;
if (param.shouldHonorAEForParameter && (null != jdbcTypeSetByUser)
&& !(null == param.getCryptoMetadata() && param.renewDefinition)) {
/*
* This means AE is ON in the connection, and (1) this is either the first round to SQL Server
* to get encryption meta data, or (2) this is the second round of renewing meta data and
* parameter is encrypted In both of these cases we need to send specific type info, otherwise
* generic type info can be used as before.
*/
if (0 == valueLength) {
// Workaround for the issue when inserting empty string and null into encrypted columns
param.typeDefinition = "varbinary(1)";
valueLength++;
} else {
param.typeDefinition = "varbinary(" + valueLength + ")";
}
if (JDBCType.LONGVARBINARY == jdbcTypeSetByUser) {
param.typeDefinition = VARBINARY_MAX;
}
} else
param.typeDefinition = VARBINARY_8K;
break;
case DATE:
// Bind DATE values to pre-Katmai servers as DATETIME (which has no DATE-only type).
param.typeDefinition = con.isKatmaiOrLater() ? SSType.DATE.toString() : SSType.DATETIME.toString();
break;
case TIME:
if (param.shouldHonorAEForParameter
&& !(null == param.getCryptoMetadata() && param.renewDefinition)) {
/*
* This means AE is ON in the connection, and (1) this is either the first round to SQL Server
* to get encryption meta data, or (2) this is the second round of renewing meta data and
* parameter is encrypted In both of these cases we need to send specific type info, otherwise
* generic type info can be used as before.
*/
if (userProvidesScale) {
param.typeDefinition = SSType.TIME.toString() + "(" + outScale + ")";
} else {
param.typeDefinition = SSType.TIME.toString() + "(" + valueLength + ")";
}
} else {
param.typeDefinition = con.getSendTimeAsDatetime() ? SSType.DATETIME.toString()
: SSType.TIME.toString();
}
break;
case TIMESTAMP:
// Bind TIMESTAMP values to pre-Katmai servers as DATETIME. Bind TIMESTAMP values to
// Katmai and later servers as DATETIME2 to take advantage of increased precision.
if (param.shouldHonorAEForParameter
&& !(null == param.getCryptoMetadata() && param.renewDefinition)) {
/*
* This means AE is ON in the connection, and (1) this is either the first round to SQL Server
* to get encryption meta data, or (2) this is the second round of renewing meta data and
* parameter is encrypted In both of these cases we need to send specific type info, otherwise
* generic type info can be used as before.
*/
if (userProvidesScale) {
param.typeDefinition = con
.isKatmaiOrLater() ? (SSType.DATETIME2.toString() + "(" + outScale + ")")
: (SSType.DATETIME.toString());
} else {
param.typeDefinition = con.isKatmaiOrLater()
? (SSType.DATETIME2.toString() + "("
+ valueLength + ")")
: SSType.DATETIME.toString();
}
} else {
param.typeDefinition = con.isKatmaiOrLater() ? SSType.DATETIME2.toString()
: SSType.DATETIME.toString();
}
break;
case DATETIME:
// send as Datetime by default
param.typeDefinition = SSType.DATETIME2.toString();
if (param.shouldHonorAEForParameter
&& !(null == param.getCryptoMetadata() && param.renewDefinition)) {
param.typeDefinition = SSType.DATETIME.toString();
}
if (!param.shouldHonorAEForParameter) {
// if AE is off and it is output parameter of stored procedure, sent it as datetime2(3)
// otherwise it returns incorrect milliseconds.
if (param.isOutput()) {
param.typeDefinition = SSType.DATETIME2.toString() + "(" + outScale + ")";
}
} else {
// when AE is on, set it to Datetime by default,
// However, if column is not encrypted and it is output parameter of stored procedure,
// renew it to datetime2(3)
if (null == param.getCryptoMetadata() && param.renewDefinition) {
if (param.isOutput()) {
param.typeDefinition = SSType.DATETIME2.toString() + "(" + outScale + ")";
}
break;
}
}
break;
case SMALLDATETIME:
param.typeDefinition = SSType.DATETIME2.toString();
if (param.shouldHonorAEForParameter
&& !(null == param.getCryptoMetadata() && param.renewDefinition)) {
param.typeDefinition = SSType.SMALLDATETIME.toString();
}
break;
case TIME_WITH_TIMEZONE:
case TIMESTAMP_WITH_TIMEZONE:
case DATETIMEOFFSET:
if (param.shouldHonorAEForParameter
&& !(null == param.getCryptoMetadata() && param.renewDefinition)) {
/*
* This means AE is ON in the connection, and (1) this is either the first round to SQL Server
* to get encryption meta data, or (2) this is the second round of renewing meta data and
* parameter is encrypted In both of these cases we need to send specific type info, otherwise
* generic type info can be used as before.
*/
if (userProvidesScale) {
param.typeDefinition = SSType.DATETIMEOFFSET.toString() + "(" + outScale + ")";
} else {
param.typeDefinition = SSType.DATETIMEOFFSET.toString() + "(" + valueLength + ")";
}
} else {
param.typeDefinition = SSType.DATETIMEOFFSET.toString();
}
break;
case LONGVARCHAR:
case CLOB:
param.typeDefinition = VARCHAR_MAX;
break;
case CHAR:
case VARCHAR:
// To avoid the server side cost of re-preparing, once a "long" type, always a "long" type...
if (VARCHAR_MAX.equals(param.typeDefinition) || TEXT.equals(param.typeDefinition))
break;
// Adding for case useColumnEncryption=true & sendStringParametersAsUnicode=false
if (param.shouldHonorAEForParameter && (null != jdbcTypeSetByUser)
&& !(null == param.getCryptoMetadata() && param.renewDefinition)) {
/*
* This means AE is ON in the connection, and (1) this is either the first round to SQL Server
* to get encryption meta data, or (2) this is the second round of renewing meta data and
* parameter is encrypted In both of these cases we need to send specific type info, otherwise
* generic type info can be used as before.
*/
if (0 == valueLength) {
// Workaround for the issue when inserting empty string and null into encrypted columns
param.typeDefinition = "varchar(1)";
valueLength++;
} else {
param.typeDefinition = "varchar(" + valueLength + ")";
if (DataTypes.SHORT_VARTYPE_MAX_BYTES <= valueLength) {
param.typeDefinition = VARCHAR_MAX;
}
}
} else
param.typeDefinition = VARCHAR_8K;
break;
case LONGNVARCHAR:
if (param.shouldHonorAEForParameter
&& !(null == param.getCryptoMetadata() && param.renewDefinition)) {
/*
* This means AE is ON in the connection, and (1) this is either the first round to SQL Server
* to get encryption meta data, or (2) this is the second round of renewing meta data and
* parameter is encrypted In both of these cases we need to send specific type info, otherwise
* generic type info can be used as before.
*/
if ((null != jdbcTypeSetByUser)
&& ((jdbcTypeSetByUser == JDBCType.VARCHAR) || (jdbcTypeSetByUser == JDBCType.CHAR)
|| (jdbcTypeSetByUser == JDBCType.LONGVARCHAR))) {
if (0 == valueLength) {
// Workaround for the issue when inserting empty string and null into encrypted columns
param.typeDefinition = "varchar(1)";
valueLength++;
} else if (DataTypes.SHORT_VARTYPE_MAX_BYTES < valueLength) {
param.typeDefinition = VARCHAR_MAX;
} else {
param.typeDefinition = "varchar(" + valueLength + ")";
}
if (jdbcTypeSetByUser == JDBCType.LONGVARCHAR) {
param.typeDefinition = VARCHAR_MAX;
}
} else if ((null != jdbcTypeSetByUser) && (jdbcTypeSetByUser == JDBCType.NVARCHAR
|| jdbcTypeSetByUser == JDBCType.LONGNVARCHAR)) {
if (0 == valueLength) {
// Workaround for the issue when inserting empty string and null into encrypted columns
param.typeDefinition = "nvarchar(1)";
valueLength++;
} else if (DataTypes.SHORT_VARTYPE_MAX_CHARS < valueLength) {
param.typeDefinition = NVARCHAR_MAX;
} else {
param.typeDefinition = "nvarchar(" + valueLength + ")";
}
if (jdbcTypeSetByUser == JDBCType.LONGNVARCHAR) {
param.typeDefinition = NVARCHAR_MAX;
}
} else { // used if setNull() is called with java.sql.Types.NCHAR
if (0 == valueLength) {
// Workaround for the issue when inserting empty string and null into encrypted columns
param.typeDefinition = "nvarchar(1)";
valueLength++;
} else {
param.typeDefinition = "nvarchar(" + valueLength + ")";
if (DataTypes.SHORT_VARTYPE_MAX_BYTES <= valueLength) {
param.typeDefinition = NVARCHAR_MAX;
}
}
}
break;
} else
param.typeDefinition = NVARCHAR_MAX;
break;
case NCLOB:
// do not need to check if AE is enabled or not,
// because NCLOB does not work with it
param.typeDefinition = NVARCHAR_MAX;
break;
case NCHAR:
case NVARCHAR:
// To avoid the server side cost of re-preparing, once a "long" type, always a "long" type...
if (NVARCHAR_MAX.equals(param.typeDefinition) || NTEXT.equals(param.typeDefinition))
break;
if (param.shouldHonorAEForParameter
&& !(null == param.getCryptoMetadata() && param.renewDefinition)) {
/*
* This means AE is ON in the connection, and (1) this is either the first round to SQL Server
* to get encryption meta data, or (2) this is the second round of renewing meta data and
* parameter is encrypted In both of these cases we need to send specific type info, otherwise
* generic type info can be used as before.
*/
if ((null != jdbcTypeSetByUser)
&& ((jdbcTypeSetByUser == JDBCType.VARCHAR) || (jdbcTypeSetByUser == JDBCType.CHAR)
|| (JDBCType.LONGVARCHAR == jdbcTypeSetByUser))) {
if (0 == valueLength) {
// Workaround for the issue when inserting empty string and null into encrypted columns
param.typeDefinition = "varchar(1)";
valueLength++;
} else {
param.typeDefinition = "varchar(" + valueLength + ")";
if (DataTypes.SHORT_VARTYPE_MAX_BYTES < valueLength) {
param.typeDefinition = VARCHAR_MAX;
}
}
if (JDBCType.LONGVARCHAR == jdbcTypeSetByUser) {
param.typeDefinition = VARCHAR_MAX;
}
} else if ((null != jdbcTypeSetByUser)
&& ((jdbcTypeSetByUser == JDBCType.NVARCHAR) || (jdbcTypeSetByUser == JDBCType.NCHAR)
|| (JDBCType.LONGNVARCHAR == jdbcTypeSetByUser))) {
if (0 == valueLength) {
// Workaround for the issue when inserting empty string and null into encrypted columns
param.typeDefinition = "nvarchar(1)";
valueLength++;
} else {
param.typeDefinition = "nvarchar(" + valueLength + ")";
if (DataTypes.SHORT_VARTYPE_MAX_BYTES <= valueLength) {
param.typeDefinition = NVARCHAR_MAX;
}
}
if (JDBCType.LONGNVARCHAR == jdbcTypeSetByUser) {
param.typeDefinition = NVARCHAR_MAX;
}
} else { // used if setNull() is called with java.sql.Types.NCHAR
if (0 == valueLength) {
// Workaround for the issue when inserting empty string and null into encrypted columns
param.typeDefinition = "nvarchar(1)";
valueLength++;
} else {
param.typeDefinition = "nvarchar(" + valueLength + ")";
if (DataTypes.SHORT_VARTYPE_MAX_BYTES <= valueLength) {
param.typeDefinition = NVARCHAR_MAX;
}
}
}
break;
} else
param.typeDefinition = NVARCHAR_4K;
break;
case SQLXML:
param.typeDefinition = SSType.XML.toString();
break;
case TVP:
// definition should contain the TVP name and the keyword READONLY
String schema = param.schemaName;
if (null != schema) {
param.typeDefinition = "[" + schema + "].[" + param.name + "] READONLY";
} else {
param.typeDefinition = "[" + param.name + "] READONLY";
}
break;
case GUID:
param.typeDefinition = SSType.GUID.toString();
break;
case SQL_VARIANT:
param.typeDefinition = SSType.SQL_VARIANT.toString();
break;
case GEOMETRY:
param.typeDefinition = SSType.GEOMETRY.toString();
break;
case GEOGRAPHY:
param.typeDefinition = SSType.GEOGRAPHY.toString();
break;
default:
assert false : "Unexpected JDBC type " + dtv.getJdbcType();
break;
}
}