|
//------------------------------------------------------------------------------
// <copyright file="OdbcUtils.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">Microsoft</owner>
// <owner current="true" primary="false">Microsoft</owner>
//------------------------------------------------------------------------------
using System;
using System.Data;
using System.Data.Common;
using System.Diagnostics; // Debug services
using System.Runtime.InteropServices;
using System.Text;
namespace System.Data.Odbc
{
sealed internal class CNativeBuffer : System.Data.ProviderBase.DbBuffer {
internal CNativeBuffer (int initialSize) : base(initialSize) {
}
internal short ShortLength {
get {
return checked((short)Length);
}
}
internal object MarshalToManaged(int offset, ODBC32.SQL_C sqlctype, int cb) {
object value;
switch(sqlctype)
{
case ODBC32.SQL_C.WCHAR:
//Note: We always bind as unicode
if(cb == ODBC32.SQL_NTS) {
value = PtrToStringUni(offset);
break;
}
Debug.Assert((cb > 0), "Character count negative ");
Debug.Assert((Length >= cb), "Native buffer too small ");
cb = Math.Min(cb/2, (Length-2)/2);
value= PtrToStringUni(offset, cb);
break;
case ODBC32.SQL_C.CHAR:
case ODBC32.SQL_C.BINARY:
Debug.Assert((cb > 0), "Character count negative ");
Debug.Assert((Length >= cb), "Native buffer too small ");
cb = Math.Min(cb, Length);
value = ReadBytes(offset, cb);
break;
case ODBC32.SQL_C.SSHORT:
value = ReadInt16(offset);
break;
case ODBC32.SQL_C.SLONG:
value = ReadInt32(offset);
break;
case ODBC32.SQL_C.SBIGINT:
value = ReadInt64(offset);
break;
case ODBC32.SQL_C.BIT:
Byte b = ReadByte(offset);
value = (b != 0x00);
break;
case ODBC32.SQL_C.REAL:
value = ReadSingle(offset);
break;
case ODBC32.SQL_C.DOUBLE:
value = ReadDouble(offset);
break;
case ODBC32.SQL_C.UTINYINT:
value = ReadByte(offset);
break;
case ODBC32.SQL_C.GUID:
value = ReadGuid(offset);
break;
case ODBC32.SQL_C.TYPE_TIMESTAMP:
//So we are mapping this ourselves.
//typedef struct tagTIMESTAMP_STRUCT
//{
// SQLSMALLINT year;
// SQLUSMALLINT month;
// SQLUSMALLINT day;
// SQLUSMALLINT hour;
// SQLUSMALLINT minute;
// SQLUSMALLINT second;
// SQLUINTEGER fraction; (billoniths of a second)
//}
value = ReadDateTime(offset);
break;
// Note: System does not provide a date-only type
case ODBC32.SQL_C.TYPE_DATE:
// typedef struct tagDATE_STRUCT
// {
// SQLSMALLINT year;
// SQLUSMALLINT month;
// SQLUSMALLINT day;
// } DATE_STRUCT;
value = ReadDate(offset);
break;
// Note: System does not provide a date-only type
case ODBC32.SQL_C.TYPE_TIME:
// typedef struct tagTIME_STRUCT
// {
// SQLUSMALLINT hour;
// SQLUSMALLINT minute;
// SQLUSMALLINT second;
// } TIME_STRUCT;
value = ReadTime(offset);
break;
case ODBC32.SQL_C.NUMERIC:
//Note: Unfortunatly the ODBC NUMERIC structure and the URT DECIMAL structure do not
//align, so we can't so do the typical "PtrToStructure" call (below) like other types
//We actually have to go through the pain of pulling our raw bytes and building the decimal
// Marshal.PtrToStructure(buffer, typeof(decimal));
//So we are mapping this ourselves
//typedef struct tagSQL_NUMERIC_STRUCT
//{
// SQLCHAR precision;
// SQLSCHAR scale;
// SQLCHAR sign; /* 1 if positive, 0 if negative */
// SQLCHAR val[SQL_MAX_NUMERIC_LEN];
//} SQL_NUMERIC_STRUCT;
value = ReadNumeric(offset);
break;
default:
Debug.Assert (false, "UnknownSQLCType");
value = null;
break;
};
return value;
}
// if sizeorprecision applies only for wchar and numeric values
// for wchar the a value of null means take the value's size
//
internal void MarshalToNative(int offset, object value, ODBC32.SQL_C sqlctype, int sizeorprecision, int valueOffset) {
switch(sqlctype) {
case ODBC32.SQL_C.WCHAR:
{
//Note: We always bind as unicode
//Note: StructureToPtr fails indicating string it a non-blittable type
//and there is no MarshalStringTo* that moves to an existing buffer,
//they all alloc and return a new one, not at all what we want...
//So we have to copy the raw bytes of the string ourself?!
Char[] rgChars;
int length;
Debug.Assert(value is string || value is char[],"Only string or char[] can be marshaled to WCHAR");
if (value is string) {
length = Math.Max(0, ((string)value).Length - valueOffset);
if ((sizeorprecision > 0) && (sizeorprecision < length)) {
length = sizeorprecision;
}
rgChars = ((string)value).ToCharArray(valueOffset, length);
Debug.Assert(rgChars.Length < (base.Length - valueOffset), "attempting to extend parameter buffer!");
WriteCharArray(offset, rgChars, 0, rgChars.Length);
WriteInt16(offset+(rgChars.Length * 2), 0); // Add the null terminator
}
else {
length = Math.Max(0, ((char[])value).Length - valueOffset);
if ((sizeorprecision > 0) && (sizeorprecision < length)) {
length = sizeorprecision;
}
rgChars = (char[])value;
Debug.Assert(rgChars.Length < (base.Length - valueOffset), "attempting to extend parameter buffer!");
WriteCharArray(offset, rgChars, valueOffset, length);
WriteInt16(offset+(rgChars.Length * 2), 0); // Add the null terminator
}
break;
}
case ODBC32.SQL_C.BINARY:
case ODBC32.SQL_C.CHAR:
{
Byte[] rgBytes = (Byte[])value;
int length = rgBytes.Length;
Debug.Assert ((valueOffset <= length), "Offset out of Range" );
// reduce length by the valueOffset
//
length -= valueOffset;
// reduce length to be no more than size (if size is given)
//
if ((sizeorprecision > 0) && (sizeorprecision < length)) {
length = sizeorprecision;
}
//AdjustSize(rgBytes.Length+1);
//buffer = DangerousAllocateAndGetHandle(); // Realloc may have changed buffer address
Debug.Assert(length < (base.Length - valueOffset), "attempting to extend parameter buffer!");
WriteBytes(offset, rgBytes, valueOffset, length);
break;
}
case ODBC32.SQL_C.UTINYINT:
WriteByte(offset, (Byte)value);
break;
case ODBC32.SQL_C.SSHORT: //Int16
WriteInt16(offset, (Int16)value);
break;
case ODBC32.SQL_C.SLONG: //Int32
WriteInt32(offset, (Int32)value);
break;
case ODBC32.SQL_C.REAL: //float
WriteSingle(offset, (Single)value);
break;
case ODBC32.SQL_C.SBIGINT: //Int64
WriteInt64(offset, (Int64)value);
break;
case ODBC32.SQL_C.DOUBLE: //Double
WriteDouble(offset, (Double)value);
break;
case ODBC32.SQL_C.GUID: //Guid
WriteGuid(offset, (Guid)value);
break;
case ODBC32.SQL_C.BIT:
WriteByte(offset, (Byte)(((bool)value) ? 1 : 0));
break;
case ODBC32.SQL_C.TYPE_TIMESTAMP:
{
//typedef struct tagTIMESTAMP_STRUCT
//{
// SQLSMALLINT year;
// SQLUSMALLINT month;
// SQLUSMALLINT day;
// SQLUSMALLINT hour;
// SQLUSMALLINT minute;
// SQLUSMALLINT second;
// SQLUINTEGER fraction; (billoniths of a second)
//}
//We have to map this ourselves, due to the different structures between
//ODBC TIMESTAMP and URT DateTime, (ie: can't use StructureToPtr)
WriteODBCDateTime(offset, (DateTime)value);
break;
}
// Note: System does not provide a date-only type
case ODBC32.SQL_C.TYPE_DATE:
{
// typedef struct tagDATE_STRUCT
// {
// SQLSMALLINT year;
// SQLUSMALLINT month;
// SQLUSMALLINT day;
// } DATE_STRUCT;
WriteDate(offset, (DateTime)value);
break;
}
// Note: System does not provide a date-only type
case ODBC32.SQL_C.TYPE_TIME:
{
// typedef struct tagTIME_STRUCT
// {
// SQLUSMALLINT hour;
// SQLUSMALLINT minute;
// SQLUSMALLINT second;
// } TIME_STRUCT;
WriteTime(offset, (TimeSpan)value);
break;
}
case ODBC32.SQL_C.NUMERIC:
{
WriteNumeric(offset, (Decimal)value, checked((byte)sizeorprecision));
break;
}
default:
Debug.Assert (false, "UnknownSQLCType");
break;
}
}
internal HandleRef PtrOffset(int offset, int length) {
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// NOTE: You must have called DangerousAddRef before calling this
// method, or you run the risk of allowing Handle Recycling
// to occur!
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Validate(offset, length);
IntPtr ptr = ADP.IntPtrOffset(DangerousGetHandle(), offset);
return new HandleRef(this, ptr);
}
internal void WriteODBCDateTime(int offset, DateTime value) {
short[] buffer = new short[6] {
unchecked((short)value.Year),
unchecked((short)value.Month),
unchecked((short)value.Day),
unchecked((short)value.Hour),
unchecked((short)value.Minute),
unchecked((short)value.Second),
};
WriteInt16Array(offset, buffer, 0, 6);
WriteInt32(offset + 12, value.Millisecond*1000000); //fraction
}
}
sealed internal class CStringTokenizer {
readonly StringBuilder _token;
readonly string _sqlstatement;
readonly char _quote; // typically the semicolon '"'
readonly char _escape; // typically the same char as the quote
int _len = 0;
int _idx = 0;
internal CStringTokenizer(string text, char quote, char escape) {
_token = new StringBuilder();
_quote = quote;
_escape = escape;
_sqlstatement = text;
if (text != null) {
int iNul = text.IndexOf('\0');
_len = (0>iNul)?text.Length:iNul;
}
else {
_len = 0;
}
}
internal int CurrentPosition {
get{ return _idx; }
}
// Returns the next token in the statement, advancing the current index to
// the start of the token
internal String NextToken() {
if (_token.Length != 0) { // if we've read a token before
_idx += _token.Length; // proceed the internal marker (_idx) behind the token
_token.Remove(0, _token.Length); // and start over with a fresh token
}
while((_idx < _len) && Char.IsWhiteSpace(_sqlstatement[_idx])) {
// skip whitespace
_idx++;
}
if (_idx == _len) {
// return if string is empty
return String.Empty;
}
int curidx = _idx; // start with internal index at current index
bool endtoken = false; //
// process characters until we reache the end of the token or the end of the string
//
while (!endtoken && curidx < _len) {
if (IsValidNameChar(_sqlstatement[curidx])) {
while ((curidx < _len) && IsValidNameChar(_sqlstatement[curidx])) {
_token.Append(_sqlstatement[curidx]);
curidx++;
}
}
else {
char currentchar = _sqlstatement[curidx];
if (currentchar == '[') {
curidx = GetTokenFromBracket(curidx);
}
else if (' ' != _quote && currentchar == _quote) { // if the ODBC driver does not support quoted identifiers it returns a single blank character
curidx = GetTokenFromQuote(curidx);
}
else {
// Some other marker like , ; ( or )
// could also be * or ?
if (!Char.IsWhiteSpace(currentchar)) {
switch (currentchar) {
case ',':
// commas are not part of a token so we'll only append them if they are at the beginning
if (curidx == _idx)
_token.Append(currentchar);
break;
default:
_token.Append(currentchar);
break;
}
}
endtoken = true;
break;
}
}
}
return (_token.Length > 0) ? _token.ToString() : String.Empty ;
}
private int GetTokenFromBracket(int curidx) {
Debug.Assert((_sqlstatement[curidx] == '['), "GetTokenFromQuote: character at starting position must be same as quotechar");
while (curidx < _len) {
_token.Append(_sqlstatement[curidx]);
curidx++;
if (_sqlstatement[curidx-1] == ']')
break;
}
return curidx;
}
// attempts to complete an encapsulated token (e.g. "scott")
// double quotes are valid part of the token (e.g. "foo""bar")
//
private int GetTokenFromQuote(int curidx) {
Debug.Assert(_quote != ' ', "ODBC driver doesn't support quoted identifiers -- GetTokenFromQuote should not be used in this case");
Debug.Assert((_sqlstatement[curidx] == _quote), "GetTokenFromQuote: character at starting position must be same as quotechar");
int localidx = curidx; // start with local index at current index
while (localidx < _len) { // run to the end of the statement
_token.Append(_sqlstatement[localidx]); // append current character to token
if (_sqlstatement[localidx] == _quote) {
if(localidx > curidx) { // don't care for the first char
if (_sqlstatement[localidx-1] != _escape) { // if it's not escape we look at the following char
if (localidx+1 < _len) { // do not overrun the end of the string
if (_sqlstatement[localidx+1] != _quote) {
return localidx+1; // We've reached the end of the quoted text
}
}
}
}
}
localidx++;
}
return localidx;
}
private bool IsValidNameChar(char ch) {
return (Char.IsLetterOrDigit(ch) ||
(ch == '_') || (ch == '-') ||(ch == '.') ||
(ch == '$') || (ch == '#') || (ch == '@') ||
(ch == '~') || (ch == '`') || (ch == '%') ||
(ch == '^') || (ch == '&') || (ch == '|') ) ;
}
// Searches for the token given, starting from the current position
// If found, positions the currentindex at the
// beginning of the token if found.
internal int FindTokenIndex(String tokenString) {
String nextToken;
while (true) {
nextToken = NextToken();
if ((_idx == _len) || ADP.IsEmpty(nextToken)) { // fxcop
break;
}
if (String.Compare(tokenString, nextToken, StringComparison.OrdinalIgnoreCase) == 0) {
return _idx;
}
}
return -1;
}
// Skips the white space found in the beginning of the string.
internal bool StartsWith(String tokenString) {
int tempidx = 0;
while((tempidx < _len) && Char.IsWhiteSpace(_sqlstatement[tempidx])) {
tempidx++;
}
if ((_len - tempidx) < tokenString.Length) {
return false;
}
if (0 == String.Compare(_sqlstatement, tempidx, tokenString, 0, tokenString.Length, StringComparison.OrdinalIgnoreCase)) {
// Reset current position and token
_idx = 0;
NextToken();
return true;
}
return false;
}
}
}
|