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};}
79 #endregion // Properties
83 private string BuildExec (string sql)
85 string esql = sql.Replace ("'", "''"); // escape single quote
86 if (Parameters != null && Parameters.Count > 0)
87 return BuildProcedureCall (String.Format ("sp_executesql N'{0}', N'{1}', ", esql, BuildPreparedParameters ()));
89 return BuildProcedureCall (String.Format ("sp_executesql N'{0}'", esql));
92 private string BuildParameters ()
94 if (Parameters == null || Parameters.Count == 0)
97 StringBuilder result = new StringBuilder ();
98 foreach (TdsMetaParameter p in Parameters) {
99 string parameterName = p.ParameterName;
100 if (parameterName [0] == '@') {
101 parameterName = parameterName.Substring (1);
103 if (p.Direction != TdsParameterDirection.ReturnValue) {
104 if (result.Length > 0)
105 result.Append (", ");
106 if (p.Direction == TdsParameterDirection.InputOutput)
107 result.AppendFormat ("@{0}={0} output", parameterName);
109 result.Append (FormatParameter (p));
112 return result.ToString ();
115 private string BuildPreparedParameters ()
117 StringBuilder parms = new StringBuilder ();
118 foreach (TdsMetaParameter p in Parameters) {
119 if (parms.Length > 0)
121 parms.Append (p.Prepare ());
122 if (p.Direction == TdsParameterDirection.Output)
123 parms.Append (" output");
125 return parms.ToString ();
128 private string BuildPreparedQuery (string id)
130 return BuildProcedureCall (String.Format ("sp_execute {0},", id));
133 private string BuildProcedureCall (string procedure)
135 string exec = String.Empty;
137 StringBuilder declare = new StringBuilder ();
138 StringBuilder select = new StringBuilder ();
139 StringBuilder set = new StringBuilder ();
142 if (Parameters != null) {
143 foreach (TdsMetaParameter p in Parameters) {
144 string parameterName = p.ParameterName;
145 if (parameterName [0] == '@') {
146 parameterName = parameterName.Substring (1);
149 if (p.Direction != TdsParameterDirection.Input) {
151 select.Append ("select ");
153 select.Append (", ");
154 select.Append ("@" + parameterName);
156 declare.Append (String.Format ("declare {0}\n", p.Prepare ()));
158 if (p.Direction != TdsParameterDirection.ReturnValue) {
159 if (p.Direction == TdsParameterDirection.InputOutput)
160 set.Append (String.Format ("set {0}\n", FormatParameter(p)));
162 set.Append (String.Format ("set @{0}=NULL\n", parameterName));
167 if (p.Direction == TdsParameterDirection.ReturnValue)
168 exec = "@" + parameterName + "=";
171 exec = "exec " + exec;
173 return String.Format ("{0}{1}{2}{3} {4}\n{5}",
174 declare.ToString (), set.ToString (), exec,
175 procedure, BuildParameters (), select.ToString ());
178 public override bool Connect (TdsConnectionParameters connectionParameters)
181 throw new InvalidOperationException ("The connection is already open.");
183 connectionParms = connectionParameters;
185 SetLanguage (connectionParameters.Language);
186 SetCharset ("utf-8");
188 byte[] empty = new byte[0];
192 byte[] domainMagic = { 6, 0x7d, 0x0f, 0xfd, 0xff, 0x0, 0x0, 0x0,
193 0x0, 0xe0, 0x83, 0x0, 0x0,
194 0x68, 0x01, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00 };
195 byte[] sqlserverMagic = { 6, 0x0, 0x0, 0x0,
197 0x0, 0xe0, 0x03, 0x0,
198 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
202 if (connectionParameters.DomainLogin)
205 magic = sqlserverMagic;
207 string username = connectionParameters.User;
208 string domain = null;
210 int idx = username.IndexOf ("\\");
212 domain = username.Substring (0, idx);
213 username = username.Substring (idx + 1);
215 connectionParameters.DefaultDomain = domain;
216 connectionParameters.User = username;
218 domain = Environment.UserDomainName;
219 connectionParameters.DefaultDomain = domain;
222 short partialPacketSize = (short) (86 + (
223 connectionParameters.Hostname.Length +
224 connectionParameters.ApplicationName.Length +
226 connectionParameters.LibraryName.Length +
228 connectionParameters.Database.Length +
229 connectionParameters.AttachDBFileName.Length) * 2);
231 if (connectionParameters.DomainLogin) {
232 authLen = ((short) (32 + (connectionParameters.Hostname.Length +
234 partialPacketSize += authLen;
236 partialPacketSize += ((short) ((username.Length + connectionParameters.Password.Length) * 2));
238 int totalPacketSize = partialPacketSize;
240 Comm.StartPacket (TdsPacketType.Logon70);
242 Comm.Append (totalPacketSize);
244 //Comm.Append (empty, 3, pad);
245 //byte[] version = {0x00, 0x0, 0x0, 0x71};
246 //Console.WriteLine ("Version: {0}", ClientVersion[3]);
247 Comm.Append (ClientVersion); // TDS Version 7
248 Comm.Append ((int)this.PacketSize); // Set the Block Size
249 Comm.Append (empty, 3, pad);
255 Comm.Append (curPos);
256 Comm.Append ((short) connectionParameters.Hostname.Length);
257 curPos += (short) (connectionParameters.Hostname.Length * 2);
259 if (connectionParameters.DomainLogin) {
260 Comm.Append((short)0);
261 Comm.Append((short)0);
262 Comm.Append((short)0);
263 Comm.Append((short)0);
266 Comm.Append (curPos);
267 Comm.Append ((short) username.Length);
268 curPos += ((short) (username.Length * 2));
271 Comm.Append (curPos);
272 Comm.Append ((short) connectionParameters.Password.Length);
273 curPos += (short) (connectionParameters.Password.Length * 2);
277 Comm.Append (curPos);
278 Comm.Append ((short) connectionParameters.ApplicationName.Length);
279 curPos += (short) (connectionParameters.ApplicationName.Length * 2);
282 Comm.Append (curPos);
283 Comm.Append ((short) DataSource.Length);
284 curPos += (short) (DataSource.Length * 2);
287 Comm.Append ((short) curPos);
288 Comm.Append ((short) 0);
291 Comm.Append (curPos);
292 Comm.Append ((short) connectionParameters.LibraryName.Length);
293 curPos += (short) (connectionParameters.LibraryName.Length * 2);
296 Comm.Append (curPos);
297 Comm.Append ((short) Language.Length);
298 curPos += (short) (Language.Length * 2);
301 Comm.Append (curPos);
302 Comm.Append ((short) connectionParameters.Database.Length);
303 curPos += (short) (connectionParameters.Database.Length * 2);
306 Comm.Append((byte) 0);
307 Comm.Append((byte) 0);
308 Comm.Append((byte) 0);
309 Comm.Append((byte) 0);
310 Comm.Append((byte) 0);
311 Comm.Append((byte) 0);
313 // Authentication Stuff
314 Comm.Append ((short) curPos);
315 if (connectionParameters.DomainLogin) {
316 Comm.Append ((short) authLen);
317 curPos += (short) authLen;
319 Comm.Append ((short) 0);
322 Comm.Append (curPos);
323 Comm.Append ((short)( connectionParameters.AttachDBFileName.Length));
324 curPos += (short)(connectionParameters.AttachDBFileName.Length*2);
326 // Connection Parameters
327 Comm.Append (connectionParameters.Hostname);
328 if (!connectionParameters.DomainLogin) {
329 // SQL Server Authentication
330 Comm.Append (connectionParameters.User);
331 string scrambledPwd = EncryptPassword (connectionParameters.Password);
332 Comm.Append (scrambledPwd);
334 Comm.Append (connectionParameters.ApplicationName);
335 Comm.Append (DataSource);
336 Comm.Append (connectionParameters.LibraryName);
337 Comm.Append (Language);
338 Comm.Append (connectionParameters.Database);
340 if (connectionParameters.DomainLogin) {
341 // the rest of the packet is NTLMSSP authentication
342 Type1Message msg = new Type1Message ();
344 msg.Host = connectionParameters.Hostname;
345 msg.Flags = NtlmFlags.NegotiateUnicode |
346 NtlmFlags.NegotiateNtlm |
347 NtlmFlags.NegotiateDomainSupplied |
348 NtlmFlags.NegotiateWorkstationSupplied |
349 NtlmFlags.NegotiateAlwaysSign; // 0xb201
350 Comm.Append (msg.GetBytes ());
353 Comm.Append (connectionParameters.AttachDBFileName);
361 private static string EncryptPassword (string pass)
363 int xormask = 0x5a5a;
364 int len = pass.Length;
365 char[] chars = new char[len];
367 for (int i = 0; i < len; ++i) {
368 int c = ((int) (pass[i])) ^ xormask;
369 int m1 = (c >> 4) & 0x0f0f;
370 int m2 = (c << 4) & 0xf0f0;
371 chars[i] = (char) (m1 | m2);
374 return new String (chars);
377 public override bool Reset ()
379 // Check validity of the connection - a false removes
380 // the connection from the pool
381 // NOTE: MS implementation will throw a connection-reset error as it will
382 // try to use the same connection
383 if (!Comm.IsConnected ())
386 // Set "reset-connection" bit for the next message packet
387 Comm.ResetConnection = true;
392 public override void ExecPrepared (string commandText, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
394 Parameters = parameters;
395 if (Parameters != null && Parameters.Count > 0)
396 ExecRPC (TdsRpcProcId.ExecuteSql, commandText, parameters, timeout, wantResults);
398 ExecuteQuery (BuildPreparedQuery (commandText), timeout, wantResults);
401 public override void ExecProc (string commandText, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
403 Parameters = parameters;
404 ExecRPC (commandText, parameters, timeout, wantResults);
407 private void WriteRpcParameterInfo (TdsMetaParameterCollection parameters)
409 if (parameters != null) {
410 foreach (TdsMetaParameter param in parameters) {
411 if (param.Direction == TdsParameterDirection.ReturnValue)
413 string pname = param.ParameterName;
414 if (pname != null && pname.Length > 0 && pname [0] == '@') {
415 Comm.Append ( (byte) pname.Length);
418 Comm.Append ( (byte) (pname.Length + 1));
419 Comm.Append ("@" + pname);
421 short status = 0; // unused
422 if (param.Direction != TdsParameterDirection.Input)
423 status |= 0x01; // output
424 Comm.Append ( (byte) status);
425 WriteParameterInfo (param);
430 private void WritePreparedParameterInfo (TdsMetaParameterCollection parameters)
432 if (parameters == null)
435 string param = BuildPreparedParameters ();
436 Comm.Append ((byte) 0x00); // no param meta data name
437 Comm.Append ((byte) 0x00); // no status flags
439 // Type_info - parameter info
440 WriteParameterInfo (new TdsMetaParameter ("prep_params",
441 param.Length > 4000 ? "ntext" : "nvarchar",
445 private void ExecRPC (TdsRpcProcId rpcId, string sql,
446 TdsMetaParameterCollection parameters,
447 int timeout, bool wantResults)
451 Comm.StartPacket (TdsPacketType.RPC);
453 Comm.Append ((ushort) 0xFFFF);
454 Comm.Append ((ushort) rpcId);
455 Comm.Append ((short) 0x02); // no meta data
457 Comm.Append ((byte) 0x00); // no param meta data name
458 Comm.Append ((byte) 0x00); // no status flags
460 // Write sql as a parameter value - UCS2
461 TdsMetaParameter param = new TdsMetaParameter ("sql",
462 sql.Length > 4000 ? "ntext":"nvarchar",
464 WriteParameterInfo (param);
466 // Write Parameter infos - name and type
467 WritePreparedParameterInfo (parameters);
469 // Write parameter/value info
470 WriteRpcParameterInfo (parameters);
472 CheckForData (timeout);
477 protected override void ExecRPC (string rpcName, TdsMetaParameterCollection parameters,
478 int timeout, bool wantResults)
482 Comm.StartPacket (TdsPacketType.RPC);
484 Comm.Append ( (short) rpcName.Length);
485 Comm.Append (rpcName);
486 Comm.Append ( (short) 0); //no meta data
487 WriteRpcParameterInfo (parameters);
489 CheckForData (timeout);
494 private void WriteParameterInfo (TdsMetaParameter param)
497 Ms.net send non-nullable datatypes as nullable and allows setting null values
498 to int/float etc.. So, using Nullable form of type for all data
500 param.IsNullable = true;
501 TdsColumnType colType = param.GetMetaType ();
502 param.IsNullable = false;
504 bool partLenType = false;
505 int size = param.Size;
509 size = param.GetActualSize ();
512 // Change colType according to the following table
514 * Original Type Maxlen New Type
516 * NVarChar 4000 UCS2 NText
517 * BigVarChar 8000 ASCII Text
518 * BigVarBinary 8000 bytes Image
521 TdsColumnType origColType = colType;
522 if (colType == TdsColumnType.BigNVarChar) {
523 // param.GetActualSize() returns len*2
524 if (size == param.Size)
526 if ((size >> 1) > 4000)
527 colType = TdsColumnType.NText;
528 } else if (colType == TdsColumnType.BigVarChar) {
530 colType = TdsColumnType.Text;
531 } else if (colType == TdsColumnType.BigVarBinary) {
533 colType = TdsColumnType.Image;
535 // Calculation of TypeInfo field
537 * orig size value TypeInfo field
539 * >= 0 <= Maxlen origColType + content len
540 * > Maxlen NewType as per above table + content len
541 * -1 origColType + USHORTMAXLEN (0xFFFF) + content len (TDS 9)
544 // Write updated colType, iff partLenType == false
545 if (TdsVersion > TdsVersion.tds81 && partLenType) {
546 Comm.Append ((byte)origColType);
547 Comm.Append ((short)-1);
548 } else if (ServerTdsVersion > TdsVersion.tds70
549 && origColType == TdsColumnType.Decimal) {
550 Comm.Append ((byte)TdsColumnType.Numeric);
552 Comm.Append ((byte)colType);
555 if (IsLargeType (colType))
556 Comm.Append ((short)size); // Parameter size passed in SqlParameter
557 else if (IsBlobType (colType))
558 Comm.Append (size); // Parameter size passed in SqlParameter
560 Comm.Append ((byte)size);
562 // Precision and Scale are non-zero for only decimal/numeric
563 if ( param.TypeName == "decimal" || param.TypeName == "numeric") {
564 Comm.Append ((param.Precision !=0 ) ? param.Precision : (byte) 29);
565 Comm.Append (param.Scale);
569 /* VARADHAN: TDS 8 Debugging */
571 if (Collation != null) {
572 Console.WriteLine ("Collation is not null");
573 Console.WriteLine ("Column Type: {0}", colType);
574 Console.WriteLine ("Collation bytes: {0} {1} {2} {3} {4}", Collation[0], Collation[1], Collation[2],
575 Collation[3], Collation[4]);
577 Console.WriteLine ("Collation is null");
581 // Tds > 7.0 uses collation
582 if (Collation != null &&
583 (colType == TdsColumnType.BigChar || colType == TdsColumnType.BigNVarChar ||
584 colType == TdsColumnType.BigVarChar || colType == TdsColumnType.NChar ||
585 colType == TdsColumnType.NVarChar || colType == TdsColumnType.Text ||
586 colType == TdsColumnType.NText))
587 Comm.Append (Collation);
589 // LAMESPEC: size should be 0xFFFF for any bigvarchar, bignvarchar and bigvarbinary
590 // types if param value is NULL
591 if ((colType == TdsColumnType.BigVarChar ||
592 colType == TdsColumnType.BigNVarChar ||
593 colType == TdsColumnType.BigVarBinary) &&
594 (param.Value == null || param.Value == DBNull.Value))
597 size = param.GetActualSize ();
599 if (IsLargeType (colType))
600 Comm.Append ((short)size);
601 else if (IsBlobType (colType))
604 Comm.Append ((byte)size);
607 switch (param.TypeName) {
609 Decimal val = (decimal) param.Value;
610 int[] arr = Decimal.GetBits (val);
613 Comm.Append (arr[1]);
614 Comm.Append (arr[0]);
616 Comm.Append (~arr[1]);
617 Comm.Append (~arr[0] + 1);
622 Decimal val = (decimal) param.Value;
623 if (val < SMALLMONEY_MIN || val > SMALLMONEY_MAX)
624 throw new OverflowException (string.Format (
625 CultureInfo.InvariantCulture,
626 "Value '{0}' is not valid for SmallMoney."
627 + " Must be between {1:N4} and {2:N4}.",
631 val.ToString (CultureInfo.CurrentCulture),
633 SMALLMONEY_MIN, SMALLMONEY_MAX));
635 int[] arr = Decimal.GetBits (val);
636 int sign = (val>0 ? 1: -1);
637 Comm.Append (sign * arr[0]);
641 Comm.Append ((DateTime)param.Value, 8);
643 case "smalldatetime":
644 Comm.Append ((DateTime)param.Value, 4);
652 byte [] tmp = param.GetBytes ();
655 case "uniqueidentifier" :
656 Comm.Append (((Guid)param.Value).ToByteArray());
659 Comm.Append (param.Value);
666 public override void Execute (string commandText, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
668 Parameters = parameters;
669 string sql = commandText;
671 if (Parameters != null && Parameters.Count > 0) {
672 ExecRPC (TdsRpcProcId.ExecuteSql, commandText, parameters, timeout, wantResults);
675 sql = BuildExec (commandText);
676 ExecuteQuery (sql, timeout, wantResults);
680 private string FormatParameter (TdsMetaParameter parameter)
682 string parameterName = parameter.ParameterName;
683 if (parameterName [0] == '@') {
684 parameterName = parameterName.Substring (1);
686 if (parameter.Direction == TdsParameterDirection.Output)
687 return String.Format ("@{0}=@{0} output", parameterName);
688 if (parameter.Value == null || parameter.Value == DBNull.Value)
689 return String.Format ("@{0}=NULL", parameterName);
692 switch (parameter.TypeName) {
693 case "smalldatetime":
695 DateTime d = Convert.ToDateTime (parameter.Value);
696 value = String.Format (base.Locale,
697 "'{0:MMM dd yyyy hh:mm:ss.fff tt}'", d);
708 object paramValue = parameter.Value;
709 Type paramType = paramValue.GetType ();
710 if (paramType.IsEnum)
711 paramValue = Convert.ChangeType (paramValue,
712 Type.GetTypeCode (paramType));
713 value = paramValue.ToString ();
717 value = String.Format ("N'{0}'", parameter.Value.ToString ().Replace ("'", "''"));
719 case "uniqueidentifier":
720 value = String.Format ("'{0}'", ((Guid) parameter.Value).ToString (string.Empty));
723 if (parameter.Value.GetType () == typeof (bool))
724 value = (((bool) parameter.Value) ? "0x1" : "0x0");
726 value = parameter.Value.ToString ();
731 byte[] byteArray = (byte[]) parameter.Value;
732 // In 1.0 profile, BitConverter.ToString() throws ArgumentOutOfRangeException when passed a 0-length
733 // array, so handle that as a special case.
734 if (byteArray.Length == 0)
737 value = String.Format ("0x{0}", BitConverter.ToString (byteArray).Replace ("-", string.Empty).ToLower ());
740 value = String.Format ("'{0}'", parameter.Value.ToString ().Replace ("'", "''"));
744 return "@" + parameterName + "=" + value;
747 public override string Prepare (string commandText, TdsMetaParameterCollection parameters)
749 Parameters = parameters;
751 TdsMetaParameterCollection parms = new TdsMetaParameterCollection ();
752 TdsMetaParameter parm = new TdsMetaParameter ("@Handle", "int", null);
753 parm.Direction = TdsParameterDirection.Output;
756 parms.Add (new TdsMetaParameter ("@VarDecl", "nvarchar", BuildPreparedParameters ()));
757 parms.Add (new TdsMetaParameter ("@Query", "nvarchar", commandText));
759 ExecProc ("sp_prepare", parms, 0, true);
761 return OutputParameters[0].ToString () ;
762 //if (ColumnValues == null || ColumnValues [0] == null || ColumnValues [0] == DBNull.Value)
763 // throw new TdsInternalException ();
764 //return string.Empty;
765 //return ColumnValues [0].ToString ();
768 protected override void ProcessColumnInfo ()
770 int numColumns = Comm.GetTdsShort ();
771 for (int i = 0; i < numColumns; i += 1) {
772 byte[] flagData = new byte[4];
773 for (int j = 0; j < 4; j += 1)
774 flagData[j] = Comm.GetByte ();
776 bool nullable = (flagData[2] & 0x01) > 0;
777 //bool caseSensitive = (flagData[2] & 0x02) > 0;
778 bool writable = (flagData[2] & 0x0c) > 0;
779 bool autoIncrement = (flagData[2] & 0x10) > 0;
780 bool isIdentity = (flagData[2] & 0x10) > 0;
782 TdsColumnType columnType = (TdsColumnType) ((Comm.GetByte () & 0xff));
784 byte xColumnType = 0;
785 if (IsLargeType (columnType)) {
786 xColumnType = (byte) columnType;
787 if (columnType != TdsColumnType.NChar)
792 string tableName = null;
794 if (IsBlobType (columnType)) {
795 columnSize = Comm.GetTdsInt ();
796 tableName = Comm.GetString (Comm.GetTdsShort ());
797 } else if (IsFixedSizeColumn (columnType)) {
798 columnSize = LookupBufferSize (columnType);
799 } else if (IsLargeType ((TdsColumnType) xColumnType)) {
800 columnSize = Comm.GetTdsShort ();
802 columnSize = Comm.GetByte () & 0xff;
805 if (IsWideType ((TdsColumnType) columnType))
811 if (columnType == TdsColumnType.Decimal || columnType == TdsColumnType.Numeric) {
812 precision = Comm.GetByte ();
813 scale = Comm.GetByte ();
815 precision = GetPrecision (columnType, columnSize);
816 scale = GetScale (columnType, columnSize);
819 string columnName = Comm.GetString (Comm.GetByte ());
821 TdsDataColumn col = new TdsDataColumn ();
824 col.ColumnType = columnType;
825 col.ColumnName = columnName;
826 col.IsAutoIncrement = autoIncrement;
827 col.IsIdentity = isIdentity;
828 col.ColumnSize = columnSize;
829 col.NumericPrecision = precision;
830 col.NumericScale = scale;
831 col.IsReadOnly = !writable;
832 col.AllowDBNull = nullable;
833 col.BaseTableName = tableName;
834 col.DataTypeName = Enum.GetName (typeof (TdsColumnType), xColumnType);
836 col ["ColumnType"] = columnType;
837 col ["ColumnName"] = columnName;
838 col ["IsAutoIncrement"] = autoIncrement;
839 col ["IsIdentity"] = isIdentity;
840 col ["ColumnSize"] = columnSize;
841 col ["NumericPrecision"] = precision;
842 col ["NumericScale"] = scale;
843 col ["IsReadOnly"] = !writable;
844 col ["AllowDBNull"] = nullable;
845 col ["BaseTableName"] = tableName;
846 col ["DataTypeName"] = Enum.GetName (typeof (TdsColumnType), xColumnType);
851 public override void Unprepare (string statementId)
853 TdsMetaParameterCollection parms = new TdsMetaParameterCollection ();
854 parms.Add (new TdsMetaParameter ("@P1", "int", Int32.Parse (statementId)));
855 ExecProc ("sp_unprepare", parms, 0, false);
858 protected override bool IsValidRowCount (byte status, byte op)
860 if ((status & (byte)0x10) == 0 || op == (byte)0xc1)
865 protected override void ProcessReturnStatus ()
867 int result = Comm.GetTdsInt ();
868 if (Parameters != null) {
869 foreach (TdsMetaParameter param in Parameters) {
870 if (param.Direction == TdsParameterDirection.ReturnValue) {
871 param.Value = result;
878 byte GetScale (TdsColumnType type, int columnSize)
881 case TdsColumnType.DateTime:
883 case TdsColumnType.DateTime4:
885 case TdsColumnType.DateTimeN:
886 switch (columnSize) {
897 throw new NotSupportedException (string.Format (
898 CultureInfo.InvariantCulture,
899 "Fixed scale not defined for column " +
900 "type '{0}' with size {1}.", type, columnSize));
903 byte GetPrecision (TdsColumnType type, int columnSize)
906 case TdsColumnType.Binary:
908 case TdsColumnType.Bit:
910 case TdsColumnType.Char:
912 case TdsColumnType.DateTime:
914 case TdsColumnType.DateTime4:
916 case TdsColumnType.DateTimeN:
917 switch (columnSize) {
924 case TdsColumnType.Real:
926 case TdsColumnType.Float8:
928 case TdsColumnType.FloatN:
929 switch (columnSize) {
936 case TdsColumnType.Image:
938 case TdsColumnType.Int1:
940 case TdsColumnType.Int2:
942 case TdsColumnType.Int4:
944 case TdsColumnType.IntN:
945 switch (columnSize) {
954 case TdsColumnType.Void:
956 case TdsColumnType.Text:
958 case TdsColumnType.UniqueIdentifier:
960 case TdsColumnType.VarBinary:
962 case TdsColumnType.VarChar:
964 case TdsColumnType.Money:
966 case TdsColumnType.NText:
968 case TdsColumnType.NVarChar:
970 case TdsColumnType.BitN:
972 case TdsColumnType.MoneyN:
973 switch (columnSize) {
980 case TdsColumnType.Money4:
982 case TdsColumnType.NChar:
984 case TdsColumnType.BigBinary:
986 case TdsColumnType.BigVarBinary:
988 case TdsColumnType.BigVarChar:
990 case TdsColumnType.BigNVarChar:
992 case TdsColumnType.BigChar:
994 case TdsColumnType.SmallMoney:
996 case TdsColumnType.Variant:
998 case TdsColumnType.BigInt:
1002 throw new NotSupportedException (string.Format (
1003 CultureInfo.InvariantCulture,
1004 "Fixed precision not defined for column " +
1005 "type '{0}' with size {1}.", type, columnSize));
1008 #endregion // Methods
1011 #region Asynchronous Methods
1013 public override IAsyncResult BeginExecuteNonQuery (string cmdText,
1014 TdsMetaParameterCollection parameters,
1015 AsyncCallback callback,
1018 Parameters = parameters;
1019 string sql = cmdText;
1020 if (Parameters != null && Parameters.Count > 0)
1021 sql = BuildExec (cmdText);
1023 IAsyncResult ar = BeginExecuteQueryInternal (sql, false, callback, state);
1027 public override void EndExecuteNonQuery (IAsyncResult ar)
1029 EndExecuteQueryInternal (ar);
1032 public override IAsyncResult BeginExecuteQuery (string cmdText,
1033 TdsMetaParameterCollection parameters,
1034 AsyncCallback callback,
1037 Parameters = parameters;
1038 string sql = cmdText;
1039 if (Parameters != null && Parameters.Count > 0)
1040 sql = BuildExec (cmdText);
1042 IAsyncResult ar = BeginExecuteQueryInternal (sql, true, callback, state);
1046 public override void EndExecuteQuery (IAsyncResult ar)
1048 EndExecuteQueryInternal (ar);
1051 public override IAsyncResult BeginExecuteProcedure (string prolog,
1055 TdsMetaParameterCollection parameters,
1056 AsyncCallback callback,
1059 Parameters = parameters;
1060 string pcall = BuildProcedureCall (cmdText);
1061 string sql = String.Format ("{0};{1};{2};", prolog, pcall, epilog);
1063 IAsyncResult ar = BeginExecuteQueryInternal (sql, !IsNonQuery, callback, state);
1067 public override void EndExecuteProcedure (IAsyncResult ar)
1069 EndExecuteQueryInternal (ar);
1072 #endregion // Asynchronous Methods