2 // Mono.Data.Tds.Protocol.TdsBulkCopy.cs
5 // Nagappan A (anagappan@novell.com)
7 // Copyright (C) 2007 Novell Inc
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:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
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.
32 namespace Mono.Data.Tds.Protocol {
33 public class TdsBulkCopy
42 public TdsBulkCopy (Tds tds)
50 public bool SendColumnMetaData (string colMetaData)
52 tds.Comm.StartPacket (TdsPacketType.Query);
53 tds.Comm.Append (colMetaData);
54 tds.ExecBulkCopyMetaData (30, false);
58 public bool BulkCopyStart (TdsMetaParameterCollection parameters)
60 tds.Comm.StartPacket (TdsPacketType.Bulk);
61 tds.Comm.Append ((byte) TdsPacketSubType.ColumnMetadata);
63 foreach (TdsMetaParameter param in parameters) {
64 if (param.Value != null)
68 tds.Comm.Append (count);
69 if (parameters != null) {
70 foreach (TdsMetaParameter param in parameters) {
71 if (param.Value != null)
73 tds.Comm.Append ((short) 0x00);
75 if (param.IsNullable) {
77 // usUpdateable = Unused/Unkown
78 tds.Comm.Append ((short) 0x09);
80 // usUpdateable = Unused/Unkown
81 tds.Comm.Append ((short) 0x08);
84 WriteParameterInfo (param);
85 tds.Comm.Append ((byte) param.ParameterName.Length);
86 tds.Comm.Append (param.ParameterName);
92 public bool BulkCopyData (object o, bool isNewRow, int size, TdsMetaParameter parameter)
94 // First append a new row byte if needed
96 tds.Comm.Append ((byte) TdsPacketSubType.Row);
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
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);
109 tds.Comm.Append ((byte)0);
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);
123 else if (o.GetType() == typeof(string))
124 tds.Comm.Append ((short) size);
126 tds.Comm.Append ((byte) size);
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);
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);
148 public bool BulkCopyEnd ()
150 tds.Comm.Append ((byte) TdsPacketSubType.Done);
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);
160 tds.ExecBulkCopy (30, false);
164 private void WriteParameterInfo (TdsMetaParameter param)
166 TdsColumnType colType = param.GetMetaType ();
170 size = param.GetActualSize ();
175 * If column type is SqlDbType.NVarChar the size of parameter is multiplied by 2
176 * FIXME: Need to check for other types
178 if (colType == TdsColumnType.BigNVarChar)
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;
190 tds.Comm.Append ((byte)colType); // type
192 param.CalculateIsVariableType();
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);
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);
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);