2 // Mono.Data.Tds.Protocol.Tds70.cs
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 // Gert Driesen (drieseng@users.sourceforge.net)
10 // Veerapuram Varadhan (vvaradhan@novell.com)
12 // Copyright (C) 2002 Tim Coleman
13 // Portions (C) 2003 Motus Technologies Inc. (http://www.motus.com)
14 // Portions (C) 2003 Daniel Morgan
17 // Permission is hereby granted, free of charge, to any person obtaining
18 // a copy of this software and associated documentation files (the
19 // "Software"), to deal in the Software without restriction, including
20 // without limitation the rights to use, copy, modify, merge, publish,
21 // distribute, sublicense, and/or sell copies of the Software, and to
22 // permit persons to whom the Software is furnished to do so, subject to
23 // the following conditions:
25 // The above copyright notice and this permission notice shall be
26 // included in all copies or substantial portions of the Software.
28 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
32 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
33 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
34 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38 using System.Globalization;
41 using Mono.Security.Protocol.Ntlm;
43 namespace Mono.Data.Tds.Protocol
45 public class Tds70 : Tds
49 //public readonly static TdsVersion Version = TdsVersion.tds70;
50 static readonly decimal SMALLMONEY_MIN = -214748.3648m;
51 static readonly decimal SMALLMONEY_MAX = 214748.3647m;
57 public Tds70 (string server, int port)
58 : this (server, port, 512, 15)
62 public Tds70 (string server, int port, int packetSize, int timeout)
63 : base (server, port, packetSize, timeout, TdsVersion.tds70)
67 public Tds70 (string server, int port, int packetSize, int timeout, TdsVersion version)
68 : base (server, port, packetSize, timeout, version)
72 #endregion // Constructors
76 protected virtual byte[] ClientVersion {
77 get { return new byte[] {0x00, 0x0, 0x0, 0x70};}
80 // Default precision is 28 for a 7.0 server. Unless and
81 // otherwise the server is started with /p option - which would be 38
82 protected virtual byte Precision {
86 #endregion // Properties
90 protected string BuildExec (string sql)
92 string esql = sql.Replace ("'", "''"); // escape single quote
93 if (Parameters != null && Parameters.Count > 0)
94 return BuildProcedureCall (String.Format ("sp_executesql N'{0}', N'{1}', ", esql, BuildPreparedParameters ()));
96 return BuildProcedureCall (String.Format ("sp_executesql N'{0}'", esql));
99 private string BuildParameters ()
101 if (Parameters == null || Parameters.Count == 0)
104 StringBuilder result = new StringBuilder ();
105 foreach (TdsMetaParameter p in Parameters) {
106 string parameterName = p.ParameterName;
107 if (parameterName [0] == '@') {
108 parameterName = parameterName.Substring (1);
110 if (p.Direction != TdsParameterDirection.ReturnValue) {
111 if (result.Length > 0)
112 result.Append (", ");
113 if (p.Direction == TdsParameterDirection.InputOutput)
114 result.AppendFormat ("@{0}={0} output", parameterName);
116 result.Append (FormatParameter (p));
119 return result.ToString ();
122 private string BuildPreparedParameters ()
124 StringBuilder parms = new StringBuilder ();
125 foreach (TdsMetaParameter p in Parameters) {
126 if (parms.Length > 0)
129 // Set default precision according to the TdsVersion
130 // Current default is 29 for Tds80
131 if (p.TypeName == "decimal")
132 p.Precision = (p.Precision !=0 ? p.Precision : (byte) Precision);
134 parms.Append (p.Prepare ());
135 if (p.Direction == TdsParameterDirection.Output)
136 parms.Append (" output");
138 return parms.ToString ();
141 private string BuildPreparedQuery (string id)
143 return BuildProcedureCall (String.Format ("sp_execute {0},", id));
146 private string BuildProcedureCall (string procedure)
148 string exec = String.Empty;
150 StringBuilder declare = new StringBuilder ();
151 StringBuilder select = new StringBuilder ();
152 StringBuilder set = new StringBuilder ();
155 if (Parameters != null) {
156 foreach (TdsMetaParameter p in Parameters) {
157 string parameterName = p.ParameterName;
158 if (parameterName [0] == '@') {
159 parameterName = parameterName.Substring (1);
162 if (p.Direction != TdsParameterDirection.Input) {
164 select.Append ("select ");
166 select.Append (", ");
167 select.Append ("@" + parameterName);
169 if (p.TypeName == "decimal")
170 p.Precision = (p.Precision !=0 ? p.Precision : (byte) Precision);
172 declare.Append (String.Format ("declare {0}\n", p.Prepare ()));
174 if (p.Direction != TdsParameterDirection.ReturnValue) {
175 if (p.Direction == TdsParameterDirection.InputOutput)
176 set.Append (String.Format ("set {0}\n", FormatParameter(p)));
178 set.Append (String.Format ("set @{0}=NULL\n", parameterName));
183 if (p.Direction == TdsParameterDirection.ReturnValue)
184 exec = "@" + parameterName + "=";
187 exec = "exec " + exec;
189 return String.Format ("{0}{1}{2}{3} {4}\n{5}",
190 declare.ToString (), set.ToString (), exec,
191 procedure, BuildParameters (), select.ToString ());
194 public override bool Connect (TdsConnectionParameters connectionParameters)
197 throw new InvalidOperationException ("The connection is already open.");
199 connectionParms = connectionParameters;
201 SetLanguage (connectionParameters.Language);
202 SetCharset ("utf-8");
204 byte[] empty = new byte[0];
208 byte[] domainMagic = { 6, 0x7d, 0x0f, 0xfd, 0xff, 0x0, 0x0, 0x0,
209 0x0, 0xe0, 0x83, 0x0, 0x0,
210 0x68, 0x01, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00 };
211 byte[] sqlserverMagic = { 6, 0x0, 0x0, 0x0,
213 0x0, 0xe0, 0x03, 0x0,
214 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
218 if (connectionParameters.DomainLogin)
221 magic = sqlserverMagic;
223 string username = connectionParameters.User;
224 string domain = null;
226 int idx = username.IndexOf ("\\");
228 domain = username.Substring (0, idx);
229 username = username.Substring (idx + 1);
231 connectionParameters.DefaultDomain = domain;
232 connectionParameters.User = username;
234 domain = Environment.UserDomainName;
235 connectionParameters.DefaultDomain = domain;
238 short partialPacketSize = (short) (86 + (
239 connectionParameters.Hostname.Length +
240 connectionParameters.ApplicationName.Length +
242 connectionParameters.LibraryName.Length +
244 connectionParameters.Database.Length +
245 connectionParameters.AttachDBFileName.Length) * 2);
247 if (connectionParameters.DomainLogin) {
248 authLen = ((short) (32 + (connectionParameters.Hostname.Length +
250 partialPacketSize += authLen;
252 partialPacketSize += ((short) ((username.Length + connectionParameters.Password.Length) * 2));
254 int totalPacketSize = partialPacketSize;
256 Comm.StartPacket (TdsPacketType.Logon70);
258 Comm.Append (totalPacketSize);
260 //Comm.Append (empty, 3, pad);
261 //byte[] version = {0x00, 0x0, 0x0, 0x71};
262 //Console.WriteLine ("Version: {0}", ClientVersion[3]);
263 Comm.Append (ClientVersion); // TDS Version 7
264 Comm.Append ((int)this.PacketSize); // Set the Block Size
265 Comm.Append (empty, 3, pad);
271 Comm.Append (curPos);
272 Comm.Append ((short) connectionParameters.Hostname.Length);
273 curPos += (short) (connectionParameters.Hostname.Length * 2);
275 if (connectionParameters.DomainLogin) {
276 Comm.Append((short)0);
277 Comm.Append((short)0);
278 Comm.Append((short)0);
279 Comm.Append((short)0);
282 Comm.Append (curPos);
283 Comm.Append ((short) username.Length);
284 curPos += ((short) (username.Length * 2));
287 Comm.Append (curPos);
288 Comm.Append ((short) connectionParameters.Password.Length);
289 curPos += (short) (connectionParameters.Password.Length * 2);
293 Comm.Append (curPos);
294 Comm.Append ((short) connectionParameters.ApplicationName.Length);
295 curPos += (short) (connectionParameters.ApplicationName.Length * 2);
298 Comm.Append (curPos);
299 Comm.Append ((short) DataSource.Length);
300 curPos += (short) (DataSource.Length * 2);
303 Comm.Append ((short) curPos);
304 Comm.Append ((short) 0);
307 Comm.Append (curPos);
308 Comm.Append ((short) connectionParameters.LibraryName.Length);
309 curPos += (short) (connectionParameters.LibraryName.Length * 2);
312 Comm.Append (curPos);
313 Comm.Append ((short) Language.Length);
314 curPos += (short) (Language.Length * 2);
317 Comm.Append (curPos);
318 Comm.Append ((short) connectionParameters.Database.Length);
319 curPos += (short) (connectionParameters.Database.Length * 2);
322 Comm.Append((byte) 0);
323 Comm.Append((byte) 0);
324 Comm.Append((byte) 0);
325 Comm.Append((byte) 0);
326 Comm.Append((byte) 0);
327 Comm.Append((byte) 0);
329 // Authentication Stuff
330 Comm.Append ((short) curPos);
331 if (connectionParameters.DomainLogin) {
332 Comm.Append ((short) authLen);
333 curPos += (short) authLen;
335 Comm.Append ((short) 0);
338 Comm.Append (curPos);
339 Comm.Append ((short)( connectionParameters.AttachDBFileName.Length));
340 curPos += (short)(connectionParameters.AttachDBFileName.Length*2);
342 // Connection Parameters
343 Comm.Append (connectionParameters.Hostname);
344 if (!connectionParameters.DomainLogin) {
345 // SQL Server Authentication
346 Comm.Append (connectionParameters.User);
347 string scrambledPwd = EncryptPassword (connectionParameters.Password);
348 Comm.Append (scrambledPwd);
350 Comm.Append (connectionParameters.ApplicationName);
351 Comm.Append (DataSource);
352 Comm.Append (connectionParameters.LibraryName);
353 Comm.Append (Language);
354 Comm.Append (connectionParameters.Database);
356 if (connectionParameters.DomainLogin) {
357 // the rest of the packet is NTLMSSP authentication
358 Type1Message msg = new Type1Message ();
360 msg.Host = connectionParameters.Hostname;
361 msg.Flags = NtlmFlags.NegotiateUnicode |
362 NtlmFlags.NegotiateNtlm |
363 NtlmFlags.NegotiateDomainSupplied |
364 NtlmFlags.NegotiateWorkstationSupplied |
365 NtlmFlags.NegotiateAlwaysSign; // 0xb201
366 Comm.Append (msg.GetBytes ());
369 Comm.Append (connectionParameters.AttachDBFileName);
377 private static string EncryptPassword (string pass)
379 int xormask = 0x5a5a;
380 int len = pass.Length;
381 char[] chars = new char[len];
383 for (int i = 0; i < len; ++i) {
384 int c = ((int) (pass[i])) ^ xormask;
385 int m1 = (c >> 4) & 0x0f0f;
386 int m2 = (c << 4) & 0xf0f0;
387 chars[i] = (char) (m1 | m2);
390 return new String (chars);
393 public override bool Reset ()
395 // Check validity of the connection - a false removes
396 // the connection from the pool
397 // NOTE: MS implementation will throw a connection-reset error as it will
398 // try to use the same connection
399 if (!Comm.IsConnected ())
402 // Set "reset-connection" bit for the next message packet
403 Comm.ResetConnection = true;
408 public override void ExecPrepared (string commandText, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
410 Parameters = parameters;
411 ExecuteQuery (BuildPreparedQuery (commandText), timeout, wantResults);
414 public override void ExecProc (string commandText, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
416 Parameters = parameters;
417 ExecRPC (commandText, parameters, timeout, wantResults);
420 private void WriteRpcParameterInfo (TdsMetaParameterCollection parameters)
422 if (parameters != null) {
423 foreach (TdsMetaParameter param in parameters) {
424 if (param.Direction == TdsParameterDirection.ReturnValue)
426 string pname = param.ParameterName;
427 if (pname != null && pname.Length > 0 && pname [0] == '@') {
428 Comm.Append ( (byte) pname.Length);
431 Comm.Append ( (byte) (pname.Length + 1));
432 Comm.Append ("@" + pname);
434 short status = 0; // unused
435 if (param.Direction != TdsParameterDirection.Input)
436 status |= 0x01; // output
437 Comm.Append ( (byte) status);
438 WriteParameterInfo (param);
443 private void WritePreparedParameterInfo (TdsMetaParameterCollection parameters)
445 if (parameters == null)
448 string param = BuildPreparedParameters ();
449 Comm.Append ((byte) 0x00); // no param meta data name
450 Comm.Append ((byte) 0x00); // no status flags
452 // Type_info - parameter info
453 WriteParameterInfo (new TdsMetaParameter ("prep_params",
454 param.Length > 4000 ? "ntext" : "nvarchar",
458 protected void ExecRPC (TdsRpcProcId rpcId, string sql,
459 TdsMetaParameterCollection parameters,
460 int timeout, bool wantResults)
464 Comm.StartPacket (TdsPacketType.RPC);
466 Comm.Append ((ushort) 0xFFFF);
467 Comm.Append ((ushort) rpcId);
468 Comm.Append ((short) 0x02); // no meta data
470 Comm.Append ((byte) 0x00); // no param meta data name
471 Comm.Append ((byte) 0x00); // no status flags
473 // Write sql as a parameter value - UCS2
474 TdsMetaParameter param = new TdsMetaParameter ("sql",
475 sql.Length > 4000 ? "ntext":"nvarchar",
477 WriteParameterInfo (param);
479 // Write Parameter infos - name and type
480 WritePreparedParameterInfo (parameters);
482 // Write parameter/value info
483 WriteRpcParameterInfo (parameters);
485 CheckForData (timeout);
490 protected override void ExecRPC (string rpcName, TdsMetaParameterCollection parameters,
491 int timeout, bool wantResults)
495 Comm.StartPacket (TdsPacketType.RPC);
497 Comm.Append ( (short) rpcName.Length);
498 Comm.Append (rpcName);
499 Comm.Append ( (short) 0); //no meta data
500 WriteRpcParameterInfo (parameters);
502 CheckForData (timeout);
507 private void WriteParameterInfo (TdsMetaParameter param)
510 Ms.net send non-nullable datatypes as nullable and allows setting null values
511 to int/float etc.. So, using Nullable form of type for all data
513 param.IsNullable = true;
514 TdsColumnType colType = param.GetMetaType ();
515 param.IsNullable = false;
517 bool partLenType = false;
518 int size = param.Size;
522 size = param.GetActualSize ();
525 // Change colType according to the following table
527 * Original Type Maxlen New Type
529 * NVarChar 4000 UCS2 NText
530 * BigVarChar 8000 ASCII Text
531 * BigVarBinary 8000 bytes Image
534 TdsColumnType origColType = colType;
535 if (colType == TdsColumnType.BigNVarChar) {
536 // param.GetActualSize() returns len*2
537 if (size == param.Size)
539 if ((size >> 1) > 4000)
540 colType = TdsColumnType.NText;
541 } else if (colType == TdsColumnType.BigVarChar) {
543 colType = TdsColumnType.Text;
544 } else if (colType == TdsColumnType.BigVarBinary) {
546 colType = TdsColumnType.Image;
548 // Calculation of TypeInfo field
550 * orig size value TypeInfo field
552 * >= 0 <= Maxlen origColType + content len
553 * > Maxlen NewType as per above table + content len
554 * -1 origColType + USHORTMAXLEN (0xFFFF) + content len (TDS 9)
557 // Write updated colType, iff partLenType == false
558 if (TdsVersion > TdsVersion.tds81 && partLenType) {
559 Comm.Append ((byte)origColType);
560 Comm.Append ((short)-1);
561 } else if (ServerTdsVersion > TdsVersion.tds70
562 && origColType == TdsColumnType.Decimal) {
563 Comm.Append ((byte)TdsColumnType.Numeric);
565 Comm.Append ((byte)colType);
568 if (IsLargeType (colType))
569 Comm.Append ((short)size); // Parameter size passed in SqlParameter
570 else if (IsBlobType (colType))
571 Comm.Append (size); // Parameter size passed in SqlParameter
573 Comm.Append ((byte)size);
575 // Precision and Scale are non-zero for only decimal/numeric
576 if ( param.TypeName == "decimal" || param.TypeName == "numeric") {
577 Comm.Append ((param.Precision !=0 ) ? param.Precision : Precision);
578 Comm.Append (param.Scale);
579 // Convert the decimal value according to Scale
580 if (param.Value != null && param.Value != DBNull.Value &&
581 ((decimal)param.Value) != Decimal.MaxValue &&
582 ((decimal)param.Value) != Decimal.MinValue &&
583 ((decimal)param.Value) != long.MaxValue &&
584 ((decimal)param.Value) != long.MinValue &&
585 ((decimal)param.Value) != ulong.MaxValue &&
586 ((decimal)param.Value) != ulong.MinValue) {
587 long expo = (long)new Decimal (System.Math.Pow (10, (double)param.Scale));
588 long pVal = (long)(((decimal)param.Value) * expo);
594 /* VARADHAN: TDS 8 Debugging */
596 if (Collation != null) {
597 Console.WriteLine ("Collation is not null");
598 Console.WriteLine ("Column Type: {0}", colType);
599 Console.WriteLine ("Collation bytes: {0} {1} {2} {3} {4}", Collation[0], Collation[1], Collation[2],
600 Collation[3], Collation[4]);
602 Console.WriteLine ("Collation is null");
606 // Tds > 7.0 uses collation
607 if (Collation != null &&
608 (colType == TdsColumnType.BigChar || colType == TdsColumnType.BigNVarChar ||
609 colType == TdsColumnType.BigVarChar || colType == TdsColumnType.NChar ||
610 colType == TdsColumnType.NVarChar || colType == TdsColumnType.Text ||
611 colType == TdsColumnType.NText))
612 Comm.Append (Collation);
614 // LAMESPEC: size should be 0xFFFF for any bigvarchar, bignvarchar and bigvarbinary
615 // types if param value is NULL
616 if ((colType == TdsColumnType.BigVarChar ||
617 colType == TdsColumnType.BigNVarChar ||
618 colType == TdsColumnType.BigVarBinary ||
619 colType == TdsColumnType.Image) &&
620 (param.Value == null || param.Value == DBNull.Value))
623 size = param.GetActualSize ();
625 if (IsLargeType (colType))
626 Comm.Append ((short)size);
627 else if (IsBlobType (colType))
630 Comm.Append ((byte)size);
633 switch (param.TypeName) {
635 // 4 == SqlMoney::MoneyFormat.NumberDecimalDigits
636 Decimal val = Decimal.Round ((decimal) param.Value, 4);
637 int[] arr = Decimal.GetBits (val);
640 Comm.Append (arr[1]);
641 Comm.Append (arr[0]);
643 Comm.Append (~arr[1]);
644 Comm.Append (~arr[0] + 1);
649 // 4 == SqlMoney::MoneyFormat.NumberDecimalDigits
650 Decimal val = Decimal.Round ((decimal) param.Value, 4);
651 if (val < SMALLMONEY_MIN || val > SMALLMONEY_MAX)
652 throw new OverflowException (string.Format (
653 CultureInfo.InvariantCulture,
654 "Value '{0}' is not valid for SmallMoney."
655 + " Must be between {1:N4} and {2:N4}.",
659 val.ToString (CultureInfo.CurrentCulture),
661 SMALLMONEY_MIN, SMALLMONEY_MAX));
663 int[] arr = Decimal.GetBits (val);
664 int sign = (val>0 ? 1: -1);
665 Comm.Append (sign * arr[0]);
669 Comm.Append ((DateTime)param.Value, 8);
671 case "smalldatetime":
672 Comm.Append ((DateTime)param.Value, 4);
680 byte [] tmp = param.GetBytes ();
683 case "uniqueidentifier" :
684 Comm.Append (((Guid)param.Value).ToByteArray());
687 Comm.Append (param.Value);
694 public override void Execute (string commandText, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
696 Parameters = parameters;
697 string sql = commandText;
698 if (wantResults || (Parameters != null && Parameters.Count > 0))
699 sql = BuildExec (commandText);
700 ExecuteQuery (sql, timeout, wantResults);
703 private string FormatParameter (TdsMetaParameter parameter)
705 string parameterName = parameter.ParameterName;
706 if (parameterName [0] == '@') {
707 parameterName = parameterName.Substring (1);
709 if (parameter.Direction == TdsParameterDirection.Output)
710 return String.Format ("@{0}=@{0} output", parameterName);
711 if (parameter.Value == null || parameter.Value == DBNull.Value)
712 return String.Format ("@{0}=NULL", parameterName);
715 switch (parameter.TypeName) {
716 case "smalldatetime":
718 DateTime d = Convert.ToDateTime (parameter.Value);
719 value = String.Format (base.Locale,
720 "'{0:MMM dd yyyy hh:mm:ss.fff tt}'", d);
731 object paramValue = parameter.Value;
732 Type paramType = paramValue.GetType ();
733 if (paramType.IsEnum)
734 paramValue = Convert.ChangeType (paramValue,
735 Type.GetTypeCode (paramType));
736 value = paramValue.ToString ();
740 value = String.Format ("N'{0}'", parameter.Value.ToString ().Replace ("'", "''"));
742 case "uniqueidentifier":
743 value = String.Format ("'{0}'", ((Guid) parameter.Value).ToString (string.Empty));
746 if (parameter.Value.GetType () == typeof (bool))
747 value = (((bool) parameter.Value) ? "0x1" : "0x0");
749 value = parameter.Value.ToString ();
754 byte[] byteArray = (byte[]) parameter.Value;
755 // In 1.0 profile, BitConverter.ToString() throws ArgumentOutOfRangeException when passed a 0-length
756 // array, so handle that as a special case.
757 if (byteArray.Length == 0)
760 value = String.Format ("0x{0}", BitConverter.ToString (byteArray).Replace ("-", string.Empty).ToLower ());
763 value = String.Format ("'{0}'", parameter.Value.ToString ().Replace ("'", "''"));
767 return "@" + parameterName + "=" + value;
770 public override string Prepare (string commandText, TdsMetaParameterCollection parameters)
772 Parameters = parameters;
774 TdsMetaParameterCollection parms = new TdsMetaParameterCollection ();
775 TdsMetaParameter parm = new TdsMetaParameter ("@Handle", "int", null);
776 parm.Direction = TdsParameterDirection.Output;
779 parms.Add (new TdsMetaParameter ("@VarDecl", "nvarchar", BuildPreparedParameters ()));
780 parms.Add (new TdsMetaParameter ("@Query", "nvarchar", commandText));
782 ExecProc ("sp_prepare", parms, 0, true);
784 return OutputParameters[0].ToString () ;
785 //if (ColumnValues == null || ColumnValues [0] == null || ColumnValues [0] == DBNull.Value)
786 // throw new TdsInternalException ();
787 //return string.Empty;
788 //return ColumnValues [0].ToString ();
791 protected override void ProcessColumnInfo ()
793 int numColumns = Comm.GetTdsShort ();
794 for (int i = 0; i < numColumns; i += 1) {
795 byte[] flagData = new byte[4];
796 for (int j = 0; j < 4; j += 1)
797 flagData[j] = Comm.GetByte ();
799 bool nullable = (flagData[2] & 0x01) > 0;
800 //bool caseSensitive = (flagData[2] & 0x02) > 0;
801 bool writable = (flagData[2] & 0x0c) > 0;
802 bool autoIncrement = (flagData[2] & 0x10) > 0;
803 bool isIdentity = (flagData[2] & 0x10) > 0;
805 TdsColumnType columnType = (TdsColumnType) ((Comm.GetByte () & 0xff));
807 byte xColumnType = 0;
808 if (IsLargeType (columnType)) {
809 xColumnType = (byte) columnType;
810 if (columnType != TdsColumnType.NChar)
815 string tableName = null;
817 if (IsBlobType (columnType)) {
818 columnSize = Comm.GetTdsInt ();
819 tableName = Comm.GetString (Comm.GetTdsShort ());
820 } else if (IsFixedSizeColumn (columnType)) {
821 columnSize = LookupBufferSize (columnType);
822 } else if (IsLargeType ((TdsColumnType) xColumnType)) {
823 columnSize = Comm.GetTdsShort ();
825 columnSize = Comm.GetByte () & 0xff;
828 if (IsWideType ((TdsColumnType) columnType))
834 if (columnType == TdsColumnType.Decimal || columnType == TdsColumnType.Numeric) {
835 precision = Comm.GetByte ();
836 scale = Comm.GetByte ();
838 precision = GetPrecision (columnType, columnSize);
839 scale = GetScale (columnType, columnSize);
842 string columnName = Comm.GetString (Comm.GetByte ());
844 TdsDataColumn col = new TdsDataColumn ();
847 col.ColumnType = columnType;
848 col.ColumnName = columnName;
849 col.IsAutoIncrement = autoIncrement;
850 col.IsIdentity = isIdentity;
851 col.ColumnSize = columnSize;
852 col.NumericPrecision = precision;
853 col.NumericScale = scale;
854 col.IsReadOnly = !writable;
855 col.AllowDBNull = nullable;
856 col.BaseTableName = tableName;
857 col.DataTypeName = Enum.GetName (typeof (TdsColumnType), xColumnType);
859 col ["ColumnType"] = columnType;
860 col ["ColumnName"] = columnName;
861 col ["IsAutoIncrement"] = autoIncrement;
862 col ["IsIdentity"] = isIdentity;
863 col ["ColumnSize"] = columnSize;
864 col ["NumericPrecision"] = precision;
865 col ["NumericScale"] = scale;
866 col ["IsReadOnly"] = !writable;
867 col ["AllowDBNull"] = nullable;
868 col ["BaseTableName"] = tableName;
869 col ["DataTypeName"] = Enum.GetName (typeof (TdsColumnType), xColumnType);
874 public override void Unprepare (string statementId)
876 TdsMetaParameterCollection parms = new TdsMetaParameterCollection ();
877 parms.Add (new TdsMetaParameter ("@P1", "int", Int32.Parse (statementId)));
878 ExecProc ("sp_unprepare", parms, 0, false);
881 protected override bool IsValidRowCount (byte status, byte op)
883 if ((status & (byte)0x10) == 0 || op == (byte)0xc1)
888 protected override void ProcessReturnStatus ()
890 int result = Comm.GetTdsInt ();
891 if (Parameters != null) {
892 foreach (TdsMetaParameter param in Parameters) {
893 if (param.Direction == TdsParameterDirection.ReturnValue) {
894 param.Value = result;
901 byte GetScale (TdsColumnType type, int columnSize)
904 case TdsColumnType.DateTime:
906 case TdsColumnType.DateTime4:
908 case TdsColumnType.DateTimeN:
909 switch (columnSize) {
920 throw new NotSupportedException (string.Format (
921 CultureInfo.InvariantCulture,
922 "Fixed scale not defined for column " +
923 "type '{0}' with size {1}.", type, columnSize));
926 byte GetPrecision (TdsColumnType type, int columnSize)
929 case TdsColumnType.Binary:
931 case TdsColumnType.Bit:
933 case TdsColumnType.Char:
935 case TdsColumnType.DateTime:
937 case TdsColumnType.DateTime4:
939 case TdsColumnType.DateTimeN:
940 switch (columnSize) {
947 case TdsColumnType.Real:
949 case TdsColumnType.Float8:
951 case TdsColumnType.FloatN:
952 switch (columnSize) {
959 case TdsColumnType.Image:
961 case TdsColumnType.Int1:
963 case TdsColumnType.Int2:
965 case TdsColumnType.Int4:
967 case TdsColumnType.IntN:
968 switch (columnSize) {
977 case TdsColumnType.Void:
979 case TdsColumnType.Text:
981 case TdsColumnType.UniqueIdentifier:
983 case TdsColumnType.VarBinary:
985 case TdsColumnType.VarChar:
987 case TdsColumnType.Money:
989 case TdsColumnType.NText:
991 case TdsColumnType.NVarChar:
993 case TdsColumnType.BitN:
995 case TdsColumnType.MoneyN:
996 switch (columnSize) {
1003 case TdsColumnType.Money4:
1005 case TdsColumnType.NChar:
1007 case TdsColumnType.BigBinary:
1009 case TdsColumnType.BigVarBinary:
1011 case TdsColumnType.BigVarChar:
1013 case TdsColumnType.BigNVarChar:
1015 case TdsColumnType.BigChar:
1017 case TdsColumnType.SmallMoney:
1019 case TdsColumnType.Variant:
1021 case TdsColumnType.BigInt:
1025 throw new NotSupportedException (string.Format (
1026 CultureInfo.InvariantCulture,
1027 "Fixed precision not defined for column " +
1028 "type '{0}' with size {1}.", type, columnSize));
1031 #endregion // Methods
1034 #region Asynchronous Methods
1036 public override IAsyncResult BeginExecuteNonQuery (string cmdText,
1037 TdsMetaParameterCollection parameters,
1038 AsyncCallback callback,
1041 Parameters = parameters;
1042 string sql = cmdText;
1043 if (Parameters != null && Parameters.Count > 0)
1044 sql = BuildExec (cmdText);
1046 IAsyncResult ar = BeginExecuteQueryInternal (sql, false, callback, state);
1050 public override void EndExecuteNonQuery (IAsyncResult ar)
1052 EndExecuteQueryInternal (ar);
1055 public override IAsyncResult BeginExecuteQuery (string cmdText,
1056 TdsMetaParameterCollection parameters,
1057 AsyncCallback callback,
1060 Parameters = parameters;
1061 string sql = cmdText;
1062 if (Parameters != null && Parameters.Count > 0)
1063 sql = BuildExec (cmdText);
1065 IAsyncResult ar = BeginExecuteQueryInternal (sql, true, callback, state);
1069 public override void EndExecuteQuery (IAsyncResult ar)
1071 EndExecuteQueryInternal (ar);
1074 public override IAsyncResult BeginExecuteProcedure (string prolog,
1078 TdsMetaParameterCollection parameters,
1079 AsyncCallback callback,
1082 Parameters = parameters;
1083 string pcall = BuildProcedureCall (cmdText);
1084 string sql = String.Format ("{0};{1};{2};", prolog, pcall, epilog);
1086 IAsyncResult ar = BeginExecuteQueryInternal (sql, !IsNonQuery, callback, state);
1090 public override void EndExecuteProcedure (IAsyncResult ar)
1092 EndExecuteQueryInternal (ar);
1095 #endregion // Asynchronous Methods