2 // Mono.Data.Tds.Protocol.Tds50.cs
5 // Tim Coleman (tim@timcoleman.com)
7 // Copyright (C) 2002 Tim Coleman
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System.Security;
36 namespace Mono.Data.Tds.Protocol
38 [MonoTODO ("FIXME: Can packetsize be anything other than 512?")]
39 public sealed class Tds50 : Tds
43 public static readonly TdsVersion Version = TdsVersion.tds50;
51 public Tds50 (string server, int port)
52 : this (server, port, 512, 15)
56 public Tds50 (string server, int port, int packetSize, int timeout)
57 : base (server, port, packetSize, timeout, Version)
59 this.packetSize = packetSize;
62 #endregion // Constructors
66 public string BuildExec (string sql)
68 if (Parameters == null || Parameters.Count == 0)
71 StringBuilder select = new StringBuilder ();
72 StringBuilder set = new StringBuilder ();
73 StringBuilder declare = new StringBuilder ();
75 foreach (TdsMetaParameter p in Parameters) {
76 declare.Append (String.Format ("declare {0}\n", p.Prepare ()));
77 set.Append (String.Format ("select {0}=", p.ParameterName));
78 if (p.Direction == TdsParameterDirection.Input)
79 set.Append (FormatParameter (p));
82 select.Append (p.ParameterName);
84 select.Append ("select ");
91 return String.Format ("{0}{1}{2}\n{3}", declare.ToString (), set.ToString (), sql, select.ToString ());
94 public override bool Connect (TdsConnectionParameters connectionParameters)
97 throw new InvalidOperationException ("The connection is already open.");
99 byte[] capabilityRequest = {0x03, 0xef, 0x65, 0x41, 0xff, 0xff, 0xff, 0xd6};
100 byte[] capabilityResponse = {0x00, 0x00, 0x00, 0x06, 0x48, 0x00, 0x00, 0x08};
102 SetCharset (connectionParameters.Charset);
103 SetLanguage (connectionParameters.Language);
106 byte[] empty = new byte[0];
108 Comm.StartPacket (TdsPacketType.Logon);
110 // hostname (offset 0)
112 byte[] tmp = Comm.Append (connectionParameters.Hostname, 30, pad);
113 Comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30));
115 // username (offset 31 0x1f)
117 tmp = Comm.Append (connectionParameters.User, 30, pad);
118 Comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30));
120 // password (offset 62 0x3e)
122 tmp = Comm.Append (GetPlainPassword(connectionParameters.Password), 30, pad);
123 Comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30));
125 // hostproc (offset 93 0x5d)
127 tmp = Comm.Append ("37876", 30, pad);
128 Comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30));
130 // Byte order of 2 byte ints
131 // 2 = <MSB, LSB>, 3 = <LSB, MSB>
133 Comm.Append ((byte) 3);
135 // Byte order of 4 byte ints
136 // 0 = <MSB, LSB>, 1 = <LSB, MSB>
138 Comm.Append ((byte) 1);
140 // Character representation
141 // (6 = ASCII, 7 = EBCDIC)
143 Comm.Append ((byte) 6);
145 // Eight byte floating point representation
146 // 4 = IEEE <MSB, ..., LSB>
148 // 10 = IEEE <LSB, ..., MSB>
151 Comm.Append ((byte) 10);
153 // Eight byte date format
154 // 8 = <MSB, ..., LSB>
156 Comm.Append ((byte) 9);
160 Comm.Append ((byte) 1);
162 // disallow dump/load and bulk insert
164 Comm.Append ((byte) 1);
166 // sql interface type
168 Comm.Append ((byte) 0);
170 // type of network connection
172 Comm.Append ((byte) 0);
176 Comm.Append (empty, 7, pad);
180 tmp = Comm.Append (connectionParameters.ApplicationName, 30, pad);
181 Comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30));
185 tmp = Comm.Append (DataSource, 30, pad);
186 Comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30));
190 Comm.Append (empty, 2, pad);
191 tmp = Comm.Append (GetPlainPassword(connectionParameters.Password), 253, pad);
192 Comm.Append ((byte) (tmp.Length < 253 ? tmp.Length + 2 : 253 + 2));
196 Comm.Append ((byte) 5);
197 Comm.Append ((byte) 0);
198 Comm.Append ((byte) 0);
199 Comm.Append ((byte) 0);
203 tmp = Comm.Append (connectionParameters.ProgName, 10, pad);
204 Comm.Append ((byte) (tmp.Length < 10 ? tmp.Length : 10));
208 Comm.Append ((byte) 6);
209 Comm.Append ((byte) 0);
210 Comm.Append ((byte) 0);
211 Comm.Append ((byte) 0);
213 // auto convert short
215 Comm.Append ((byte) 0);
219 Comm.Append ((byte) 0x0d);
223 Comm.Append ((byte) 0x11);
227 tmp = Comm.Append (Language, 30, pad);
228 Comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30));
230 // notify on lang change
232 Comm.Append ((byte) 1);
234 // security label hierarchy
236 Comm.Append ((short) 0);
238 // security components
240 Comm.Append (empty, 8, pad);
244 Comm.Append ((short) 0);
246 // security login role
248 Comm.Append ((byte) 0);
252 tmp = Comm.Append (Charset, 30, pad);
253 Comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30));
255 // notify on charset change
257 Comm.Append ((byte) 1);
259 // length of tds packets
261 tmp = Comm.Append (this.packetSize.ToString (), 6, pad);
262 Comm.Append ((byte) (tmp.Length < 6 ? tmp.Length : 6));
264 Comm.Append (empty, 8, pad);
267 //Comm.Append (empty, 4, pad);
270 Comm.Append ((byte) TdsPacketSubType.Capability);
271 Comm.Append ((short) 20);
272 Comm.Append ((byte) 0x01); // TDS_CAP_REQUEST
273 Comm.Append (capabilityRequest);
274 Comm.Append ((byte) 0x02);
275 Comm.Append (capabilityResponse);
285 public override void ExecPrepared (string id, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
287 Parameters = parameters;
288 bool hasParameters = (Parameters != null && Parameters.Count > 0);
290 Comm.StartPacket (TdsPacketType.Normal);
292 Comm.Append ((byte) TdsPacketSubType.Dynamic);
293 Comm.Append ((short) (id.Length + 5));
294 Comm.Append ((byte) 0x02); // TDS_DYN_EXEC
295 Comm.Append ((byte) (hasParameters ? 0x01 : 0x00));
296 Comm.Append ((byte) id.Length);
298 Comm.Append ((short) 0);
307 CheckForData (timeout);
312 public override void Execute (string sql, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
314 Parameters = parameters;
315 string ex = BuildExec (sql);
316 ExecuteQuery (ex, timeout, wantResults);
319 public override void ExecProc (string commandText, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
321 Parameters = parameters;
322 ExecuteQuery (BuildProcedureCall (commandText), timeout, wantResults);
325 private string BuildProcedureCall (string procedure)
327 string exec = String.Empty;
329 StringBuilder declare = new StringBuilder ();
330 StringBuilder select = new StringBuilder ();
331 StringBuilder set = new StringBuilder ();
334 if (Parameters != null) {
335 foreach (TdsMetaParameter p in Parameters) {
336 if (p.Direction != TdsParameterDirection.Input) {
339 select.Append ("select ");
341 select.Append (", ");
342 select.Append (p.ParameterName);
344 declare.Append (String.Format ("declare {0}\n", p.Prepare ()));
346 if (p.Direction != TdsParameterDirection.ReturnValue) {
347 if( p.Direction == TdsParameterDirection.InputOutput )
348 set.Append (String.Format ("set {0}\n", FormatParameter(p)));
350 set.Append (String.Format ("set {0}=NULL\n", p.ParameterName));
356 if (p.Direction == TdsParameterDirection.ReturnValue)
357 exec = p.ParameterName + "=";
360 exec = "exec " + exec;
362 string sql = String.Format ("{0}{1}{2}{3} {4}\n{5}", declare.ToString (),
365 BuildParameters (), select.ToString ());
369 private string BuildParameters ()
371 if (Parameters == null || Parameters.Count == 0)
374 StringBuilder result = new StringBuilder ();
375 foreach (TdsMetaParameter p in Parameters) {
376 if (p.Direction != TdsParameterDirection.ReturnValue) {
377 if (result.Length > 0)
378 result.Append (", ");
379 if (p.Direction == TdsParameterDirection.InputOutput)
380 result.Append (String.Format("{0}={0} output", p.ParameterName));
382 result.Append (FormatParameter (p));
385 return result.ToString ();
389 private string FormatParameter (TdsMetaParameter parameter)
391 if (parameter.Direction == TdsParameterDirection.Output)
392 return String.Format ("{0} output", parameter.ParameterName);
394 if (parameter.Value == null || parameter.Value == DBNull.Value)
397 switch (parameter.TypeName) {
398 case "smalldatetime":
400 DateTime d = (DateTime)parameter.Value;
401 return String.Format(System.Globalization.CultureInfo.InvariantCulture,
402 "'{0:MMM dd yyyy hh:mm:ss tt}'", d );
412 return parameter.Value.ToString ();
415 return String.Format ("N'{0}'", parameter.Value.ToString ().Replace ("'", "''"));
416 case "uniqueidentifier":
417 return String.Format ("0x{0}", ((Guid) parameter.Value).ToString ("N"));
419 if (parameter.Value.GetType () == typeof (bool))
420 return (((bool) parameter.Value) ? "0x1" : "0x0");
421 return parameter.Value.ToString ();
425 return String.Format ("0x{0}", BitConverter.ToString ((byte[]) parameter.Value).Replace ("-", string.Empty).ToLower ());
427 return String.Format ("'{0}'", parameter.Value.ToString ().Replace ("'", "''"));
431 public override string Prepare (string sql, TdsMetaParameterCollection parameters)
433 Parameters = parameters;
435 Random rand = new Random ();
436 StringBuilder idBuilder = new StringBuilder ();
437 for (int i = 0; i < 25; i += 1)
438 idBuilder.Append ((char) (rand.Next (26) + 65));
439 string id = idBuilder.ToString ();
441 //StringBuilder declare = new StringBuilder ();
444 sql = String.Format ("create proc {0} as\n{1}", id, sql);
445 short len = (short) ((id.Length) + sql.Length + 5);
447 Comm.StartPacket (TdsPacketType.Normal);
448 Comm.Append ((byte) TdsPacketSubType.Dynamic);
450 Comm.Append ((byte) 0x1); // PREPARE
451 Comm.Append ((byte) 0x0); // UNUSED
452 Comm.Append ((byte) id.Length);
454 Comm.Append ((short) sql.Length);
464 protected override void ProcessColumnInfo ()
466 isSelectQuery = true;
467 /*int totalLength = */Comm.GetTdsShort ();
468 int count = Comm.GetTdsShort ();
469 for (int i = 0; i < count; i += 1) {
470 string columnName = Comm.GetString (Comm.GetByte ());
471 int status = Comm.GetByte ();
472 bool hidden = (status & 0x01) > 0;
473 bool isKey = (status & 0x02) > 0;
474 bool isRowVersion = (status & 0x04) > 0;
475 bool isUpdatable = (status & 0x10) > 0;
476 bool allowDBNull = (status & 0x20) > 0;
477 bool isIdentity = (status & 0x40) > 0;
479 Comm.Skip (4); // User type
481 byte type = Comm.GetByte ();
482 bool isBlob = (type == 0x24);
484 TdsColumnType columnType = (TdsColumnType) type;
490 if (columnType == TdsColumnType.Text || columnType == TdsColumnType.Image) {
491 bufLength = Comm.GetTdsInt ();
492 Comm.Skip (Comm.GetTdsShort ());
494 else if (IsFixedSizeColumn (columnType))
495 bufLength = LookupBufferSize (columnType);
497 //bufLength = Comm.GetTdsShort ();
498 bufLength = Comm.GetByte ();
500 if (columnType == TdsColumnType.Decimal || columnType == TdsColumnType.Numeric) {
501 precision = Comm.GetByte ();
502 scale = Comm.GetByte ();
505 Comm.Skip (Comm.GetByte ()); // Locale
507 Comm.Skip (Comm.GetTdsShort ()); // Class ID
509 TdsDataColumn col = new TdsDataColumn ();
512 col.ColumnType = columnType;
513 col.ColumnName = columnName;
514 col.IsIdentity = isIdentity;
515 col.IsRowVersion = isRowVersion;
516 col.ColumnType = columnType;
517 col.ColumnSize = bufLength;
518 col.NumericPrecision = precision;
519 col.NumericScale = scale;
520 col.IsReadOnly = !isUpdatable;
522 col.AllowDBNull = allowDBNull;
523 col.IsHidden = hidden;
525 col ["ColumnType"] = columnType;
526 col ["ColumnName"] = columnName;
527 col ["IsIdentity"] = isIdentity;
528 col ["IsRowVersion"] = isRowVersion;
529 col ["ColumnType"] = columnType;
530 col ["ColumnSize"] = bufLength;
531 col ["NumericPrecision"] = precision;
532 col ["NumericScale"] = scale;
533 col ["IsReadOnly"] = !isUpdatable;
534 col ["IsKey"] = isKey;
535 col ["AllowDBNull"] = allowDBNull;
536 col ["IsHidden"] = hidden;
541 private void SendParamFormat ()
543 Comm.Append ((byte) TdsPacketSubType.ParamFormat);
545 int len = 2 + (8 * Parameters.Count);
546 TdsColumnType metaType;
547 foreach (TdsMetaParameter p in Parameters) {
548 metaType = p.GetMetaType ();
549 if (!IsFixedSizeColumn (metaType))
551 if (metaType == TdsColumnType.Numeric || metaType == TdsColumnType.Decimal)
555 Comm.Append ((short) len);
556 Comm.Append ((short) Parameters.Count);
558 foreach (TdsMetaParameter p in Parameters) {
559 string locale = String.Empty;
560 string parameterName = String.Empty;
566 if (p.Direction == TdsParameterDirection.Output)
569 metaType = p.GetMetaType ();
571 Comm.Append ((byte) parameterName.Length);
572 Comm.Append (parameterName);
573 Comm.Append (status);
574 Comm.Append (userType);
575 Comm.Append ((byte) metaType);
577 if (!IsFixedSizeColumn (metaType))
578 Comm.Append ((byte) p.Size); // MAXIMUM SIZE
579 if (metaType == TdsColumnType.Numeric || metaType == TdsColumnType.Decimal) {
580 Comm.Append (p.Precision);
581 Comm.Append (p.Scale);
583 Comm.Append ((byte) locale.Length);
584 Comm.Append (locale);
588 private void SendParams ()
590 Comm.Append ((byte) TdsPacketSubType.Parameters);
592 TdsColumnType metaType;
593 foreach (TdsMetaParameter p in Parameters) {
594 metaType = p.GetMetaType ();
595 bool isNull = (p.Value == DBNull.Value || p.Value == null);
596 if (!IsFixedSizeColumn (metaType))
597 Comm.Append ((byte) p.GetActualSize ());
599 Comm.Append (p.Value);
603 public override void Unprepare (string statementId)
605 Comm.StartPacket (TdsPacketType.Normal);
606 Comm.Append ((byte) TdsPacketSubType.Dynamic);
607 Comm.Append ((short) (3 + statementId.Length));
608 Comm.Append ((byte) 0x04);
609 Comm.Append ((byte) 0x00);
610 Comm.Append ((byte) statementId.Length);
611 Comm.Append (statementId);
612 //Comm.Append ((short) 0);
619 protected override bool IsValidRowCount (byte status, byte op)
622 return (isSelectQuery = false);
624 // TODO : Need to figure out how to calculate rowcount inside stored
625 // procedures. For now, Ignoring RowCount if they are returned by
626 // statements executing inside a StoredProcedure
628 if (((status & (byte)0x40) != 0) || ((status & (byte)0x10) == 0))
634 #endregion // Methods