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

com.microsoft.sqlserver.jdbc.Util Maven / Gradle / Ivy

//---------------------------------------------------------------------------------------------------------------------------------
// File: Util.java
//
//
// Microsoft JDBC Driver for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""), 
//  to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
//  and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 
//  IN THE SOFTWARE.
//---------------------------------------------------------------------------------------------------------------------------------
 

package com.microsoft.sqlserver.jdbc;

import java.util.*;
import java.text.DecimalFormat;
import java.util.Map.Entry;
import java.text.MessageFormat;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.*;
import java.util.logging.*;
import java.io.*;

/**
 * Various driver utilites.
 *
 */

final class Util {
	final static String SYSTEM_SPEC_VERSION = System.getProperty("java.specification.version");
	final static char[] hexChars = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
	final static String WSIDNotAvailable = ""; // default string when WSID is not available 

	final static String ActivityIdTraceProperty = "com.microsoft.sqlserver.jdbc.traceactivity";

	//The JRE is identified by the string below so that the driver can make 
	//any vendor or version specific decisions
	static final String SYSTEM_JRE = System.getProperty("java.vendor") + " " + System.getProperty("java.version");

	static boolean isIBM()
	{
		return SYSTEM_JRE.startsWith("IBM");
	}
	
	
    static final Boolean isCharType(int jdbcType)
    {
    	switch(jdbcType)
    	{
    	case java.sql.Types.CHAR:
    	case java.sql.Types.NCHAR:
    	case java.sql.Types.VARCHAR:
    	case java.sql.Types.NVARCHAR:
    	case java.sql.Types.LONGVARCHAR:
    	case java.sql.Types.LONGNVARCHAR:
    		return true;
    	default:
    		return false;
    	}
    }

    static final Boolean isCharType(SSType ssType)
    {
    	switch(ssType)
    	{
    	case CHAR:
    	case NCHAR:
    	case VARCHAR:
    	case NVARCHAR:
    	case VARCHARMAX:
    	case NVARCHARMAX:
    		return true;
    	default:
    		return false;
    	}
    }
    
    static final Boolean isBinaryType(SSType ssType)
    {
    	switch(ssType)
    	{
    	case BINARY:
    	case VARBINARY:
    	case VARBINARYMAX:
    	case IMAGE:
    		return true;
    	default:
    		return false;
    	}
    }
    	
    static final Boolean isBinaryType(int jdbcType)
    {
    	switch(jdbcType)
    	{
    	case java.sql.Types.BINARY:
    	case java.sql.Types.VARBINARY:
    	case java.sql.Types.LONGVARBINARY:
    		return true;
    	default:
    		return false;
    	}
    }
    

	/**
	 * Read a short int from a byte stream
	 * @param data the databytes
	 * @param nOffset offset to read from
	 * @return the value
	 */
	/*L0*/ static short readShort(byte data[], int nOffset) {
		return (short) ((data[nOffset] & 0xff) | ((data[nOffset+1] & 0xff) << 8));
	}

	/**
	 * Read an unsigned short int (16 bits) from a byte stream
	 * @param data the databytes
	 * @param nOffset offset to read from
	 * @return the value
	 */
	/*L0*/ static int readUnsignedShort(byte data[], int nOffset) {
		return ((data[nOffset] & 0xff) | ((data[nOffset+1] & 0xff) << 8));
	}

	static int readUnsignedShortBigEndian(byte data[], int nOffset)
	{
		return ((data[nOffset] & 0xFF) << 8) | (data[nOffset+1] & 0xFF);
	}

	static void writeShort(short value, byte valueBytes[], int offset)
	{
		valueBytes[offset + 0] = (byte)((value >> 0) & 0xFF);
		valueBytes[offset + 1] = (byte)((value >> 8) & 0xFF);
	}

