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