Merge pull request #819 from brendanzagaeski/patch-1
[mono.git] / mcs / class / Mono.Data.Tds / Mono.Data.Tds.Protocol / TdsBulkCopy.cs
1 //
2 // Mono.Data.Tds.Protocol.TdsBulkCopy.cs
3 //
4 // Author:
5 //   Nagappan A (anagappan@novell.com)
6 //
7 // Copyright (C) 2007 Novell Inc
8
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29 #if NET_2_0
30 using System;
31
32 namespace Mono.Data.Tds.Protocol {
33         public class TdsBulkCopy
34         {
35                 #region Fields
36
37                 Tds tds;
38                 #endregion
39
40                 #region Constructors
41
42                 public TdsBulkCopy (Tds tds)
43                 {
44                         this.tds = tds;
45                 }
46
47                 #endregion
48
49                 #region Methods
50                 public bool SendColumnMetaData (string colMetaData)
51                 {
52                         tds.Comm.StartPacket (TdsPacketType.Query);
53                         tds.Comm.Append (colMetaData);
54                         tds.ExecBulkCopyMetaData (30, false);
55                         return true;
56                 }
57
58                 public bool BulkCopyStart (TdsMetaParameterCollection parameters)
59                 {
60                         tds.Comm.StartPacket (TdsPacketType.Bulk);
61                         tds.Comm.Append ((byte) TdsPacketSubType.ColumnMetadata);
62                         short count = 0;
63                         foreach (TdsMetaParameter param in parameters) {
64                                 if (param.Value != null)
65                                         continue;
66                                 count++;
67                         }
68                         tds.Comm.Append (count);
69                         if (parameters != null) {
70                                 foreach (TdsMetaParameter param in parameters) {
71                                         if (param.Value != null)
72                                                 continue;
73                                         tds.Comm.Append ((short) 0x00);
74
75                                         if (param.IsNullable) {
76                                                 // fNullable = true
77                                                 // usUpdateable = Unused/Unkown
78                                                 tds.Comm.Append ((short) 0x09);
79                                         } else {
80                                                 // usUpdateable = Unused/Unkown
81                                                 tds.Comm.Append ((short) 0x08);
82                                         }
83
84                                         WriteParameterInfo (param);
85                                         tds.Comm.Append ((byte) param.ParameterName.Length);
86                                         tds.Comm.Append (param.ParameterName);
87                                 }
88                         }
89                         return true;
90                 }
91
92                 public bool BulkCopyData (object o, bool isNewRow, int size, TdsMetaParameter parameter)
93                 {
94                         // First append a new row byte if needed
95                         if (isNewRow)
96                                 tds.Comm.Append ((byte) TdsPacketSubType.Row);
97
98                         // Push the null value if that is what was supplied
99                         if (o == null || o == DBNull.Value) {
100                                 if (parameter.IsAnyVarCharMax) {
101                                         // So max varchar and nvarchar needs to contain all F's as a long value.  Seems crazy
102                                         // but oh well
103                                         tds.Comm.Append(System.Convert.ToInt64("0xFFFFFFFFFFFFFFFF", 16));
104                                 } else if (parameter.IsTextType) {
105                                         tds.Comm.Append((byte)0XFF);
106                                         tds.Comm.Append((byte)0XFF);
107                                 }
108                                 else
109                                         tds.Comm.Append ((byte)0);
110                                 return true;
111                         }
112
113                         // Now we must put the size in if it is a VariableType
114                         // The length of the size field varies based on what type it is
115                         parameter.CalculateIsVariableType();
116                         if (parameter.IsVariableSizeType) {
117                                 //int size = parameter.GetActualSize();
118                                 if (parameter.IsAnyVarCharMax) {
119                                         // So max varchar and nvarchar needs to contain the long value as well as size is specified as int
120                                         tds.Comm.Append(System.Convert.ToInt64("0xFFFFFFFFFFFFFFFE", 16));
121                                         tds.Comm.Append ((int) size);
122                                 }
123                                 else if (o.GetType() == typeof(string))
124                                         tds.Comm.Append ((short) size);
125                                 else
126                                         tds.Comm.Append ((byte) size);
127                         }
128
129                         // There are a few special cases for bulk insert that we will handle ourself
130                         // Otherwise we can just pass the value down to the generic Append Object function
131                         if (parameter.IsNonUnicodeText)
132                                 tds.Comm.AppendNonUnicode ((string)o);
133                         else if (parameter.IsMoneyType)
134                                 tds.Comm.AppendMoney ((decimal)o, size);
135                         else if (parameter.IsDateTimeType)
136                                 tds.Comm.Append((DateTime)o, size);
137                         else if (parameter.IsDecimalType)
138                                 tds.Comm.AppendDecimal((decimal)o, size, parameter.Scale);
139                         else
140                                 tds.Comm.Append (o);
141
142                         // For some reason max varchar and nvarchar values need to have 4 bytes of 0 appended
143                         if (parameter.IsAnyVarCharMax)
144                                 tds.Comm.Append ((int)0);
145                         return true;
146                 }
147
148                 public bool BulkCopyEnd ()
149                 {
150                         tds.Comm.Append ((byte) TdsPacketSubType.Done);
151
152                         // So the TDS spec calls for a Status (ushort), CurCmd (ushort) and DoneRowCount (long)
153                         // all of which are 0.
154                         // However it looks like MS .net is only sending 8 bytes not sure which parts they are leaving
155                         // out but we are going with the TDS spec size
156                         tds.Comm.Append ((short) 0x00);
157                         tds.Comm.Append ((short) 0x00);
158                         tds.Comm.Append ((long) 0x00);
159
160                         tds.ExecBulkCopy (30, false);
161                         return true;
162                 }
163
164                 private void WriteParameterInfo (TdsMetaParameter param)
165                 {
166                         TdsColumnType colType = param.GetMetaType ();
167
168                         int size = 0;
169                         if (param.Size == 0)
170                                 size = param.GetActualSize ();
171                         else
172                                 size = param.Size;
173
174                         /*
175                          * If column type is SqlDbType.NVarChar the size of parameter is multiplied by 2
176                          * FIXME: Need to check for other types
177                          */
178                         if (colType == TdsColumnType.BigNVarChar)
179                                 size <<= 1;
180
181                         // Total hack for varchar(max) and nvarchar(max)
182                         // They are coming back as Text and not the correct values
183                         // based on the size we can determine what the correct type is
184                         // We need the size to come out to 0xFFFF on the wire.
185                         if (param.IsVarNVarCharMax)
186                                 colType = TdsColumnType.BigNVarChar;
187                         else if (param.IsVarCharMax)
188                                 colType = TdsColumnType.BigVarChar;
189
190                         tds.Comm.Append ((byte)colType); // type
191
192                         param.CalculateIsVariableType();
193
194                         if (param.IsAnyVarCharMax) {
195                                 tds.Comm.Append ((byte)0xFF);
196                                 tds.Comm.Append ((byte)0xFF);
197                         } else if (tds.IsLargeType (colType))
198                                 tds.Comm.Append ((short)size); // Parameter size passed in SqlParameter
199                         else if (tds.IsBlobType (colType))
200                                 tds.Comm.Append (size); // Parameter size passed in SqlParameter
201                         else if (param.IsVariableSizeType)
202                                 tds.Comm.Append ((byte)size);
203
204                         // Precision and Scale are non-zero for only decimal/numeric
205                         if ( param.TypeName == "decimal" || param.TypeName == "numeric") {
206                                 tds.Comm.Append ((param.Precision!=0)?param.Precision:(byte)29);
207                                 tds.Comm.Append (param.Scale);
208                         }
209
210                         // Documentation is basically 0 on these 5 bytes.  But in a nutshell it seems during a bulk insert
211                         // these are required for text types.
212                         if (param.IsTextType) {
213                                 tds.Comm.Append ((byte)0x09);
214                                 tds.Comm.Append ((byte)0x04);
215                                 tds.Comm.Append ((byte)0xd0);
216                                 tds.Comm.Append ((byte)0x00);
217                                 tds.Comm.Append ((byte)0x34);
218                         }
219                 }
220                 #endregion
221         }
222 }
223 #endif