	static void writeShortBigEndian(short value, byte valueBytes[], int offset)
	{
		valueBytes[offset + 0] = (byte)((value >> 8) & 0xFF);
		valueBytes[offset + 1] = (byte)((value >> 0) & 0xFF);
	}
	/**
	 * Read an int from a byte stream
	 * @param data the databytes
	 * @param nOffset offset to read from
	 * @return the value
	 */
	/*L0*/ static int readInt(byte data[], int nOffset) {
		int b1 = ((int)data[nOffset+0] & 0xff);
		int b2 = ((int)data[nOffset+1] & 0xff) << 8;
		int b3 = ((int)data[nOffset+2] & 0xff) << 16;
		int b4 = ((int)data[nOffset+3] & 0xff) << 24;
		return b4 | b3 | b2 | b1;
	}

	static int readIntBigEndian(byte data[], int nOffset)
	{
		return
				((data[nOffset + 3] & 0xFF) <<  0) |
				((data[nOffset + 2] & 0xFF) <<  8) |
				((data[nOffset + 1] & 0xFF) << 16) |
				((data[nOffset + 0] & 0xFF) << 24);
	}

	static void writeInt(int value, byte valueBytes[], int offset)
	{
		valueBytes[offset + 0] = (byte)((value >>  0) & 0xFF);
		valueBytes[offset + 1] = (byte)((value >>  8) & 0xFF);
		valueBytes[offset + 2] = (byte)((value >> 16) & 0xFF);
		valueBytes[offset + 3] = (byte)((value >> 24) & 0xFF);
	}

	static void writeIntBigEndian(int value, byte valueBytes[], int offset)
	{
		valueBytes[offset + 0] = (byte)((value >> 24) & 0xFF);
		valueBytes[offset + 1] = (byte)((value >> 16) & 0xFF);
		valueBytes[offset + 2] = (byte)((value >>  8) & 0xFF);
		valueBytes[offset + 3] = (byte)((value >>  0) & 0xFF);
	}

	static void writeLongBigEndian(long value, byte valueBytes[], int offset)
	{
		valueBytes[offset + 0] = (byte)((value >> 56) & 0xFF);
		valueBytes[offset + 1] = (byte)((value >> 48) & 0xFF);
		valueBytes[offset + 2] = (byte)((value >> 40) & 0xFF);
		valueBytes[offset + 3] = (byte)((value >> 32) & 0xFF);
		valueBytes[offset + 4] = (byte)((value >> 24) & 0xFF);
		valueBytes[offset + 5] = (byte)((value >> 16) & 0xFF);
		valueBytes[offset + 6] = (byte)((value >>  8) & 0xFF);
		valueBytes[offset + 7] = (byte)((value >>  0) & 0xFF);
	}

	static BigDecimal readBigDecimal(byte valueBytes[], int valueLength, int scale)
	{
		int sign = (0 == valueBytes[0]) ? -1 : 1;
		byte[] magnitude = new byte[valueLength-1];
		for (int i = 1; i <= magnitude.length; i++)
			magnitude[magnitude.length - i] = valueBytes[i];
		return new BigDecimal(new BigInteger(sign, magnitude), scale);	  
	}

	/**
	 * Reads a long value from byte array.
	 * @param data the byte array.
	 * @param nOffset the offset into byte array to start reading. 
	 * @return long value as read from bytes.
	 */	
	/*L0*/static long readLong(byte data[], int nOffset) 
	{
		long v = 0;
		for (int i=7; i>0; i--)
		{
			v += (long)(data[nOffset+i]&0xff);
			v <<= 8;
		}
		return v + (long)(data[nOffset]&0xff);
	}

