Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Data / System / Data / Odbc / OdbcUtils.cs
blobb5d23b8aae40fcaaf1e29bda470fc7b53696748d
1 //------------------------------------------------------------------------------
2 // <copyright file="OdbcUtils.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
9 using System;
10 using System.Data;
11 using System.Data.Common;
12 using System.Diagnostics; // Debug services
13 using System.Runtime.InteropServices;
14 using System.Text;
16 namespace System.Data.Odbc
18 sealed internal class CNativeBuffer : System.Data.ProviderBase.DbBuffer {
20 internal CNativeBuffer (int initialSize) : base(initialSize) {
23 internal short ShortLength {
24 get {
25 return checked((short)Length);
29 internal object MarshalToManaged(int offset, ODBC32.SQL_C sqlctype, int cb) {
30 object value;
31 switch(sqlctype)
33 case ODBC32.SQL_C.WCHAR:
34 //Note: We always bind as unicode
35 if(cb == ODBC32.SQL_NTS) {
36 value = PtrToStringUni(offset);
37 break;
39 Debug.Assert((cb > 0), "Character count negative ");
40 Debug.Assert((Length >= cb), "Native buffer too small ");
41 cb = Math.Min(cb/2, (Length-2)/2);
42 value= PtrToStringUni(offset, cb);
43 break;
45 case ODBC32.SQL_C.CHAR:
46 case ODBC32.SQL_C.BINARY:
47 Debug.Assert((cb > 0), "Character count negative ");
48 Debug.Assert((Length >= cb), "Native buffer too small ");
49 cb = Math.Min(cb, Length);
50 value = ReadBytes(offset, cb);
51 break;
53 case ODBC32.SQL_C.SSHORT:
54 value = ReadInt16(offset);
55 break;
57 case ODBC32.SQL_C.SLONG:
58 value = ReadInt32(offset);
59 break;
61 case ODBC32.SQL_C.SBIGINT:
62 value = ReadInt64(offset);
63 break;
65 case ODBC32.SQL_C.BIT:
66 Byte b = ReadByte(offset);
67 value = (b != 0x00);
68 break;
70 case ODBC32.SQL_C.REAL:
71 value = ReadSingle(offset);
72 break;
74 case ODBC32.SQL_C.DOUBLE:
75 value = ReadDouble(offset);
76 break;
78 case ODBC32.SQL_C.UTINYINT:
79 value = ReadByte(offset);
80 break;
82 case ODBC32.SQL_C.GUID:
83 value = ReadGuid(offset);
84 break;
86 case ODBC32.SQL_C.TYPE_TIMESTAMP:
87 //So we are mapping this ourselves.
88 //typedef struct tagTIMESTAMP_STRUCT
89 //{
90 // SQLSMALLINT year;
91 // SQLUSMALLINT month;
92 // SQLUSMALLINT day;
93 // SQLUSMALLINT hour;
94 // SQLUSMALLINT minute;
95 // SQLUSMALLINT second;
96 // SQLUINTEGER fraction; (billoniths of a second)
97 //}
99 value = ReadDateTime(offset);
100 break;
102 // Note: System does not provide a date-only type
103 case ODBC32.SQL_C.TYPE_DATE:
104 // typedef struct tagDATE_STRUCT
105 // {
106 // SQLSMALLINT year;
107 // SQLUSMALLINT month;
108 // SQLUSMALLINT day;
109 // } DATE_STRUCT;
111 value = ReadDate(offset);
112 break;
114 // Note: System does not provide a date-only type
115 case ODBC32.SQL_C.TYPE_TIME:
116 // typedef struct tagTIME_STRUCT
117 // {
118 // SQLUSMALLINT hour;
119 // SQLUSMALLINT minute;
120 // SQLUSMALLINT second;
121 // } TIME_STRUCT;
123 value = ReadTime(offset);
124 break;
126 case ODBC32.SQL_C.NUMERIC:
127 //Note: Unfortunatly the ODBC NUMERIC structure and the URT DECIMAL structure do not
128 //align, so we can't so do the typical "PtrToStructure" call (below) like other types
129 //We actually have to go through the pain of pulling our raw bytes and building the decimal
130 // Marshal.PtrToStructure(buffer, typeof(decimal));
132 //So we are mapping this ourselves
133 //typedef struct tagSQL_NUMERIC_STRUCT
135 // SQLCHAR precision;
136 // SQLSCHAR scale;
137 // SQLCHAR sign; /* 1 if positive, 0 if negative */
138 // SQLCHAR val[SQL_MAX_NUMERIC_LEN];
139 //} SQL_NUMERIC_STRUCT;
141 value = ReadNumeric(offset);
142 break;
144 default:
145 Debug.Assert (false, "UnknownSQLCType");
146 value = null;
147 break;
149 return value;
152 // if sizeorprecision applies only for wchar and numeric values
153 // for wchar the a value of null means take the value's size
155 internal void MarshalToNative(int offset, object value, ODBC32.SQL_C sqlctype, int sizeorprecision, int valueOffset) {
156 switch(sqlctype) {
157 case ODBC32.SQL_C.WCHAR:
159 //Note: We always bind as unicode
160 //Note: StructureToPtr fails indicating string it a non-blittable type
161 //and there is no MarshalStringTo* that moves to an existing buffer,
162 //they all alloc and return a new one, not at all what we want...
164 //So we have to copy the raw bytes of the string ourself?!
166 Char[] rgChars;
167 int length;
168 Debug.Assert(value is string || value is char[],"Only string or char[] can be marshaled to WCHAR");
170 if (value is string) {
171 length = Math.Max(0, ((string)value).Length - valueOffset);
173 if ((sizeorprecision > 0) && (sizeorprecision < length)) {
174 length = sizeorprecision;
177 rgChars = ((string)value).ToCharArray(valueOffset, length);
178 Debug.Assert(rgChars.Length < (base.Length - valueOffset), "attempting to extend parameter buffer!");
180 WriteCharArray(offset, rgChars, 0, rgChars.Length);
181 WriteInt16(offset+(rgChars.Length * 2), 0); // Add the null terminator
183 else {
184 length = Math.Max(0, ((char[])value).Length - valueOffset);
185 if ((sizeorprecision > 0) && (sizeorprecision < length)) {
186 length = sizeorprecision;
188 rgChars = (char[])value;
189 Debug.Assert(rgChars.Length < (base.Length - valueOffset), "attempting to extend parameter buffer!");
191 WriteCharArray(offset, rgChars, valueOffset, length);
192 WriteInt16(offset+(rgChars.Length * 2), 0); // Add the null terminator
194 break;
197 case ODBC32.SQL_C.BINARY:
198 case ODBC32.SQL_C.CHAR:
200 Byte[] rgBytes = (Byte[])value;
201 int length = rgBytes.Length;
203 Debug.Assert ((valueOffset <= length), "Offset out of Range" );
205 // reduce length by the valueOffset
207 length -= valueOffset;
209 // reduce length to be no more than size (if size is given)
211 if ((sizeorprecision > 0) && (sizeorprecision < length)) {
212 length = sizeorprecision;
215 //AdjustSize(rgBytes.Length+1);
216 //buffer = DangerousAllocateAndGetHandle(); // Realloc may have changed buffer address
217 Debug.Assert(length < (base.Length - valueOffset), "attempting to extend parameter buffer!");
219 WriteBytes(offset, rgBytes, valueOffset, length);
220 break;
223 case ODBC32.SQL_C.UTINYINT:
224 WriteByte(offset, (Byte)value);
225 break;
227 case ODBC32.SQL_C.SSHORT: //Int16
228 WriteInt16(offset, (Int16)value);
229 break;
231 case ODBC32.SQL_C.SLONG: //Int32
232 WriteInt32(offset, (Int32)value);
233 break;
235 case ODBC32.SQL_C.REAL: //float
236 WriteSingle(offset, (Single)value);
237 break;
239 case ODBC32.SQL_C.SBIGINT: //Int64
240 WriteInt64(offset, (Int64)value);
241 break;
243 case ODBC32.SQL_C.DOUBLE: //Double
244 WriteDouble(offset, (Double)value);
245 break;
247 case ODBC32.SQL_C.GUID: //Guid
248 WriteGuid(offset, (Guid)value);
249 break;
251 case ODBC32.SQL_C.BIT:
252 WriteByte(offset, (Byte)(((bool)value) ? 1 : 0));
253 break;
255 case ODBC32.SQL_C.TYPE_TIMESTAMP:
257 //typedef struct tagTIMESTAMP_STRUCT
259 // SQLSMALLINT year;
260 // SQLUSMALLINT month;
261 // SQLUSMALLINT day;
262 // SQLUSMALLINT hour;
263 // SQLUSMALLINT minute;
264 // SQLUSMALLINT second;
265 // SQLUINTEGER fraction; (billoniths of a second)
268 //We have to map this ourselves, due to the different structures between
269 //ODBC TIMESTAMP and URT DateTime, (ie: can't use StructureToPtr)
271 WriteODBCDateTime(offset, (DateTime)value);
272 break;
275 // Note: System does not provide a date-only type
276 case ODBC32.SQL_C.TYPE_DATE:
278 // typedef struct tagDATE_STRUCT
279 // {
280 // SQLSMALLINT year;
281 // SQLUSMALLINT month;
282 // SQLUSMALLINT day;
283 // } DATE_STRUCT;
285 WriteDate(offset, (DateTime)value);
286 break;
289 // Note: System does not provide a date-only type
290 case ODBC32.SQL_C.TYPE_TIME:
292 // typedef struct tagTIME_STRUCT
293 // {
294 // SQLUSMALLINT hour;
295 // SQLUSMALLINT minute;
296 // SQLUSMALLINT second;
297 // } TIME_STRUCT;
299 WriteTime(offset, (TimeSpan)value);
300 break;
303 case ODBC32.SQL_C.NUMERIC:
305 WriteNumeric(offset, (Decimal)value, checked((byte)sizeorprecision));
306 break;
309 default:
310 Debug.Assert (false, "UnknownSQLCType");
311 break;
315 internal HandleRef PtrOffset(int offset, int length) {
316 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
317 // NOTE: You must have called DangerousAddRef before calling this
318 // method, or you run the risk of allowing Handle Recycling
319 // to occur!
320 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
321 Validate(offset, length);
323 IntPtr ptr = ADP.IntPtrOffset(DangerousGetHandle(), offset);
324 return new HandleRef(this, ptr);
327 internal void WriteODBCDateTime(int offset, DateTime value) {
328 short[] buffer = new short[6] {
329 unchecked((short)value.Year),
330 unchecked((short)value.Month),
331 unchecked((short)value.Day),
332 unchecked((short)value.Hour),
333 unchecked((short)value.Minute),
334 unchecked((short)value.Second),
336 WriteInt16Array(offset, buffer, 0, 6);
337 WriteInt32(offset + 12, value.Millisecond*1000000); //fraction
342 sealed internal class CStringTokenizer {
343 readonly StringBuilder _token;
344 readonly string _sqlstatement;
345 readonly char _quote; // typically the semicolon '"'
346 readonly char _escape; // typically the same char as the quote
347 int _len = 0;
348 int _idx = 0;
350 internal CStringTokenizer(string text, char quote, char escape) {
351 _token = new StringBuilder();
352 _quote = quote;
353 _escape = escape;
354 _sqlstatement = text;
355 if (text != null) {
356 int iNul = text.IndexOf('\0');
357 _len = (0>iNul)?text.Length:iNul;
359 else {
360 _len = 0;
364 internal int CurrentPosition {
365 get{ return _idx; }
368 // Returns the next token in the statement, advancing the current index to
369 // the start of the token
370 internal String NextToken() {
371 if (_token.Length != 0) { // if we've read a token before
372 _idx += _token.Length; // proceed the internal marker (_idx) behind the token
373 _token.Remove(0, _token.Length); // and start over with a fresh token
376 while((_idx < _len) && Char.IsWhiteSpace(_sqlstatement[_idx])) {
377 // skip whitespace
378 _idx++;
381 if (_idx == _len) {
382 // return if string is empty
383 return String.Empty;
386 int curidx = _idx; // start with internal index at current index
387 bool endtoken = false; //
389 // process characters until we reache the end of the token or the end of the string
391 while (!endtoken && curidx < _len) {
392 if (IsValidNameChar(_sqlstatement[curidx])) {
393 while ((curidx < _len) && IsValidNameChar(_sqlstatement[curidx])) {
394 _token.Append(_sqlstatement[curidx]);
395 curidx++;
398 else {
399 char currentchar = _sqlstatement[curidx];
400 if (currentchar == '[') {
401 curidx = GetTokenFromBracket(curidx);
403 else if (' ' != _quote && currentchar == _quote) { // if the ODBC driver does not support quoted identifiers it returns a single blank character
404 curidx = GetTokenFromQuote(curidx);
406 else {
407 // Some other marker like , ; ( or )
408 // could also be * or ?
409 if (!Char.IsWhiteSpace(currentchar)) {
410 switch (currentchar) {
411 case ',':
412 // commas are not part of a token so we'll only append them if they are at the beginning
413 if (curidx == _idx)
414 _token.Append(currentchar);
415 break;
416 default:
417 _token.Append(currentchar);
418 break;
421 endtoken = true;
422 break;
427 return (_token.Length > 0) ? _token.ToString() : String.Empty ;
431 private int GetTokenFromBracket(int curidx) {
432 Debug.Assert((_sqlstatement[curidx] == '['), "GetTokenFromQuote: character at starting position must be same as quotechar");
433 while (curidx < _len) {
434 _token.Append(_sqlstatement[curidx]);
435 curidx++;
436 if (_sqlstatement[curidx-1] == ']')
437 break;
439 return curidx;
442 // attempts to complete an encapsulated token (e.g. "scott")
443 // double quotes are valid part of the token (e.g. "foo""bar")
445 private int GetTokenFromQuote(int curidx) {
446 Debug.Assert(_quote != ' ', "ODBC driver doesn't support quoted identifiers -- GetTokenFromQuote should not be used in this case");
447 Debug.Assert((_sqlstatement[curidx] == _quote), "GetTokenFromQuote: character at starting position must be same as quotechar");
449 int localidx = curidx; // start with local index at current index
450 while (localidx < _len) { // run to the end of the statement
451 _token.Append(_sqlstatement[localidx]); // append current character to token
452 if (_sqlstatement[localidx] == _quote) {
453 if(localidx > curidx) { // don't care for the first char
454 if (_sqlstatement[localidx-1] != _escape) { // if it's not escape we look at the following char
455 if (localidx+1 < _len) { // do not overrun the end of the string
456 if (_sqlstatement[localidx+1] != _quote) {
457 return localidx+1; // We've reached the end of the quoted text
463 localidx++;
465 return localidx;
468 private bool IsValidNameChar(char ch) {
469 return (Char.IsLetterOrDigit(ch) ||
470 (ch == '_') || (ch == '-') ||(ch == '.') ||
471 (ch == '$') || (ch == '#') || (ch == '@') ||
472 (ch == '~') || (ch == '`') || (ch == '%') ||
473 (ch == '^') || (ch == '&') || (ch == '|') ) ;
476 // Searches for the token given, starting from the current position
477 // If found, positions the currentindex at the
478 // beginning of the token if found.
479 internal int FindTokenIndex(String tokenString) {
480 String nextToken;
481 while (true) {
482 nextToken = NextToken();
483 if ((_idx == _len) || ADP.IsEmpty(nextToken)) { // fxcop
484 break;
486 if (String.Compare(tokenString, nextToken, StringComparison.OrdinalIgnoreCase) == 0) {
487 return _idx;
490 return -1;
493 // Skips the white space found in the beginning of the string.
494 internal bool StartsWith(String tokenString) {
495 int tempidx = 0;
496 while((tempidx < _len) && Char.IsWhiteSpace(_sqlstatement[tempidx])) {
497 tempidx++;
499 if ((_len - tempidx) < tokenString.Length) {
500 return false;
503 if (0 == String.Compare(_sqlstatement, tempidx, tokenString, 0, tokenString.Length, StringComparison.OrdinalIgnoreCase)) {
504 // Reset current position and token
505 _idx = 0;
506 NextToken();
507 return true;
509 return false;