222 lines
6.9 KiB
C#
Raw Normal View History

//
// Mono.Data.Tds.Protocol.TdsBulkCopy.cs
//
// Author:
// Nagappan A (anagappan@novell.com)
//
// Copyright (C) 2007 Novell Inc
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
namespace Mono.Data.Tds.Protocol {
public class TdsBulkCopy
{
#region Fields
Tds tds;
#endregion
#region Constructors
public TdsBulkCopy (Tds tds)
{
this.tds = tds;
}
#endregion
#region Methods
public bool SendColumnMetaData (string colMetaData)
{
tds.Comm.StartPacket (TdsPacketType.Query);
tds.Comm.Append (colMetaData);
tds.ExecBulkCopyMetaData (30, false);
return true;
}
public bool BulkCopyStart (TdsMetaParameterCollection parameters)
{
tds.Comm.StartPacket (TdsPacketType.Bulk);
tds.Comm.Append ((byte) TdsPacketSubType.ColumnMetadata);
short count = 0;
foreach (TdsMetaParameter param in parameters) {
if (param.Value != null)
continue;
count++;
}
tds.Comm.Append (count);
if (parameters != null) {
foreach (TdsMetaParameter param in parameters) {
if (param.Value != null)
continue;
tds.Comm.Append ((short) 0x00);
if (param.IsNullable) {
// fNullable = true
// usUpdateable = Unused/Unkown
tds.Comm.Append ((short) 0x09);
} else {
// usUpdateable = Unused/Unkown
tds.Comm.Append ((short) 0x08);
}
WriteParameterInfo (param);
tds.Comm.Append ((byte) param.ParameterName.Length);
tds.Comm.Append (param.ParameterName);
}
}
return true;
}
public bool BulkCopyData (object o, bool isNewRow, int size, TdsMetaParameter parameter)
{
// First append a new row byte if needed
if (isNewRow)
tds.Comm.Append ((byte) TdsPacketSubType.Row);
// Push the null value if that is what was supplied
if (o == null || o == DBNull.Value) {
if (parameter.IsAnyVarCharMax) {
// So max varchar and nvarchar needs to contain all F's as a long value. Seems crazy
// but oh well
tds.Comm.Append(System.Convert.ToInt64("0xFFFFFFFFFFFFFFFF", 16));
} else if (parameter.IsTextType) {
tds.Comm.Append((byte)0XFF);
tds.Comm.Append((byte)0XFF);
}
else
tds.Comm.Append ((byte)0);
return true;
}
// Now we must put the size in if it is a VariableType
// The length of the size field varies based on what type it is
parameter.CalculateIsVariableType();
if (parameter.IsVariableSizeType) {
//int size = parameter.GetActualSize();
if (parameter.IsAnyVarCharMax) {
// So max varchar and nvarchar needs to contain the long value as well as size is specified as int
tds.Comm.Append(System.Convert.ToInt64("0xFFFFFFFFFFFFFFFE", 16));
tds.Comm.Append ((int) size);
}
else if (o.GetType() == typeof(string))
tds.Comm.Append ((short) size);
else
tds.Comm.Append ((byte) size);
}
// There are a few special cases for bulk insert that we will handle ourself
// Otherwise we can just pass the value down to the generic Append Object function
if (parameter.IsNonUnicodeText)
tds.Comm.AppendNonUnicode ((string)o);
else if (parameter.IsMoneyType)
tds.Comm.AppendMoney ((decimal)o, size);
else if (parameter.IsDateTimeType)
tds.Comm.Append((DateTime)o, size);
else if (parameter.IsDecimalType)
tds.Comm.AppendDecimal((decimal)o, size, parameter.Scale);
else
tds.Comm.Append (o);
// For some reason max varchar and nvarchar values need to have 4 bytes of 0 appended
if (parameter.IsAnyVarCharMax)
tds.Comm.Append ((int)0);
return true;
}
public bool BulkCopyEnd ()
{
tds.Comm.Append ((byte) TdsPacketSubType.Done);
// So the TDS spec calls for a Status (ushort), CurCmd (ushort) and DoneRowCount (long)
// all of which are 0.
// However it looks like MS .net is only sending 8 bytes not sure which parts they are leaving
// out but we are going with the TDS spec size
tds.Comm.Append ((short) 0x00);
tds.Comm.Append ((short) 0x00);
tds.Comm.Append ((long) 0x00);
tds.ExecBulkCopy (30, false);
return true;
}
private void WriteParameterInfo (TdsMetaParameter param)
{
TdsColumnType colType = param.GetMetaType ();
int size = 0;
if (param.Size == 0)
size = param.GetActualSize ();
else
size = param.Size;
/*
* If column type is SqlDbType.NVarChar the size of parameter is multiplied by 2
* FIXME: Need to check for other types
*/
if (colType == TdsColumnType.BigNVarChar)
size <<= 1;
// Total hack for varchar(max) and nvarchar(max)
// They are coming back as Text and not the correct values
// based on the size we can determine what the correct type is
// We need the size to come out to 0xFFFF on the wire.
if (param.IsVarNVarCharMax)
colType = TdsColumnType.BigNVarChar;
else if (param.IsVarCharMax)
colType = TdsColumnType.BigVarChar;
tds.Comm.Append ((byte)colType); // type
param.CalculateIsVariableType();
if (param.IsAnyVarCharMax) {
tds.Comm.Append ((byte)0xFF);
tds.Comm.Append ((byte)0xFF);
} else if (tds.IsLargeType (colType))
tds.Comm.Append ((short)size); // Parameter size passed in SqlParameter
else if (tds.IsBlobType (colType))
tds.Comm.Append (size); // Parameter size passed in SqlParameter
else if (param.IsVariableSizeType)
tds.Comm.Append ((byte)size);
// Precision and Scale are non-zero for only decimal/numeric
if ( param.TypeName == "decimal" || param.TypeName == "numeric") {
tds.Comm.Append ((param.Precision!=0)?param.Precision:(byte)29);
tds.Comm.Append (param.Scale);
}
// Documentation is basically 0 on these 5 bytes. But in a nutshell it seems during a bulk insert
// these are required for text types.
if (param.IsTextType) {
tds.Comm.Append ((byte)0x09);
tds.Comm.Append ((byte)0x04);
tds.Comm.Append ((byte)0xd0);
tds.Comm.Append ((byte)0x00);
tds.Comm.Append ((byte)0x34);
}
}
#endregion
}
}