	/**
	 * Parse a JDBC URL into a set of properties.
	 * @param url the JDBC URL
	 * @param logger
	 * @return the properties
	 * @throws SQLServerException
	 */
	/*L0*/ static Properties parseUrl(String url, Logger logger) throws SQLServerException {
		Properties p = new Properties();
		String tmpUrl = url;
		String sPrefix = "jdbc:sqlserver://";
		String 	result ="";
		String name = "";
		String value= "";

		if (!tmpUrl.startsWith(sPrefix))
			return null;

		tmpUrl = tmpUrl.substring(sPrefix.length());
		int               i=0;

		// Simple finite state machine. 
		// always look at one char at a time
		final int   inStart             = 0;
		final int   inServerName		= 1;
		final int   inPort         	    = 2;
		final int 	inInstanceName 		= 3;
		final int   inEscapedValueStart = 4;
		final int   inEscapedValueEnd  	= 5;
		final int   inValue 			= 6;
		final int   inName 				= 7;  

		int         state = inStart;
		char        ch;
		i = 0;
		while(i 0)
					{
						p.put(SQLServerDriverStringProperty.SERVER_NAME.toString(), result);
						if(logger.isLoggable(Level.FINE))
						{
							logger.fine("Property:serverName "  + "Value:" + result);
						}
					}
					result ="";

					if(ch == ';' )
						state = inName;
					else
						if(ch ==':' )
							state = inPort;
						else
							state = inInstanceName;
				}
				else
				{
					result = result + ch;
					// same state
				}
				break;
			}

			case inPort:
			{
				if (ch == ';')
				{
					result = result.trim();
					if(logger.isLoggable(Level.FINE))
					{
						logger.fine("Property:portNumber "  + "Value:" + result);
					}
					p.put(SQLServerDriverIntProperty.PORT_NUMBER.toString(), result);
					result ="";
					state = inName;
				}
				else
				{
					result = result + ch;
					// same state
				}
				break;
			}
			case inInstanceName:
			{
				if (ch == ';' || ch == ':' )
				{
					// non escaped trim the string
					result = result.trim();
					if(logger.isLoggable(Level.FINE))
					{
						logger.fine("Property:instanceName "  + "Value:" + result);
					}
					p.put(SQLServerDriverStringProperty.INSTANCE_NAME.toString(), result.toLowerCase(Locale.US));
					result ="";

					if(ch == ';' )
						state = inName;
					else
						state = inPort;
				}
				else
				{
					result = result + ch;
					// same state
				}
				break;
			}
			case inName:
			{
				if (ch == '=')
				{
					// name is never escaped!
					name = name.trim();
					if(name.length()<= 0)
					{
						SQLServerException.makeFromDriverError(null, null,
								SQLServerException.getErrString("R_errorConnectionString"), null, true);
					}
					state = inValue;
				}
				else
					if (ch == ';')
					{
						name = name.trim();
						if(name.length()> 0)
						{
							SQLServerException.makeFromDriverError(null, null,
									SQLServerException.getErrString("R_errorConnectionString"), null, true);
						}
						// same state
					}
					else
					{
						name = name + ch;
						// same state
					}
				break;
			}
			case inValue:
			{
				if (ch == ';')
				{
					// simple value trim
					value = value.trim();
					name = SQLServerDriver.getNormalizedPropertyName(name, logger);
					if(null != name)
					{
						if(logger.isLoggable(Level.FINE))
						{
							if((false ==name.equals(SQLServerDriverStringProperty.USER.toString()))&& (false==name.equals(SQLServerDriverStringProperty.PASSWORD.toString())))
								logger.fine("Property:" + name + " Value:" + value);
						}
						p.put(name, value);
					}
					name = "";
					value="";
					state = inName;

				}
				else
					if(ch == '{')
					{
						state = inEscapedValueStart;
						value = value.trim();
						if(value.length() >0 )
						{
							SQLServerException.makeFromDriverError(null, null,
									SQLServerException.getErrString("R_errorConnectionString") , null, true);
						}
					}else
					{
						value = value + ch;
						// same state
					}
				break;
			}
			case inEscapedValueStart:
			{
				if (ch == '}')
				{
					// no trimming use the value as it is.
					name = SQLServerDriver.getNormalizedPropertyName(name, logger);
					if(null != name)
					{
						if(logger.isLoggable(Level.FINE))
						{
							if((false ==name.equals(SQLServerDriverStringProperty.USER.toString()))&& (false==name.equals(SQLServerDriverStringProperty.PASSWORD.toString())))
								logger.fine("Property:" + name + " Value:" + value);
						}
						p.put(name, value);
					}

					name = "";
					value="";
					// to eat the spaces until the ; potentially we could do without the state but
					// it would not be clean
					state = inEscapedValueEnd; 
				}
				else
				{
					value = value + ch;
					// same state
				}
				break;
			}
			case inEscapedValueEnd:
			{
				if (ch == ';') // eat space chars till ; anything else is an error
				{
					state = inName;
				}
				else if(ch != ' ')
				{
					// error if the chars are not space
					SQLServerException.makeFromDriverError(null, null,
							SQLServerException.getErrString("R_errorConnectionString"), null, true);
				}
				break;
			}

			default:
				assert false : "parseURL: Invalid state " + state;
			}
			i++;
		}

		// Exit 
		switch(state)
		{
		case inServerName:
			result = result.trim();
			if(result.length()> 0)
			{
				if(logger.isLoggable(Level.FINE))
				{
					logger.fine("Property:serverName "  + "Value:" + result);
				}
				p.put(SQLServerDriverStringProperty.SERVER_NAME.toString(), result);
			}
			break;
		case inPort:	
			result = result.trim();
			if(logger.isLoggable(Level.FINE))
			{
				logger.fine("Property:portNumber "  + "Value:" + result);
			}
			p.put(SQLServerDriverIntProperty.PORT_NUMBER.toString(), result);
			break;
		case inInstanceName:	
			result = result.trim();
			if(logger.isLoggable(Level.FINE))
			{
				logger.fine("Property:instanceName "  + "Value:" + result);
			}
			p.put(SQLServerDriverStringProperty.INSTANCE_NAME.toString(), result);
			break;
		case inValue:
			// simple value trim
			value = value.trim();
			name = SQLServerDriver.getNormalizedPropertyName(name, logger);
			if(null != name)
			{
				if(logger.isLoggable(Level.FINE))
				{
					if((false == name.equals(SQLServerDriverStringProperty.USER.toString()))&& (false == name.equals(SQLServerDriverStringProperty.PASSWORD.toString()))
							&&(false == name.equals(SQLServerDriverStringProperty.KEY_STORE_SECRET.toString())))
							logger.fine("Property:" + name + " Value:" + value);
				}
				p.put(name, value);
			}

			break;
		case inEscapedValueEnd:
		case inStart:
			// do nothing!
			break;
		case inName:
		{
			name = name.trim();
			if(name.length()> 0)
			{
				SQLServerException.makeFromDriverError(null, null,
						SQLServerException.getErrString("R_errorConnectionString"), null, true);
			}

			break;
		}	
		default:
			SQLServerException.makeFromDriverError(null, null,
					SQLServerException.getErrString( "R_errorConnectionString"), null, true);
		}
		return p;
	}

	/**
	 * Accepts a SQL identifier (such as a column name or table name) and
	 * escapes the identifier using SQL Server bracket escaping rules.
	 * Assumes that the incoming identifier is unescaped.
	 * @inID input identifier to escape.
	 * @return the escaped value.
	 */
	static String escapeSQLId(String inID)
	{
		// SQL bracket escaping rules.
		// Given  yields -> []
		// Where  is first escaped to replace all 
		// instances of "]" with "]]".
		// For example, column name "abc" -> "[abc]"
		// For example, column name "]" -> "[]]]"
		// For example, column name "]ab]cd" -> "[]]ab]]cd]"
		char ch;

		// Add 2 extra chars for open and closing brackets.
		StringBuilder outID = new StringBuilder(inID.length()+2);

		outID.append('[');
		for(int i=0; i columnMetadata ) throws SQLServerException 
	{
		if (columnMetadata.get(0) instanceof SQLServerMetaData )
		{
			for (Entry entry : columnMetadata.entrySet()) 
			{
				SQLServerMetaData value =  (SQLServerMetaData) entry.getValue();
				if (value.columnName.equals(columnName))
				{
					MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_TVPDuplicateColumnName"));
					Object[] msgArgs = {columnName};
					throw new SQLServerException(null , form.format(msgArgs) , null, 0 , false);  
				}
			}
		}
		else if (columnMetadata.get(0) instanceof SQLServerDataColumn )
		{
			for (Entry entry : columnMetadata.entrySet()) 
			{
				SQLServerDataColumn value = (SQLServerDataColumn) entry.getValue();
				if (value.columnName.equals(columnName))
				{
					MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_TVPDuplicateColumnName"));
					Object[] msgArgs = {columnName};
					throw new SQLServerException(null , form.format(msgArgs) , null, 0 , false);  
				}
			}
		}
	}

	/**
	 * Reads a UNICODE string from byte buffer at offset (up to byteLength).
	 * @param b the buffer containing UNICODE bytes.
	 * @param offset - the offset into b where the UNICODE string starts.
	 * @param byteLength - the length in bytes of the UNICODE string.
	 * @param conn - the SQLServerConnection object.
	 * @return new String with UNICODE data inside.
	 */
	static String readUnicodeString(byte [] b, int offset, int byteLength, SQLServerConnection conn) throws SQLServerException
	{
		try
		{
			return new String(b, offset, byteLength, Encoding.UNICODE.charsetName());
		}
		catch (UnsupportedEncodingException ex)
		{
			String txtMsg = SQLServerException.checkAndAppendClientConnId(SQLServerException.getErrString("R_stringReadError"), conn);
			MessageFormat form = new MessageFormat(txtMsg);
			Object[] msgArgs = {new Integer(offset)};
			// Re-throw SQLServerException if conversion fails.
			throw new SQLServerException(null, form.format(msgArgs), null, 0, true);
		}
		catch (IndexOutOfBoundsException  ex)
		{
			String txtMsg = SQLServerException.checkAndAppendClientConnId(SQLServerException.getErrString("R_stringReadError"), conn);
			MessageFormat form = new MessageFormat(txtMsg);
			Object[] msgArgs = {new Integer(offset)};
			// Re-throw SQLServerException if conversion fails.
			throw new SQLServerException(null, form.format(msgArgs), null, 0, true);
		}

	}

	//NOTE: This is for display purposes ONLY. NOT TO BE USED for data conversion.
	/**
	 * Converts byte array to a string representation of hex bytes for display purposes.
	 * @param b the source buffer.
	 * @return "hexized" string representation of bytes.
	 */
	static String byteToHexDisplayString(byte [] b)
	{
		if (null == b) return "(null)";
		int hexVal;
		StringBuilder sb = new StringBuilder(b.length*2+2);
		sb.append("0x");
		for (int i=0; i>4]);
			sb.append(hexChars[(hexVal&0x0F)]);
		}
		return sb.toString();
	}

	/**
	 * Converts byte array to a string representation of hex bytes.
	 * @param b the source buffer.
	 * @return "hexized" string representation of bytes.
	 */
	static String bytesToHexString(byte[] b, int length)
	{
		StringBuilder sb = new StringBuilder(length*2);
		for (int i=0; i>4]);
			sb.append(hexChars[(hexVal&0x0F)]);
		}
		return sb.toString();
	}

	/**
	 * Looks up local hostname of client machine.
	 * @exception UnknownHostException if local hostname is not found.
	 * @return hostname string or ip of host if hostname cannot be resolved.
	 * If neither hostname or ip found returns "" per spec.
	 */	
	static String lookupHostName() 
	{   

		try
		{
			InetAddress localAddress = InetAddress.getLocalHost();
			if (null != localAddress)
			{
				String value = localAddress.getHostName();
				if (null != value && value.length()>0) return value;

				value = localAddress.getHostAddress();
				if (null != value && value.length()>0) return value;
			}
		}
		catch (UnknownHostException e)
		{
			return WSIDNotAvailable;
		}
		// If hostname not found, return standard "" string.
		return  WSIDNotAvailable; 
	}

	static final byte[] asGuidByteArray(UUID aId) 
	{
		long msb = aId.getMostSignificantBits();
		long lsb = aId.getLeastSignificantBits();
		byte[] buffer = new byte[16]; 
		Util.writeLongBigEndian (msb, buffer, 0);
		Util.writeLongBigEndian (lsb, buffer, 8);

		// For the first three fields, UUID uses network byte order,
		// Guid uses native byte order. So we need to reverse 
		// the first three fields before sending to server.
 
		byte tmpByte;

		// Reverse the first 4 bytes 
		tmpByte = buffer[0];
		buffer[0] = buffer[3];
		buffer[3] = tmpByte;
		tmpByte = buffer[1];
		buffer[1] = buffer[2];
		buffer[2] = tmpByte;

		// Reverse the 5th and the 6th
		tmpByte = buffer[4];
		buffer[4] = buffer[5];
		buffer[5] = tmpByte;

		// Reverse the 7th and the 8th
		tmpByte = buffer[6];
		buffer[6] = buffer[7];
		buffer[7] = tmpByte;

		return buffer;  
	} 

	static final String readGUID(byte[] inputGUID) throws SQLServerException
	{
		String guidTemplate = "NNNNNNNN-NNNN-NNNN-NNNN-NNNNNNNNNNNN";
		byte guid[] = inputGUID;

		StringBuilder sb = new StringBuilder(guidTemplate.length());
		for (int i=0; i<4; i++)
		{
			sb.append(Util.hexChars[(guid[3-i] & 0xF0) >> 4]);
			sb.append(Util.hexChars[guid[3-i] & 0x0F]);
		}
		sb.append('-');
		for (int i=0; i<2; i++)
		{
			sb.append(Util.hexChars[(guid[5-i] & 0xF0) >> 4]);
			sb.append(Util.hexChars[guid[5-i] & 0x0F]);
		}
		sb.append('-');
		for (int i=0; i<2; i++)
		{
			sb.append(Util.hexChars[(guid[7-i] & 0xF0) >> 4]);
			sb.append(Util.hexChars[guid[7-i] & 0x0F]);
		}
		sb.append('-');
		for (int i=0; i<2; i++)
		{
			sb.append(Util.hexChars[(guid[8+i] & 0xF0) >> 4]);
			sb.append(Util.hexChars[guid[8+i] & 0x0F]);
		}
		sb.append('-');
		for (int i=0; i<6; i++)
		{
			sb.append(Util.hexChars[(guid[10+i] & 0xF0) >> 4]);
			sb.append(Util.hexChars[guid[10+i] & 0x0F]);
		}

		return sb.toString();
	}

	static boolean IsActivityTraceOn()
	{
		LogManager lm=LogManager.getLogManager();
		String activityTrace = lm.getProperty(ActivityIdTraceProperty);
		if (null !=activityTrace  && activityTrace.equalsIgnoreCase("on"))
			return true;
		else 
			return false;
	}

	/**
	 * Determines if a column value should be transparently decrypted (based on SQLServerStatement and the connection string settings).
	 * @return true if the value should be transparently decrypted, false otherwise.
	 */
	static boolean shouldHonorAEForRead (SQLServerStatementColumnEncryptionSetting stmtColumnEncryptionSetting, SQLServerConnection connection) {
		// Command leve setting trumps all
		switch (stmtColumnEncryptionSetting) {
		case Disabled:
			return false;
		case Enabled:
			return true;
		case ResultSetOnly:
			return true;
		default:
			// Check connection level setting!
			assert SQLServerStatementColumnEncryptionSetting.UseConnectionSetting == stmtColumnEncryptionSetting :
				"Unexpected value for command level override";
			return (connection != null && connection.isColumnEncryptionSettingEnabled());
		}
	}    

	/**
	 * Determines if parameters should be transparently encrypted (based on SQLServerStatement and the connection string settings).
	 * @return true if the value should be transparently encrypted, false otherwise.
	 */    
	static boolean shouldHonorAEForParameters (SQLServerStatementColumnEncryptionSetting stmtColumnEncryptionSetting, SQLServerConnection connection) {
		// Command leve setting trumps all
		switch (stmtColumnEncryptionSetting) {
		case Disabled:
			return false;
		case Enabled:
			return true;
		case ResultSetOnly:
			return false;
		default:
			// Check connection level setting!
			assert SQLServerStatementColumnEncryptionSetting.UseConnectionSetting == stmtColumnEncryptionSetting :
				"Unexpected value for command level override";
			return (connection != null && connection.isColumnEncryptionSettingEnabled());
		}
	}

	static void validateMoneyRange(BigDecimal bd, JDBCType jdbcType) throws SQLServerException
	{
		if (null == bd) 
			return;

		switch (jdbcType)
		{
		case MONEY:
			if ((1 != bd.compareTo(SSType.MAX_VALUE_MONEY)) && 
					(-1 != bd.compareTo(SSType.MIN_VALUE_MONEY)))
			{
				return;
			}			
			break;
		case SMALLMONEY:
			if ((1 != bd.compareTo(SSType.MAX_VALUE_SMALLMONEY)) && 
					(-1 != bd.compareTo(SSType.MIN_VALUE_SMALLMONEY)))
			{
				return;
			}		
			break;
		}
		MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_valueOutOfRange"));
		Object[] msgArgs = { jdbcType };
		throw new SQLServerException(form.format(msgArgs), null);
	}    

	static int getValueLengthBaseOnJavaType(Object value,
			JavaType javaType,
			Integer precision,
			Integer scale, 
			JDBCType jdbcType) throws SQLServerException{
		switch(javaType)
		{
		//when the value of setObject() is null, the javaType stays
		//as OBJECT. We need to get the javaType base on jdbcType
		case OBJECT:
			switch(jdbcType){
			case DECIMAL:
			case NUMERIC:
				javaType = JavaType.BIGDECIMAL;
				break;
			case TIME:
				javaType = JavaType.TIME;
				break; 
			case TIMESTAMP:
				javaType = JavaType.TIMESTAMP;
				break; 
			case DATETIMEOFFSET:
				javaType = JavaType.DATETIMEOFFSET;
				break;
			default:
				break;
			}
			break;
		}
		
		switch(javaType)
		{		
		case STRING:
			if (JDBCType.GUID == jdbcType){
				String guidTemplate = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX";
				return ((null == value) ? 0 : ((String)guidTemplate).length());
			}
			else if (JDBCType.TIMESTAMP == jdbcType ||
					JDBCType.TIME == jdbcType ||
					JDBCType.DATETIMEOFFSET == jdbcType ){
				return((null == scale) ? TDS.MAX_FRACTIONAL_SECONDS_SCALE : scale);
			}
			else if(JDBCType.BINARY == jdbcType || JDBCType.VARBINARY == jdbcType )
			{
				return ((null == value) ? 0 : (ParameterUtils.HexToBin((String)value).length ));
			}
			else{
				return ((null == value) ? 0 : ((String)value).length());
			}

		case BYTEARRAY:
			return((null == value) ? 0 : ((byte[])value).length);

		case BIGDECIMAL:
			int length = -1;
			
			if(null == precision){
				if(null == value){
					length = 0;
				}
				else{
					if(0 == ((BigDecimal)value).intValue()){
						String s = "" + ((BigDecimal)value);
						s = s.replaceAll("\\.", "");
						s = s.replaceAll("\\-", "");
						length = s.length();
					}
					//if the value is in scientific notation format
					else if (("" + ((BigDecimal)value)).contains("E")){
						DecimalFormat dform = new DecimalFormat("###.#####");
						String s = dform.format((BigDecimal)value);  
						s = s.replaceAll("\\.", "");
						s = s.replaceAll("\\-", "");
						length = s.length();
					}
					else{
						length = ((BigDecimal)value).precision();
					}
				}
			}
			else{
				length = precision;
			}
			
			return length;

		case TIMESTAMP:
		case TIME:
		case DATETIMEOFFSET:
			return((null == scale) ? TDS.MAX_FRACTIONAL_SECONDS_SCALE : scale);
		case READER:
			return((null == value) ? 0 : DataTypes.NTEXT_MAX_CHARS);
			
		case CLOB:
			return((null == value) ? 0 : (DataTypes.NTEXT_MAX_CHARS * 2));
			
		case NCLOB:
			return((null == value) ? 0 : DataTypes.NTEXT_MAX_CHARS);
		}
		return 0;
	}

	// If the access token is expiring within next 10 minutes, lets just re-create a token for this connection attempt.
	// If the token is expiring within the next 45 mins, try to fetch a new token if there is no thread already doing it.
	// If a thread is already doing the refresh, just use the existing token and proceed.
	static synchronized boolean checkIfNeedNewAccessToken(SQLServerConnection connection){
		Date accessTokenExpireDate = connection.getAuthenticationResult().getExpiresOnDate();
		Date now = new Date();

		//if the token's expiration is within the next 45 mins
		//45 mins * 60 sec/min * 1000 millisec/sec
		if((accessTokenExpireDate.getTime() - now.getTime()) < (45 * 60 * 1000)){

			//within the next 10 mins
			if((accessTokenExpireDate.getTime() - now.getTime()) < (10 * 60 * 1000)){
				return true;
			}
			else{
				//check if another thread is already updating the access token
				if (connection.attemptRefreshTokenLocked){
					return false;
				}
				else{
					connection.attemptRefreshTokenLocked = true;
					return true;
				}
			}
		}

		return false;
	}
	
	//if driver is for JDBC 42 and jvm version is 8 or higher, then always return as SQLServerPreparedStatement42,
	//otherwise return SQLServerPreparedStatement
	static boolean use42Wrapper(){
		
		boolean supportJDBC42 = true;
		try{
			DriverJDBCVersion.checkSupportsJDBC42();    
		}
		catch(UnsupportedOperationException e){
			supportJDBC42 = false;
		}
		
		double jvmVersion = Double.parseDouble(Util.SYSTEM_SPEC_VERSION);
		
		return supportJDBC42 && (1.8 <= jvmVersion);
	}
}

final class SQLIdentifier
{
	// Component names default to empty string (rather than null) for consistency
	// with API behavior which returns empty string (rather than null) when the
	// particular value is not present.

	private String serverName = "";
	final String getServerName() { return serverName; }
	final void setServerName(String name) { serverName = name; }

	private String databaseName = "";
	final String getDatabaseName() { return databaseName; }
	final void setDatabaseName(String name) { databaseName = name; }

	private String schemaName = "";
	final String getSchemaName() { return schemaName; }
	final void setSchemaName(String name) { schemaName = name; }

	private String objectName = "";
	final String getObjectName() { return objectName; }
	final void setObjectName(String name) { objectName = name; }

	final String asEscapedString()
	{
		StringBuilder fullName = new StringBuilder(256);

		if (serverName.length() > 0)
			fullName.append("[" + serverName + "].");

		if (databaseName.length() > 0)
			fullName.append("[" + databaseName + "].");
		else
			assert 0 == serverName.length();

		if (schemaName.length() > 0)
			fullName.append("[" + schemaName + "].");
		else if (databaseName.length() > 0)
			fullName.append('.');

		fullName.append("[" + objectName + "]");

		return fullName.toString();
	}   
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy