Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Execute Cstmt Directly #2499

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 37 additions & 7 deletions src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,7 @@ static final String getEncryptionLevel(int level) {
final static int COLINFO_STATUS_DIFFERENT_NAME = 0x20;

final static int MAX_FRACTIONAL_SECONDS_SCALE = 7;
final static int DEFAULT_FRACTIONAL_SECONDS_SCALE = 3;

final static Timestamp MAX_TIMESTAMP = Timestamp.valueOf("2079-06-06 23:59:59");
final static Timestamp MIN_TIMESTAMP = Timestamp.valueOf("1900-01-01 00:00:00");
Expand Down Expand Up @@ -4853,7 +4854,7 @@ void writeVMaxHeader(long headerLength, boolean isNull, SQLCollation collation)
* Utility for internal writeRPCString calls
*/
void writeRPCStringUnicode(String sValue) throws SQLServerException {
writeRPCStringUnicode(null, sValue, false, null);
writeRPCStringUnicode(null, sValue, false, null, false);
}

/**
Expand All @@ -4868,8 +4869,8 @@ void writeRPCStringUnicode(String sValue) throws SQLServerException {
* @param collation
* the collation of the data value
*/
void writeRPCStringUnicode(String sName, String sValue, boolean bOut,
SQLCollation collation) throws SQLServerException {
void writeRPCStringUnicode(String sName, String sValue, boolean bOut, SQLCollation collation,
boolean isNonPLP) throws SQLServerException {
boolean bValueNull = (sValue == null);
int nValueLen = bValueNull ? 0 : (2 * sValue.length());
// Textual RPC requires a collation. If none is provided, as is the case when
Expand All @@ -4881,7 +4882,7 @@ void writeRPCStringUnicode(String sName, String sValue, boolean bOut,
* Use PLP encoding if either OUT params were specified or if the user query exceeds
* DataTypes.SHORT_VARTYPE_MAX_BYTES
*/
if (nValueLen > DataTypes.SHORT_VARTYPE_MAX_BYTES || bOut) {
if ((nValueLen > DataTypes.SHORT_VARTYPE_MAX_BYTES || bOut) && !isNonPLP) {
writeRPCNameValType(sName, bOut, TDSType.NVARCHAR);

writeVMaxHeader(nValueLen, // Length
Expand Down Expand Up @@ -5632,8 +5633,8 @@ void writeCryptoMetaData() throws SQLServerException {
writeByte(cryptoMeta.normalizationRuleVersion);
}

void writeRPCByteArray(String sName, byte[] bValue, boolean bOut, JDBCType jdbcType,
SQLCollation collation) throws SQLServerException {
void writeRPCByteArray(String sName, byte[] bValue, boolean bOut, JDBCType jdbcType, SQLCollation collation,
boolean isNonPLP) throws SQLServerException {
boolean bValueNull = (bValue == null);
int nValueLen = bValueNull ? 0 : bValue.length;
boolean isShortValue = (nValueLen <= DataTypes.SHORT_VARTYPE_MAX_BYTES);
Expand Down Expand Up @@ -5679,7 +5680,7 @@ void writeRPCByteArray(String sName, byte[] bValue, boolean bOut, JDBCType jdbcT

writeRPCNameValType(sName, bOut, tdsType);

if (usePLP) {
if (usePLP && !isNonPLP) {
writeVMaxHeader(nValueLen, bValueNull, collation);

// Send the data.
Expand Down Expand Up @@ -7058,6 +7059,35 @@ final short peekStatusFlag() {
return 0;
}

final int peekReturnValueStatus() throws SQLServerException {
// Ensure that we have a packet to read from.
if (!ensurePayload()) {
throwInvalidTDS();
}

// In order to parse the 'status' value, we need to skip over the following properties in the TDS packet
// payload: TDS token type (1 byte value), ordinal/length (2 byte value), parameter name length value (1 byte value) and
// the number of bytes that make the parameter name (need to be calculated).
//
// 'offset' starts at 4 because tdsTokenType + ordinal/length + parameter name length value is 4 bytes. So, we
// skip 4 bytes immediateley.
int offset = 4;
int paramNameLength = currentPacket.payload[payloadOffset + 3];

// Check if parameter name is set. If it's set, it should be > 0. In which case, we add the
// additional bytes to skip.
if (paramNameLength > 0) {
// Each character in unicode is 2 bytes
offset += 2 * paramNameLength;
}

if (payloadOffset + offset <= currentPacket.payloadLength) {
return currentPacket.payload[payloadOffset + offset] & 0xFF;
}

return -1;
}

final int readUnsignedByte() throws SQLServerException {
// Ensure that we have a packet to read from.
if (!ensurePayload())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,25 @@ public interface ISQLServerDataSource extends javax.sql.CommonDataSource {
*/
boolean getUseDefaultGSSCredential();

/**
* Sets whether or not sp_sproc_columns will be used for parameter name lookup.
*
* @param useFlexibleCallableStatements
* When set to false, sp_sproc_columns is not used for parameter name lookup
* in callable statements. This eliminates a round trip to the server but imposes limitations
* on how parameters are set. When set to false, applications must either reference
* parameters by name or by index, not both. Parameters must also be set in the same
* order as the stored procedure definition.
*/
void setUseFlexibleCallableStatements(boolean useFlexibleCallableStatements);

/**
* Returns whether or not sp_sproc_columns is being used for parameter name lookup.
*
* @return useFlexibleCallableStatements
*/
boolean getUseFlexibleCallableStatements();

/**
* Sets the GSSCredential.
*
Expand Down Expand Up @@ -1346,27 +1365,4 @@ public interface ISQLServerDataSource extends javax.sql.CommonDataSource {
* @return cacheBulkCopyMetadata boolean value
*/
boolean getcacheBulkCopyMetadata();

/**
* useFlexibleCallableStatements is temporarily removed. This is meant as a no-op.
*
* Sets whether or not sp_sproc_columns will be used for parameter name lookup.
*
* @param useFlexibleCallableStatements
* When set to false, sp_sproc_columns is not used for parameter name lookup
* in callable statements. This eliminates a round trip to the server but imposes limitations
* on how parameters are set. When set to false, applications must either reference
* parameters by name or by index, not both. Parameters must also be set in the same
* order as the stored procedure definition.
*/
void setUseFlexibleCallableStatements(boolean useFlexibleCallableStatements);

/**
* useFlexibleCallableStatements is temporarily removed. This is meant as a no-op.
*
* Returns whether or not sp_sproc_columns is being used for parameter name lookup.
*
* @return useFlexibleCallableStatements
*/
boolean getUseFlexibleCallableStatements();
}
76 changes: 65 additions & 11 deletions src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ final class Parameter {
// For unencrypted parameters cryptometa will be null. For encrypted parameters it will hold encryption metadata.
CryptoMetadata cryptoMeta = null;

boolean isNonPLP = false;

TypeInfo getTypeInfo() {
return typeInfo;
}
Expand All @@ -49,6 +51,7 @@ final CryptoMetadata getCryptoMetadata() {
private boolean shouldHonorAEForParameter = false;
private boolean userProvidesPrecision = false;
private boolean userProvidesScale = false;
private boolean isReturnValue = false;

// The parameter type definition
private String typeDefinition = null;
Expand All @@ -71,11 +74,49 @@ boolean isOutput() {
return null != registeredOutDTV;
}

// Since a parameter can have only one type definition for both sending its value to the server (IN)
// and getting its value from the server (OUT), we use the JDBC type of the IN parameter value if there
// is one; otherwise we use the registered OUT param JDBC type.
JDBCType getJdbcType() {
return (null != inputDTV) ? inputDTV.getJdbcType() : JDBCType.UNKNOWN;
/**
* Returns true/false if the parameter is of return type
*
* @return isReturnValue
*/
boolean isReturnValue() {
return isReturnValue;
}

/**
* Sets the parameter to be of return type
*
* @param isReturnValue
*/
void setReturnValue(boolean isReturnValue) {
this.isReturnValue = isReturnValue;
}

/**
* Sets the name of the parameter
*
* @param name
*/
void setName(String name) {
this.name = name;
}

/**
* Retrieve the name of the parameter
*
* @return
*/
String getName() {
return this.name;
}

/**
* Returns the `registeredOutDTV` instance of the parameter
*
* @return registeredOutDTV
*/
DTV getRegisteredOutDTV() {
return this.registeredOutDTV;
}

/**
Expand All @@ -87,6 +128,13 @@ DTV getInputDTV() {
return this.inputDTV;
}

// Since a parameter can have only one type definition for both sending its value to the server (IN)
// and getting its value from the server (OUT), we use the JDBC type of the IN parameter value if there
// is one; otherwise we use the registered OUT param JDBC type.
JDBCType getJdbcType() {
return (null != inputDTV) ? inputDTV.getJdbcType() : JDBCType.UNKNOWN;
}

/**
* Used when sendStringParametersAsUnicode=true to derive the appropriate National Character Set JDBC type
* corresponding to the specified JDBC type.
Expand Down Expand Up @@ -256,7 +304,7 @@ void setFromReturnStatus(int returnStatus, SQLServerConnection con) throws SQLSe
if (null == getterDTV)
getterDTV = new DTV();

getterDTV.setValue(null, JDBCType.INTEGER, returnStatus, JavaType.INTEGER, null, null, null, con,
getterDTV.setValue(null, this.getJdbcType(), returnStatus, JavaType.INTEGER, null, null, null, con,
getForceEncryption());
}

Expand Down Expand Up @@ -397,10 +445,14 @@ boolean isValueGotten() {

Object getValue(JDBCType jdbcType, InputStreamGetterArgs getterArgs, Calendar cal, TDSReader tdsReader,
SQLServerStatement statement) throws SQLServerException {
if (null == getterDTV)
if (null == getterDTV) {
getterDTV = new DTV();
}

if (null != tdsReader) {
deriveTypeInfo(tdsReader);
}

deriveTypeInfo(tdsReader);
// If the parameter is not encrypted or column encryption is turned off (either at connection or
// statement level), cryptoMeta would be null.
return getterDTV.getValue(jdbcType, outScale, getterArgs, cal, typeInfo, cryptoMeta, tdsReader, statement);
Expand Down Expand Up @@ -1220,15 +1272,17 @@ String getTypeDefinition(SQLServerConnection con, TDSReader tdsReader) throws SQ
return typeDefinition;
}

void sendByRPC(TDSWriter tdsWriter, SQLServerStatement statement) throws SQLServerException {
void sendByRPC(TDSWriter tdsWriter, boolean callRPCDirectly,
SQLServerStatement statement) throws SQLServerException {
assert null != inputDTV : "Parameter was neither set nor registered";
SQLServerConnection conn = statement.connection;

try {
inputDTV.isNonPLP = isNonPLP;
inputDTV.sendCryptoMetaData(this.cryptoMeta, tdsWriter);
inputDTV.setJdbcTypeSetByUser(getJdbcTypeSetByUser(), getValueLength());
inputDTV.sendByRPC(name, null, conn.getDatabaseCollation(), valueLength, isOutput() ? outScale : scale,
isOutput(), tdsWriter, statement);
inputDTV.sendByRPC(callRPCDirectly ? name : null, null, conn.getDatabaseCollation(), valueLength,
isOutput() ? outScale : scale, isOutput(), tdsWriter, statement);
} finally {
// reset the cryptoMeta in IOBuffer
inputDTV.sendCryptoMetaData(null, tdsWriter);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2129,7 +2129,8 @@ private void writeNullToTdsWriter(TDSWriter tdsWriter, int srcJdbcType,

private void writeColumnToTdsWriter(TDSWriter tdsWriter, int bulkPrecision, int bulkScale, int bulkJdbcType,
boolean bulkNullable, // should it be destNullable instead?
int srcColOrdinal, int destColOrdinal, boolean isStreaming, Object colValue, Calendar cal) throws SQLServerException {
int srcColOrdinal, int destColOrdinal, boolean isStreaming, Object colValue,
Calendar cal) throws SQLServerException {
SSType destSSType = destColumnMetadata.get(destColOrdinal).ssType;

bulkPrecision = validateSourcePrecision(bulkPrecision, bulkJdbcType,
Expand Down Expand Up @@ -3046,8 +3047,8 @@ private Object readColumnFromResultSet(int srcColOrdinal, int srcJdbcType, boole
/**
* Reads the given column from the result set current row and writes the data to tdsWriter.
*/
private void writeColumn(TDSWriter tdsWriter, int srcColOrdinal, int destColOrdinal,
Object colValue, Calendar cal) throws SQLServerException {
private void writeColumn(TDSWriter tdsWriter, int srcColOrdinal, int destColOrdinal, Object colValue,
Calendar cal) throws SQLServerException {
String destName = destColumnMetadata.get(destColOrdinal).columnName;
int srcPrecision, srcScale, destPrecision, srcJdbcType;
SSType destSSType = null;
Expand Down Expand Up @@ -3699,8 +3700,8 @@ private boolean writeBatchData(TDSWriter tdsWriter, TDSCommand command,
// Loop for each destination column. The mappings is a many to one mapping
// where multiple source columns can be mapped to one destination column.
for (ColumnMapping columnMapping : columnMappings) {
writeColumn(tdsWriter, columnMapping.sourceColumnOrdinal, columnMapping.destinationColumnOrdinal, null,
null // cell
writeColumn(tdsWriter, columnMapping.sourceColumnOrdinal, columnMapping.destinationColumnOrdinal,
null, null // cell
// value is
// retrieved
// inside
Expand Down
Loading
Loading