importing messaging-2008 branch to trunk.
[mono.git] / mcs / class / Mono.Data.Tds / Mono.Data.Tds.Protocol / Tds70.cs
1 //
2 // Mono.Data.Tds.Protocol.Tds70.cs
3 //
4 // Author:
5 //   Tim Coleman (tim@timcoleman.com)
6 //   Diego Caravana (diego@toth.it)
7 //   Sebastien Pouliot (sebastien@ximian.com)
8 //   Daniel Morgan (danielmorgan@verizon.net)
9 //
10 // Copyright (C) 2002 Tim Coleman
11 // Portions (C) 2003 Motus Technologies Inc. (http://www.motus.com)
12 // Portions (C) 2003 Daniel Morgan
13 //
14
15 //
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
23 // 
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
26 // 
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 //
35
36 using System;
37 using System.Globalization;
38 using System.Text;
39
40 using Mono.Security.Protocol.Ntlm;
41
42 namespace Mono.Data.Tds.Protocol
43 {
44         public sealed class Tds70 : Tds
45         {
46                 #region Fields
47
48                 public readonly static TdsVersion Version = TdsVersion.tds70;
49                 static readonly decimal SMALLMONEY_MIN = -214748.3648m;
50                 static readonly decimal SMALLMONEY_MAX = 214748.3647m;
51
52                 #endregion // Fields
53
54                 #region Constructors
55
56                 public Tds70 (string server, int port)
57                         : this (server, port, 512, 15)
58                 {
59                 }
60
61                 public Tds70 (string server, int port, int packetSize, int timeout)
62                         : base (server, port, packetSize, timeout, Version)
63                 {
64                 }
65
66                 #endregion // Constructors
67
68                 #region Methods
69
70                 private string BuildExec (string sql)
71                 {
72                         string esql = sql.Replace ("'", "''"); // escape single quote
73                         if (Parameters != null && Parameters.Count > 0)
74                                 return BuildProcedureCall (String.Format ("sp_executesql N'{0}', N'{1}', ", esql, BuildPreparedParameters ()));
75                         else
76                                 return BuildProcedureCall (String.Format ("sp_executesql N'{0}'", esql));
77                 }
78
79                 private string BuildParameters ()
80                 {
81                         if (Parameters == null || Parameters.Count == 0)
82                                 return String.Empty;
83
84                         StringBuilder result = new StringBuilder ();
85                         foreach (TdsMetaParameter p in Parameters) {
86                                 string parameterName = p.ParameterName;
87                                 if (parameterName [0] == '@') {
88                                         parameterName = parameterName.Substring (1);
89                                 }
90                                 if (p.Direction != TdsParameterDirection.ReturnValue) {
91                                         if (result.Length > 0)
92                                                 result.Append (", ");
93                                         if (p.Direction == TdsParameterDirection.InputOutput)
94                                                 result.AppendFormat ("@{0}={0} output", parameterName);
95                                         else
96                                                 result.Append (FormatParameter (p));
97                                 }
98                         }
99                         return result.ToString ();
100                 }
101
102                 private string BuildPreparedParameters ()
103                 {
104                         StringBuilder parms = new StringBuilder ();
105                         foreach (TdsMetaParameter p in Parameters) {
106                                 if (parms.Length > 0)
107                                         parms.Append (", ");
108                                 parms.Append (p.Prepare ());
109                                 if (p.Direction == TdsParameterDirection.Output)
110                                         parms.Append (" output");
111                         }
112                         return parms.ToString ();
113                 }
114
115                 private string BuildPreparedQuery (string id)
116                 {
117                         return BuildProcedureCall (String.Format ("sp_execute {0},", id));
118                 }
119
120                 private string BuildProcedureCall (string procedure)
121                 {
122                         string exec = String.Empty;
123
124                         StringBuilder declare = new StringBuilder ();
125                         StringBuilder select = new StringBuilder ();
126                         StringBuilder set = new StringBuilder ();
127                         
128                         int count = 0;
129                         if (Parameters != null) {
130                                 foreach (TdsMetaParameter p in Parameters) {
131                                         if (p.Direction != TdsParameterDirection.Input) {
132                                                 if (count == 0)
133                                                         select.Append ("select ");
134                                                 else
135                                                         select.Append (", ");
136                                                 select.Append (p.ParameterName);
137                                                         
138                                                 declare.Append (String.Format ("declare {0}\n", p.Prepare ()));
139
140                                                 if (p.Direction != TdsParameterDirection.ReturnValue) {
141                                                         if (p.Direction == TdsParameterDirection.InputOutput)
142                                                                 set.Append (String.Format ("set {0}\n", FormatParameter(p)));
143                                                         else
144                                                                 set.Append (String.Format ("set {0}=NULL\n", p.ParameterName));
145                                                 }
146                                         
147                                                 count += 1;
148                                         }
149                                         
150                                         if (p.Direction == TdsParameterDirection.ReturnValue)
151                                                 exec = p.ParameterName + "=";
152                                 }
153                         }
154                         exec = "exec " + exec;
155
156                         return String.Format ("{0}{1}{2}{3} {4}\n{5}",
157                                 declare.ToString (), set.ToString (), exec,
158                                 procedure, BuildParameters (), select.ToString ());
159                 }
160
161                 public override bool Connect (TdsConnectionParameters connectionParameters)
162                 {
163                         if (IsConnected)
164                                 throw new InvalidOperationException ("The connection is already open.");
165         
166                         connectionParms = connectionParameters;
167
168                         SetLanguage (connectionParameters.Language);
169                         SetCharset ("utf-8");
170                 
171                         byte[] empty = new byte[0];
172                         short authLen = 0;
173                         byte pad = (byte) 0;
174                         
175                         byte[] domainMagic = { 6, 0x7d, 0x0f, 0xfd, 0xff, 0x0, 0x0, 0x0,
176                                                                         0x0, 0xe0, 0x83, 0x0, 0x0,
177                                                                         0x68, 0x01, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00 };
178                         byte[] sqlserverMagic = { 6, 0x0, 0x0, 0x0,
179                                                                                 0x0, 0x0, 0x0, 0x0,
180                                                                                 0x0, 0xe0, 0x03, 0x0,
181                                                                                 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 
182                                                                                 0x0, 0x0, 0x0 };
183                         byte[] magic = null;
184                         
185                         if (connectionParameters.DomainLogin)
186                                 magic = domainMagic;
187                         else
188                                 magic = sqlserverMagic;
189                         
190                         string username = connectionParameters.User;
191                         string domain = null;
192
193                         int idx = username.IndexOf ("\\");
194                         if (idx != -1) {
195                                 domain = username.Substring (0, idx);
196                                 username = username.Substring (idx + 1);
197
198                                 connectionParameters.DefaultDomain = domain;
199                                 connectionParameters.User = username;
200                         } else {
201                                 domain = Environment.UserDomainName;
202                                 connectionParameters.DefaultDomain = domain;
203                         }
204
205                         short partialPacketSize = (short) (86 + (
206                                 connectionParameters.Hostname.Length +
207                                 connectionParameters.ApplicationName.Length +
208                                 DataSource.Length +
209                                 connectionParameters.LibraryName.Length +
210                                 Language.Length +
211                                 connectionParameters.Database.Length +
212                                 connectionParameters.AttachDBFileName.Length) * 2);
213
214                         if (connectionParameters.DomainLogin) {
215                                 authLen = ((short) (32 + (connectionParameters.Hostname.Length +
216                                         domain.Length)));
217                                 partialPacketSize += authLen;
218                         } else
219                                 partialPacketSize += ((short) ((username.Length + connectionParameters.Password.Length) * 2));
220                         
221                         int totalPacketSize = partialPacketSize;
222                         
223                         Comm.StartPacket (TdsPacketType.Logon70);
224                         
225                         Comm.Append (totalPacketSize);
226
227                         //Comm.Append (empty, 3, pad);
228                         byte[] version = {0x00, 0x0, 0x0, 0x70};
229                         Comm.Append (version); // TDS Version 7
230                         Comm.Append ((int)this.PacketSize); // Set the Block Size
231                         Comm.Append (empty, 3, pad);
232                         Comm.Append (magic);
233
234                         short curPos = 86;
235
236                         // Hostname
237                         Comm.Append (curPos);
238                         Comm.Append ((short) connectionParameters.Hostname.Length);
239                         curPos += (short) (connectionParameters.Hostname.Length * 2);
240
241                         if (connectionParameters.DomainLogin) {
242                                 Comm.Append((short)0);
243                                 Comm.Append((short)0);
244                                 Comm.Append((short)0);
245                                 Comm.Append((short)0);
246                         } else {
247                                 // Username
248                                 Comm.Append (curPos);
249                                 Comm.Append ((short) username.Length);
250                                 curPos += ((short) (username.Length * 2));
251
252                                 // Password
253                                 Comm.Append (curPos);
254                                 Comm.Append ((short) connectionParameters.Password.Length);
255                                 curPos += (short) (connectionParameters.Password.Length * 2);
256                         }
257
258                         // AppName
259                         Comm.Append (curPos);
260                         Comm.Append ((short) connectionParameters.ApplicationName.Length);
261                         curPos += (short) (connectionParameters.ApplicationName.Length * 2);
262
263                         // Server Name
264                         Comm.Append (curPos);
265                         Comm.Append ((short) DataSource.Length);
266                         curPos += (short) (DataSource.Length * 2);
267
268                         // Unknown
269                         Comm.Append ((short) curPos);
270                         Comm.Append ((short) 0);
271
272                         // Library Name
273                         Comm.Append (curPos);
274                         Comm.Append ((short) connectionParameters.LibraryName.Length);
275                         curPos += (short) (connectionParameters.LibraryName.Length * 2);
276
277                         // Language
278                         Comm.Append (curPos);
279                         Comm.Append ((short) Language.Length);
280                         curPos += (short) (Language.Length * 2);
281
282                         // Database
283                         Comm.Append (curPos);
284                         Comm.Append ((short) connectionParameters.Database.Length);
285                         curPos += (short) (connectionParameters.Database.Length * 2);
286
287                         // MAC Address
288                         Comm.Append((byte) 0);
289                         Comm.Append((byte) 0);
290                         Comm.Append((byte) 0);
291                         Comm.Append((byte) 0);
292                         Comm.Append((byte) 0);
293                         Comm.Append((byte) 0);
294
295                         // Authentication Stuff
296                         Comm.Append ((short) curPos);
297                         if (connectionParameters.DomainLogin) {
298                                 Comm.Append ((short) authLen);
299                                 curPos += (short) authLen;
300                         } else
301                                 Comm.Append ((short) 0);
302                         
303                         // Unknown
304                         Comm.Append (curPos);
305                         Comm.Append ((short)( connectionParameters.AttachDBFileName.Length));
306                         curPos += (short)(connectionParameters.AttachDBFileName.Length*2);
307                         
308                         // Connection Parameters
309                         Comm.Append (connectionParameters.Hostname);
310                         if (!connectionParameters.DomainLogin) {
311                                 // SQL Server Authentication
312                                 Comm.Append (connectionParameters.User);
313                                 string scrambledPwd = EncryptPassword (connectionParameters.Password);
314                                 Comm.Append (scrambledPwd);
315                         }
316                         Comm.Append (connectionParameters.ApplicationName);
317                         Comm.Append (DataSource);
318                         Comm.Append (connectionParameters.LibraryName);
319                         Comm.Append (Language);
320                         Comm.Append (connectionParameters.Database);
321
322                         if (connectionParameters.DomainLogin) {
323                                 // the rest of the packet is NTLMSSP authentication
324                                 Type1Message msg = new Type1Message ();
325                                 msg.Domain = domain;
326                                 msg.Host = connectionParameters.Hostname;
327                                 msg.Flags = NtlmFlags.NegotiateUnicode |
328                                         NtlmFlags.NegotiateNtlm |
329                                         NtlmFlags.NegotiateDomainSupplied |
330                                         NtlmFlags.NegotiateWorkstationSupplied |
331                                         NtlmFlags.NegotiateAlwaysSign; // 0xb201
332                                 Comm.Append (msg.GetBytes ());
333                         }
334
335                         Comm.Append (connectionParameters.AttachDBFileName);
336                         Comm.SendPacket ();
337                         MoreResults = true;
338                         SkipToEnd ();
339                         
340                         return IsConnected;
341                 }
342
343                 private static string EncryptPassword (string pass)
344                 {
345                         int xormask = 0x5a5a;
346                         int len = pass.Length;
347                         char[] chars = new char[len];
348
349                         for (int i = 0; i < len; ++i) {
350                                 int c = ((int) (pass[i])) ^ xormask;
351                                 int m1 = (c >> 4) & 0x0f0f;
352                                 int m2 = (c << 4) & 0xf0f0;
353                                 chars[i] = (char) (m1 | m2);
354                         }
355
356                         return new String (chars);
357                 }
358
359                 public override bool Reset ()
360                 {
361                         // Check validity of the connection - a false removes
362                         // the connection from the pool
363                         // NOTE: MS implementation will throw a connection-reset error as it will
364                         // try to use the same connection
365                         if (!Comm.IsConnected ())
366                                 return false;
367
368                         // Set "reset-connection" bit for the next message packet
369                         Comm.ResetConnection = true;
370
371                         return true;
372                 }
373
374                 public override void ExecPrepared (string commandText, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
375                 {
376                         Parameters = parameters;
377                         ExecuteQuery (BuildPreparedQuery (commandText), timeout, wantResults);
378                 }
379                         
380                 public override void ExecProc (string commandText, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
381                 {
382                         Parameters = parameters;
383                         ExecRPC (commandText, parameters, timeout, wantResults);
384                 }
385
386                 protected override void ExecRPC (string rpcName, TdsMetaParameterCollection parameters, 
387                                                  int timeout, bool wantResults)
388                 {
389                         // clean up
390                         InitExec ();
391                         Comm.StartPacket (TdsPacketType.RPC);
392
393                         Comm.Append ( (short) rpcName.Length);
394                         Comm.Append (rpcName);
395                         Comm.Append ( (short) 0); //no meta data
396                         if (parameters != null) {
397                                 foreach (TdsMetaParameter param in parameters) {
398                                         if (param.Direction == TdsParameterDirection.ReturnValue) 
399                                                 continue;
400                                         string pname = param.ParameterName;
401                                         if (pname != null && pname.Length > 0 && pname [0] == '@') {
402                                                 Comm.Append ( (byte) pname.Length);
403                                                 Comm.Append (pname);
404                                         } else {
405                                                 Comm.Append ( (byte) (pname.Length + 1));
406                                                 Comm.Append ("@" + pname);
407                                         }
408                                         short status = 0; // unused
409                                         if (param.Direction != TdsParameterDirection.Input)
410                                                 status |= 0x01; // output
411                                         Comm.Append ( (byte) status);
412                                         WriteParameterInfo (param);
413                                 }
414                         }
415                         Comm.SendPacket ();
416                         CheckForData (timeout);
417                         if (!wantResults)
418                                 SkipToEnd ();
419                 }
420
421                 private void WriteParameterInfo (TdsMetaParameter param)
422                 {
423                         /*
424                         Ms.net send non-nullable datatypes as nullable and allows setting null values
425                         to int/float etc.. So, using Nullable form of type for all data
426                         */
427                         param.IsNullable = true;
428                         TdsColumnType colType = param.GetMetaType ();
429                         param.IsNullable = false;
430
431                         Comm.Append ((byte)colType); // type
432
433                         int size = param.Size;
434                         if (size == 0)
435                                 size = param.GetActualSize ();
436
437                         /*
438                           If column type is SqlDbType.NVarChar the size of parameter is multiplied by 2
439                           FIXME: Need to check for other types
440                          */
441                         if (colType == TdsColumnType.BigNVarChar)
442                                 size <<= 1;
443                         if (IsLargeType (colType))
444                                 Comm.Append ((short)size); // Parameter size passed in SqlParameter
445                         else if (IsBlobType (colType))
446                                 Comm.Append (size); // Parameter size passed in SqlParameter
447                         else
448                                 Comm.Append ((byte)size);
449
450                         // Precision and Scale are non-zero for only decimal/numeric
451                         if ( param.TypeName == "decimal" || param.TypeName == "numeric") {
452                                 Comm.Append ((param.Precision !=0 ) ? param.Precision : (byte) 28);
453                                 Comm.Append (param.Scale);
454                         }
455
456                         size = param.GetActualSize ();
457                         if (IsLargeType (colType))
458                                 Comm.Append ((short)size);
459                         else if (IsBlobType (colType))
460                                 Comm.Append (size);
461                         else
462                                 Comm.Append ((byte)size);
463
464                         if (size > 0) {
465                                 switch (param.TypeName) {
466                                 case "money" : {
467                                         Decimal val = (decimal) param.Value;
468                                         int[] arr = Decimal.GetBits (val);
469
470                                         if (val >= 0) {
471                                                 Comm.Append (arr[1]);
472                                                 Comm.Append (arr[0]);
473                                         } else {
474                                                 Comm.Append (~arr[1]);
475                                                 Comm.Append (~arr[0] + 1);
476                                         }
477                                         break;
478                                 }
479                                 case "smallmoney": {
480                                         Decimal val = (decimal) param.Value;
481                                         if (val < SMALLMONEY_MIN || val > SMALLMONEY_MAX)
482                                                 throw new OverflowException (string.Format (
483                                                         CultureInfo.InvariantCulture,
484                                                         "Value '{0}' is not valid for SmallMoney."
485                                                         + "  Must be between {1:N4} and {2:N4}.",
486 #if NET_2_0
487                                                         val,
488 #else
489                                                         val.ToString (CultureInfo.CurrentCulture),
490 #endif
491                                                         SMALLMONEY_MIN, SMALLMONEY_MAX));
492
493                                         int[] arr = Decimal.GetBits (val);
494                                         int sign = (val>0 ? 1: -1);
495                                         Comm.Append (sign * arr[0]);
496                                         break;
497                                 }
498                                 case "datetime":
499                                         Comm.Append ((DateTime)param.Value, 8);
500                                         break;
501                                 case "smalldatetime":
502                                         Comm.Append ((DateTime)param.Value, 4);
503                                         break;
504                                 case "varchar" :
505                                 case "nvarchar" :
506                                 case "char" :
507                                 case "nchar" :
508                                 case "text" :
509                                 case "ntext" :
510                                         byte [] tmp = param.GetBytes ();
511                                         Comm.Append (tmp);
512                                         break;
513                                 case "uniqueidentifier" :
514                                         Comm.Append (((Guid)param.Value).ToByteArray());
515                                         break;
516                                 default :
517                                         Comm.Append (param.Value);
518                                         break;
519                                 }
520                         }
521                         return;
522                 }
523
524                 public override void Execute (string commandText, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
525                 {
526                         Parameters = parameters;
527                         string sql = commandText;
528                         if (wantResults || (Parameters != null && Parameters.Count > 0))
529                                 sql = BuildExec (commandText);
530                         ExecuteQuery (sql, timeout, wantResults);
531                 }
532
533                 private string FormatParameter (TdsMetaParameter parameter)
534                 {
535                         string parameterName = parameter.ParameterName;
536                         if (parameterName [0] == '@') {
537                                 parameterName = parameterName.Substring (1);
538                         }
539                         if (parameter.Direction == TdsParameterDirection.Output)
540                                 return String.Format ("@{0}={0} output", parameterName);
541                         if (parameter.Value == null || parameter.Value == DBNull.Value)
542                                 return parameter.ParameterName + "=NULL";
543
544                         string value = null;
545                         switch (parameter.TypeName) {
546                         case "smalldatetime":
547                         case "datetime":
548                                 DateTime d = Convert.ToDateTime (parameter.Value);
549                                 value = String.Format (base.Locale,
550                                         "'{0:MMM dd yyyy hh:mm:ss.fff tt}'", d);
551                                 break;
552                         case "bigint":
553                         case "decimal":
554                         case "float":
555                         case "int":
556                         case "money":
557                         case "real":
558                         case "smallint":
559                         case "smallmoney":
560                         case "tinyint":
561                                 object paramValue = parameter.Value;
562                                 Type paramType = paramValue.GetType ();
563                                 if (paramType.IsEnum)
564                                         paramValue = Convert.ChangeType (paramValue,
565                                                 Type.GetTypeCode (paramType));
566                                 value = paramValue.ToString ();
567                                 break;
568                         case "nvarchar":
569                         case "nchar":
570                                 value = String.Format ("N'{0}'", parameter.Value.ToString ().Replace ("'", "''"));
571                                 break;
572                         case "uniqueidentifier":
573                                 value = String.Format ("'{0}'", ((Guid) parameter.Value).ToString (string.Empty));
574                                 break;
575                         case "bit":
576                                 if (parameter.Value.GetType () == typeof (bool))
577                                         value = (((bool) parameter.Value) ? "0x1" : "0x0");
578                                 else
579                                         value = parameter.Value.ToString ();
580                                 break;
581                         case "image":
582                         case "binary":
583                         case "varbinary":
584                                 byte[] byteArray = (byte[]) parameter.Value;
585                                 // In 1.0 profile, BitConverter.ToString() throws ArgumentOutOfRangeException when passed a 0-length
586                                 // array, so handle that as a special case.
587                                 if (byteArray.Length == 0)
588                                         value = "0x";
589                                 else
590                                         value = String.Format ("0x{0}", BitConverter.ToString (byteArray).Replace ("-", string.Empty).ToLower ());
591                                 break;
592                         default:
593                                 value = String.Format ("'{0}'", parameter.Value.ToString ().Replace ("'", "''"));
594                                 break;
595                         }
596
597                         return "@" + parameterName + "=" + value;
598                 }
599
600                 public override string Prepare (string commandText, TdsMetaParameterCollection parameters)
601                 {
602                         Parameters = parameters;
603
604                         TdsMetaParameterCollection parms = new TdsMetaParameterCollection ();
605                         TdsMetaParameter parm = new TdsMetaParameter ("@Handle", "int", null);
606                         parm.Direction = TdsParameterDirection.Output;
607                         parms.Add (parm);
608
609                         parms.Add (new TdsMetaParameter ("@VarDecl", "nvarchar", BuildPreparedParameters ()));
610                         parms.Add (new TdsMetaParameter ("@Query", "nvarchar", commandText));
611
612                         ExecProc ("sp_prepare", parms, 0, true);
613                         SkipToEnd ();
614                         return OutputParameters[0].ToString () ;
615                         //if (ColumnValues == null || ColumnValues [0] == null || ColumnValues [0] == DBNull.Value)
616                         //      throw new TdsInternalException ();
617                         //return string.Empty;
618                         //return ColumnValues [0].ToString ();
619                 }
620
621                 protected override TdsDataColumnCollection ProcessColumnInfo ()
622                 {
623                         TdsDataColumnCollection result = new TdsDataColumnCollection ();
624                         int numColumns = Comm.GetTdsShort ();
625                         for (int i = 0; i < numColumns; i += 1) {
626                                 byte[] flagData = new byte[4];
627                                 for (int j = 0; j < 4; j += 1) 
628                                         flagData[j] = Comm.GetByte ();
629
630                                 bool nullable = (flagData[2] & 0x01) > 0;
631                                 //bool caseSensitive = (flagData[2] & 0x02) > 0;
632                                 bool writable = (flagData[2] & 0x0c) > 0;
633                                 bool autoIncrement = (flagData[2] & 0x10) > 0;
634                                 bool isIdentity = (flagData[2] & 0x10) > 0;
635
636                                 TdsColumnType columnType = (TdsColumnType) (Comm.GetByte () & 0xff);
637                                 if ((byte) columnType == 0xef)
638                                         columnType = TdsColumnType.NChar;
639
640                                 byte xColumnType = 0;
641                                 if (IsLargeType (columnType)) {
642                                         xColumnType = (byte) columnType;
643                                         if (columnType != TdsColumnType.NChar)
644                                                 columnType -= 128;
645                                 }
646
647                                 int columnSize;
648                                 string tableName = null;
649
650                                 if (IsBlobType (columnType)) {
651                                         columnSize = Comm.GetTdsInt ();
652                                         tableName = Comm.GetString (Comm.GetTdsShort ());
653                                 } else if (IsFixedSizeColumn (columnType))
654                                         columnSize = LookupBufferSize (columnType);
655                                 else if (IsLargeType ((TdsColumnType) xColumnType))
656                                         columnSize = Comm.GetTdsShort ();
657                                 else
658                                         columnSize = Comm.GetByte () & 0xff;
659
660                                 byte precision = 0;
661                                 byte scale = 0;
662
663                                 switch (columnType) {
664                                 case TdsColumnType.NText:
665                                 case TdsColumnType.NChar:
666                                 case TdsColumnType.NVarChar:
667                                         columnSize /= 2;
668                                         break;
669                                 case TdsColumnType.Decimal:
670                                 case TdsColumnType.Numeric:
671                                   /*
672                                         Comm.Skip (1);
673                                   */
674                                         precision = Comm.GetByte ();
675                                         scale = Comm.GetByte ();
676                                         break;
677                                 }
678
679                                 string columnName = Comm.GetString (Comm.GetByte ());
680
681                                 TdsDataColumn col = new TdsDataColumn ();
682                                 result.Add (col);
683 #if NET_2_0
684                                 col.ColumnType = columnType;
685                                 col.ColumnName = columnName;
686                                 col.IsAutoIncrement = autoIncrement;
687                                 col.IsIdentity = isIdentity;
688                                 col.ColumnSize = columnSize;
689                                 col.NumericPrecision = precision;
690                                 col.NumericScale = scale;
691                                 col.IsReadOnly = !writable;
692                                 col.AllowDBNull = nullable;
693                                 col.BaseTableName = tableName;
694 #else
695                                 col ["ColumnType"] = columnType;
696                                 col ["ColumnName"] = columnName;
697                                 col ["IsAutoIncrement"] = autoIncrement;
698                                 col ["IsIdentity"] = isIdentity;
699                                 col ["ColumnSize"] = columnSize;
700                                 col ["NumericPrecision"] = precision;
701                                 col ["NumericScale"] = scale;
702                                 col ["IsReadOnly"] = !writable;
703                                 col ["AllowDBNull"] = nullable;
704                                 col ["BaseTableName"] = tableName;
705 #endif
706                         }
707                         return result;
708                 }
709
710                 public override void Unprepare (string statementId)
711                 {
712                         TdsMetaParameterCollection parms = new TdsMetaParameterCollection ();
713                         parms.Add (new TdsMetaParameter ("@P1", "int", Int32.Parse (statementId)));
714                         ExecProc ("sp_unprepare", parms, 0, false);
715                 }
716                 
717                 protected override bool IsValidRowCount (byte status, byte op)
718                 {
719                         if ((status & (byte)0x10) == 0 || op == (byte)0xc1)
720                                 return false;
721                         return true; 
722                 }
723
724                 protected override void ProcessReturnStatus ()
725                 {
726                         int result = Comm.GetTdsInt ();
727                         if (Parameters != null) {
728                                 foreach (TdsMetaParameter param in Parameters) {
729                                         if (param.Direction == TdsParameterDirection.ReturnValue) {
730                                                 param.Value = result;
731                                                 break;
732                                         }
733                                 }
734                         }
735                 }
736
737                 #endregion // Methods
738
739 #if NET_2_0
740                 #region Asynchronous Methods
741
742                 public override IAsyncResult BeginExecuteNonQuery (string cmdText,
743                                                           TdsMetaParameterCollection parameters,
744                                                           AsyncCallback callback,
745                                                           object state)
746                 {
747                         Parameters = parameters;
748                         string sql = cmdText;
749                         if (Parameters != null && Parameters.Count > 0)
750                                 sql = BuildExec (cmdText);
751
752                         IAsyncResult ar = BeginExecuteQueryInternal (sql, false, callback, state);
753                         return ar;
754                 }
755
756                 public override void EndExecuteNonQuery (IAsyncResult ar)
757                 {
758                         EndExecuteQueryInternal (ar);
759                 }
760
761                 public override IAsyncResult BeginExecuteQuery (string cmdText,
762                                                                 TdsMetaParameterCollection parameters,
763                                                                 AsyncCallback callback,
764                                                                 object state)
765                 {
766                         Parameters = parameters;
767                         string sql = cmdText;
768                         if (Parameters != null && Parameters.Count > 0)
769                                 sql = BuildExec (cmdText);
770
771                         IAsyncResult ar = BeginExecuteQueryInternal (sql, true, callback, state);
772                         return ar;
773                 }
774
775                 public override void EndExecuteQuery (IAsyncResult ar)
776                 {
777                         EndExecuteQueryInternal (ar);
778                 }
779
780                 public override IAsyncResult BeginExecuteProcedure (string prolog,
781                                                                     string epilog,
782                                                                     string cmdText,
783                                                                     bool IsNonQuery,
784                                                                     TdsMetaParameterCollection parameters,
785                                                                     AsyncCallback callback,
786                                                                     object state)
787                 {
788                         Parameters = parameters;
789                         string pcall = BuildProcedureCall (cmdText);
790                         string sql = String.Format ("{0};{1};{2};", prolog, pcall, epilog);
791
792                         IAsyncResult ar = BeginExecuteQueryInternal (sql, !IsNonQuery, callback, state);
793                         return ar;
794                 }
795
796                 public override void EndExecuteProcedure (IAsyncResult ar)
797                 {
798                         EndExecuteQueryInternal (ar);
799                 }
800
801                 #endregion // Asynchronous Methods
802 #endif // NET_2_0
803         }
804 }