[utils] Fix force inlining compilation error with gcc on linux
[mono.git] / mcs / class / Mono.Data.Tds / Mono.Data.Tds.Protocol / Tds50.cs
1 //
2 // Mono.Data.Tds.Protocol.Tds50.cs
3 //
4 // Author:
5 //   Tim Coleman (tim@timcoleman.com)
6 //
7 // Copyright (C) 2002 Tim Coleman
8 //
9
10 //
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:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
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.
29 //
30
31 using Mono.Data.Tds;
32 using System;
33 using System.Text;
34
35 namespace Mono.Data.Tds.Protocol
36 {
37         [MonoTODO ("FIXME: Can packetsize be anything other than 512?")]
38         public sealed class Tds50 : Tds
39         {
40                 #region Fields
41
42                 public static readonly TdsVersion Version = TdsVersion.tds50;
43                 int packetSize;
44                 bool isSelectQuery;
45
46                 #endregion // Fields
47
48                 #region Constructors
49
50                 public Tds50 (string server, int port)
51                         : this (server, port, 512, 15)
52                 {
53                 }
54
55                 public Tds50 (string server, int port, int packetSize, int timeout)
56                         : base (server, port, packetSize, timeout, Version)
57                 {
58                         this.packetSize = packetSize;
59                 }
60
61                 #endregion // Constructors
62         
63                 #region Methods
64
65                 public string BuildExec (string sql)
66                 {
67                         if (Parameters == null || Parameters.Count == 0) 
68                                 return sql;
69
70                         StringBuilder select = new StringBuilder ();
71                         StringBuilder set = new StringBuilder ();
72                         StringBuilder declare = new StringBuilder ();
73                         int count = 0;
74                         foreach (TdsMetaParameter p in Parameters) {
75                                 declare.Append (String.Format ("declare {0}\n", p.Prepare ()));
76                                 set.Append (String.Format ("select {0}=", p.ParameterName));
77                                 if (p.Direction == TdsParameterDirection.Input)
78                                         set.Append (FormatParameter (p));
79                                 else {
80                                         set.Append ("NULL");
81                                         select.Append (p.ParameterName);
82                                         if (count == 0)
83                                                 select.Append ("select ");
84                                         else
85                                                 select.Append (", ");
86                                         count += 1;
87                                 }
88                                 set.Append ("\n");
89                         }       
90                         return String.Format ("{0}{1}{2}\n{3}", declare.ToString (), set.ToString (), sql, select.ToString ());
91                 }
92
93                 public override bool Connect (TdsConnectionParameters connectionParameters)
94                 {
95                         if (IsConnected)
96                                 throw new InvalidOperationException ("The connection is already open.");
97
98                         byte[] capabilityRequest = {0x03, 0xef, 0x65, 0x41, 0xff, 0xff, 0xff, 0xd6};
99                         byte[] capabilityResponse = {0x00, 0x00, 0x00, 0x06, 0x48, 0x00, 0x00, 0x08};
100
101                         SetCharset (connectionParameters.Charset);
102                         SetLanguage (connectionParameters.Language);
103
104                         byte pad = (byte) 0;
105                         byte[] empty = new byte[0];
106
107                         Comm.StartPacket (TdsPacketType.Logon);
108
109                         // hostname (offset 0)
110                         // 0-30
111                         byte[] tmp = Comm.Append (connectionParameters.Hostname, 30, pad);
112                         Comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30));
113
114                         // username (offset 31 0x1f)
115                         // 31-61
116                         tmp = Comm.Append (connectionParameters.User, 30, pad);
117                         Comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30));
118
119                         // password (offset 62 0x3e)
120                         // 62-92
121                         tmp = Comm.Append (connectionParameters.Password, 30, pad);
122                         Comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30));
123
124                         // hostproc (offset 93 0x5d)
125                         // 93-123
126                         tmp = Comm.Append ("37876", 30, pad);
127                         Comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30));
128
129                         // Byte order of 2 byte ints
130                         // 2 = <MSB, LSB>, 3 = <LSB, MSB>
131                         // 124
132                         Comm.Append ((byte) 3);
133
134                         // Byte order of 4 byte ints
135                         // 0 = <MSB, LSB>, 1 = <LSB, MSB>
136                         // 125
137                         Comm.Append ((byte) 1);
138
139                         // Character representation
140                         // (6 = ASCII, 7 = EBCDIC)
141                         // 126
142                         Comm.Append ((byte) 6);
143
144                         // Eight byte floating point representation
145                         // 4 = IEEE <MSB, ..., LSB>
146                         // 5 = VAX 'D'
147                         // 10 = IEEE <LSB, ..., MSB>
148                         // 11 = ND5000
149                         // 127
150                         Comm.Append ((byte) 10);
151
152                         // Eight byte date format
153                         // 8 = <MSB, ..., LSB>
154                         // 128
155                         Comm.Append ((byte) 9);
156                 
157                         // notify of use db
158                         // 129
159                         Comm.Append ((byte) 1);
160
161                         // disallow dump/load and bulk insert
162                         // 130
163                         Comm.Append ((byte) 1);
164
165                         // sql interface type
166                         // 131
167                         Comm.Append ((byte) 0);
168
169                         // type of network connection
170                         // 132
171                         Comm.Append ((byte) 0);
172
173                         // spare [7]
174                         // 133-139
175                         Comm.Append (empty, 7, pad);
176
177                         // appname
178                         // 140-170
179                         tmp = Comm.Append (connectionParameters.ApplicationName, 30, pad);
180                         Comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30));
181
182                         // server name
183                         // 171-201
184                         tmp = Comm.Append (DataSource, 30, pad);
185                         Comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30));
186
187                         // remote passwords
188                         // 202-457      
189                         Comm.Append (empty, 2, pad);
190                         tmp = Comm.Append (connectionParameters.Password, 253, pad);
191                         Comm.Append ((byte) (tmp.Length < 253 ? tmp.Length + 2 : 253 + 2));
192
193                         // tds version
194                         // 458-461
195                         Comm.Append ((byte) 5);
196                         Comm.Append ((byte) 0);
197                         Comm.Append ((byte) 0);
198                         Comm.Append ((byte) 0);
199
200                         // prog name
201                         // 462-472
202                         tmp = Comm.Append (connectionParameters.ProgName, 10, pad);
203                         Comm.Append ((byte) (tmp.Length < 10 ? tmp.Length : 10));
204
205                         // prog version
206                         // 473-476
207                         Comm.Append ((byte) 6);
208                         Comm.Append ((byte) 0);
209                         Comm.Append ((byte) 0);
210                         Comm.Append ((byte) 0);
211
212                         // auto convert short
213                         // 477
214                         Comm.Append ((byte) 0);
215
216                         // type of flt4
217                         // 478
218                         Comm.Append ((byte) 0x0d);
219
220                         // type of date4
221                         // 479
222                         Comm.Append ((byte) 0x11);
223
224                         // language
225                         // 480-510
226                         tmp = Comm.Append (Language, 30, pad);
227                         Comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30));
228
229                         // notify on lang change
230                         // 511
231                         Comm.Append ((byte) 1);
232
233                         // security label hierarchy
234                         // 512-513
235                         Comm.Append ((short) 0);
236
237                         // security components
238                         // 514-521
239                         Comm.Append (empty, 8, pad);
240
241                         // security spare
242                         // 522-523
243                         Comm.Append ((short) 0);
244
245                         // security login role
246                         // 524
247                         Comm.Append ((byte) 0);
248
249                         // charset
250                         // 525-555
251                         tmp = Comm.Append (Charset, 30, pad);
252                         Comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30));
253
254                         // notify on charset change
255                         // 556
256                         Comm.Append ((byte) 1);
257
258                         // length of tds packets
259                         // 557-563
260                         tmp = Comm.Append (this.packetSize.ToString (), 6, pad);
261                         Comm.Append ((byte) (tmp.Length < 6 ? tmp.Length : 6));
262
263                         Comm.Append (empty, 8, pad);
264                         // Padding...
265                         // 564-567
266                         //Comm.Append (empty, 4, pad);
267
268                         // Capabilities
269                         Comm.Append ((byte) TdsPacketSubType.Capability);
270                         Comm.Append ((short) 20);
271                         Comm.Append ((byte) 0x01); // TDS_CAP_REQUEST
272                         Comm.Append (capabilityRequest);
273                         Comm.Append ((byte) 0x02);
274                         Comm.Append (capabilityResponse);
275
276                         Comm.SendPacket ();
277
278                         MoreResults = true;
279                         SkipToEnd ();
280
281                         return IsConnected;
282                 }
283
284                 public override void ExecPrepared (string id, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
285                 {
286                         Parameters = parameters;
287                         bool hasParameters = (Parameters != null && Parameters.Count > 0);
288
289                         Comm.StartPacket (TdsPacketType.Normal);
290
291                         Comm.Append ((byte) TdsPacketSubType.Dynamic);
292                         Comm.Append ((short) (id.Length + 5));
293                         Comm.Append ((byte) 0x02);                  // TDS_DYN_EXEC
294                         Comm.Append ((byte) (hasParameters ? 0x01 : 0x00));
295                         Comm.Append ((byte) id.Length);
296                         Comm.Append (id);
297                         Comm.Append ((short) 0);
298
299                         if (hasParameters) {
300                                 SendParamFormat ();
301                                 SendParams ();
302                         }
303
304                         MoreResults = true;
305                         Comm.SendPacket ();
306                         CheckForData (timeout);
307                         if (!wantResults)
308                                 SkipToEnd ();
309                 }
310
311                 public override void Execute (string sql, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
312                 {
313                         Parameters = parameters;
314                         string ex = BuildExec (sql);
315                         ExecuteQuery (ex, timeout, wantResults);
316                 }
317
318                 public override void ExecProc (string commandText, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
319                 {
320                         Parameters = parameters;
321                         ExecuteQuery (BuildProcedureCall (commandText), timeout, wantResults);
322                 }
323
324                 private string BuildProcedureCall (string procedure)
325                 {
326                         string exec = String.Empty;
327
328                         StringBuilder declare = new StringBuilder ();
329                         StringBuilder select = new StringBuilder ();
330                         StringBuilder set = new StringBuilder ();
331                         
332                         int count = 0;
333                         if (Parameters != null) {
334                                 foreach (TdsMetaParameter p in Parameters) {
335                                         if (p.Direction != TdsParameterDirection.Input) {
336
337                                                 if (count == 0)
338                                                         select.Append ("select ");
339                                                 else
340                                                         select.Append (", ");
341                                                 select.Append (p.ParameterName);
342                                                         
343                                                 declare.Append (String.Format ("declare {0}\n", p.Prepare ()));
344
345                                                 if (p.Direction != TdsParameterDirection.ReturnValue) {
346                                                         if( p.Direction == TdsParameterDirection.InputOutput )
347                                                                 set.Append (String.Format ("set {0}\n", FormatParameter(p)));
348                                                         else
349                                                 set.Append (String.Format ("set {0}=NULL\n", p.ParameterName));
350                                                 }
351                                         
352                                                 count += 1;
353                                         }
354                                         
355                                         if (p.Direction == TdsParameterDirection.ReturnValue)
356                                                 exec = p.ParameterName + "=";
357                                 }
358                         }
359                         exec = "exec " + exec;
360
361                         string sql = String.Format ("{0}{1}{2}{3} {4}\n{5}", declare.ToString (),
362                                 set.ToString (),
363                                 exec, procedure,
364                                 BuildParameters (), select.ToString ());
365                         return sql;
366                 }
367
368                 private string BuildParameters ()
369                 {
370                         if (Parameters == null || Parameters.Count == 0)
371                                 return String.Empty;
372
373                         StringBuilder result = new StringBuilder ();
374                         foreach (TdsMetaParameter p in Parameters) {
375                                 if (p.Direction != TdsParameterDirection.ReturnValue) {
376                                 if (result.Length > 0)
377                                         result.Append (", ");
378                                         if (p.Direction == TdsParameterDirection.InputOutput)
379                                                 result.Append (String.Format("{0}={0} output", p.ParameterName));
380                                         else
381                                 result.Append (FormatParameter (p));
382                         }
383                         }
384                         return result.ToString ();
385                 }
386
387
388                 private string FormatParameter (TdsMetaParameter parameter)
389                 {
390                         if (parameter.Direction == TdsParameterDirection.Output)
391                                 return String.Format ("{0} output", parameter.ParameterName);
392                 
393                         if (parameter.Value == null || parameter.Value == DBNull.Value)
394                                 return "NULL";
395                 
396                         switch (parameter.TypeName) {
397                         case "smalldatetime":
398                         case "datetime":
399                                 DateTime d = (DateTime)parameter.Value;
400                                 return String.Format(System.Globalization.CultureInfo.InvariantCulture, 
401                                                      "'{0:MMM dd yyyy hh:mm:ss tt}'", d );
402                         case "bigint":
403                         case "decimal":
404                         case "float":
405                         case "int":
406                         case "money":
407                         case "real":
408                         case "smallint":
409                         case "smallmoney":
410                         case "tinyint":
411                                 return parameter.Value.ToString ();
412                         case "nvarchar":
413                         case "nchar":
414                                 return String.Format ("N'{0}'", parameter.Value.ToString ().Replace ("'", "''"));
415                         case "uniqueidentifier":
416                                 return String.Format ("0x{0}", ((Guid) parameter.Value).ToString ("N"));
417                         case "bit":
418                                 if (parameter.Value.GetType () == typeof (bool))
419                                         return (((bool) parameter.Value) ? "0x1" : "0x0");
420                                 return parameter.Value.ToString ();
421                         case "image":
422                         case "binary":
423                         case "varbinary":
424                                 return String.Format ("0x{0}", BitConverter.ToString ((byte[]) parameter.Value).Replace ("-", string.Empty).ToLower ());
425                         default:
426                                 return String.Format ("'{0}'", parameter.Value.ToString ().Replace ("'", "''"));
427                         }
428                 }
429
430                 public override string Prepare (string sql, TdsMetaParameterCollection parameters)
431                 {
432                         Parameters = parameters;
433
434                         Random rand = new Random ();
435                         StringBuilder idBuilder = new StringBuilder ();
436                         for (int i = 0; i < 25; i += 1)
437                                 idBuilder.Append ((char) (rand.Next (26) + 65));
438                         string id = idBuilder.ToString ();
439
440                         //StringBuilder declare = new StringBuilder ();
441
442                 
443                         sql = String.Format ("create proc {0} as\n{1}", id, sql);
444                         short len = (short) ((id.Length) + sql.Length + 5);
445
446                         Comm.StartPacket (TdsPacketType.Normal);
447                         Comm.Append ((byte) TdsPacketSubType.Dynamic);
448                         Comm.Append (len);
449                         Comm.Append ((byte) 0x1); // PREPARE
450                         Comm.Append ((byte) 0x0); // UNUSED
451                         Comm.Append ((byte) id.Length);
452                         Comm.Append (id);
453                         Comm.Append ((short) sql.Length);
454                         Comm.Append (sql);
455
456                         Comm.SendPacket ();
457                         MoreResults = true;
458                         SkipToEnd ();
459
460                         return id;
461                 }
462
463                 protected override void ProcessColumnInfo ()
464                 {
465                         isSelectQuery = true; 
466                         /*int totalLength = */Comm.GetTdsShort ();
467                         int count = Comm.GetTdsShort ();
468                         for (int i = 0; i < count; i += 1) {
469                                 string columnName = Comm.GetString (Comm.GetByte ());
470                                 int status = Comm.GetByte ();
471                                 bool hidden = (status & 0x01) > 0;
472                                 bool isKey = (status & 0x02) > 0;
473                                 bool isRowVersion = (status & 0x04) > 0;
474                                 bool isUpdatable = (status & 0x10) > 0;
475                                 bool allowDBNull = (status & 0x20) > 0;
476                                 bool isIdentity = (status & 0x40) > 0;
477
478                                 Comm.Skip (4); // User type
479
480                                 byte type = Comm.GetByte ();
481                                 bool isBlob = (type == 0x24);
482
483                                 TdsColumnType columnType = (TdsColumnType) type;
484                                 int bufLength = 0;
485
486                                 byte precision = 0;
487                                 byte scale = 0;
488
489                                 if (columnType == TdsColumnType.Text || columnType == TdsColumnType.Image) {
490                                         bufLength = Comm.GetTdsInt ();
491                                         Comm.Skip (Comm.GetTdsShort ());
492                                 }
493                                 else if (IsFixedSizeColumn (columnType))
494                                         bufLength = LookupBufferSize (columnType);
495                                 else
496                                         //bufLength = Comm.GetTdsShort ();
497                                         bufLength = Comm.GetByte ();
498
499                                 if (columnType == TdsColumnType.Decimal || columnType == TdsColumnType.Numeric) {
500                                         precision = Comm.GetByte ();
501                                         scale = Comm.GetByte ();
502                                 }
503
504                                 Comm.Skip (Comm.GetByte ()); // Locale
505                                 if (isBlob)
506                                         Comm.Skip (Comm.GetTdsShort ()); // Class ID
507
508                                 TdsDataColumn col = new TdsDataColumn ();
509                                 Columns.Add (col);
510 #if NET_2_0
511                                 col.ColumnType = columnType;
512                                 col.ColumnName = columnName;
513                                 col.IsIdentity = isIdentity;
514                                 col.IsRowVersion = isRowVersion;
515                                 col.ColumnType = columnType;
516                                 col.ColumnSize = bufLength;
517                                 col.NumericPrecision = precision;
518                                 col.NumericScale = scale;
519                                 col.IsReadOnly = !isUpdatable;
520                                 col.IsKey = isKey;
521                                 col.AllowDBNull = allowDBNull;
522                                 col.IsHidden = hidden;
523 #else
524                                 col ["ColumnType"] = columnType;
525                                 col ["ColumnName"] = columnName;
526                                 col ["IsIdentity"] = isIdentity;
527                                 col ["IsRowVersion"] = isRowVersion;
528                                 col ["ColumnType"] = columnType;
529                                 col ["ColumnSize"] = bufLength;
530                                 col ["NumericPrecision"] = precision;
531                                 col ["NumericScale"] = scale;
532                                 col ["IsReadOnly"] = !isUpdatable;
533                                 col ["IsKey"] = isKey;
534                                 col ["AllowDBNull"] = allowDBNull;
535                                 col ["IsHidden"] = hidden;
536 #endif
537                         }
538                 }
539
540                 private void SendParamFormat ()
541                 {
542                         Comm.Append ((byte) TdsPacketSubType.ParamFormat);
543
544                         int len = 2 + (8 * Parameters.Count);
545                         TdsColumnType metaType;
546                         foreach (TdsMetaParameter p in Parameters) {
547                                 metaType = p.GetMetaType ();
548                                 if (!IsFixedSizeColumn (metaType))
549                                         len += 1;
550                                 if (metaType == TdsColumnType.Numeric || metaType == TdsColumnType.Decimal)
551                                         len += 2;
552                         }
553
554                         Comm.Append ((short) len);
555                         Comm.Append ((short) Parameters.Count);
556
557                         foreach (TdsMetaParameter p in Parameters) {
558                                 string locale = String.Empty;
559                                 string parameterName = String.Empty;
560                                 int userType = 0;
561
562                                 byte status = 0x00;
563                                 if (p.IsNullable)
564                                         status |= 0x20;
565                                 if (p.Direction == TdsParameterDirection.Output)
566                                         status |= 0x01;
567
568                                 metaType = p.GetMetaType ();
569
570                                 Comm.Append ((byte) parameterName.Length);
571                                 Comm.Append (parameterName);
572                                 Comm.Append (status);
573                                 Comm.Append (userType);
574                                 Comm.Append ((byte) metaType);
575
576                                 if (!IsFixedSizeColumn (metaType))
577                                         Comm.Append ((byte) p.Size);         // MAXIMUM SIZE
578                                 if (metaType == TdsColumnType.Numeric || metaType == TdsColumnType.Decimal) {
579                                         Comm.Append (p.Precision);
580                                         Comm.Append (p.Scale);
581                                 }
582                                 Comm.Append ((byte) locale.Length);
583                                 Comm.Append (locale);
584                         }
585                 }
586
587                 private void SendParams ()
588                 {
589                         Comm.Append ((byte) TdsPacketSubType.Parameters);
590
591                         TdsColumnType metaType;
592                         foreach (TdsMetaParameter p in Parameters) {
593                                 metaType = p.GetMetaType ();
594                                 bool isNull = (p.Value == DBNull.Value || p.Value == null);
595                                 if (!IsFixedSizeColumn (metaType))
596                                         Comm.Append ((byte) p.GetActualSize ());
597                                 if (!isNull)
598                                         Comm.Append (p.Value);
599                         }
600                 }
601
602                 public override void Unprepare (string statementId)
603                 {
604                         Comm.StartPacket (TdsPacketType.Normal);
605                         Comm.Append ((byte) TdsPacketSubType.Dynamic);
606                         Comm.Append ((short) (3 + statementId.Length));
607                         Comm.Append ((byte) 0x04);
608                         Comm.Append ((byte) 0x00);
609                         Comm.Append ((byte) statementId.Length);
610                         Comm.Append (statementId);
611                         //Comm.Append ((short) 0);
612
613                         MoreResults = true;
614                         Comm.SendPacket ();
615                         SkipToEnd ();
616                 }
617
618                 protected override bool IsValidRowCount (byte status, byte op)
619                 {
620                         if (isSelectQuery)
621                                 return (isSelectQuery = false);
622
623                         // TODO : Need to figure out how to calculate rowcount inside stored 
624                         // procedures. For now, Ignoring RowCount if they are returned by 
625                         // statements executing inside a StoredProcedure
626
627                         if (((status & (byte)0x40) != 0) || ((status & (byte)0x10) == 0))
628                                 return false;
629
630                         return true;
631                 }
632
633                 #endregion // Methods
634         }
635 }