3c1f479b9d
Former-commit-id: 806294f5ded97629b74c85c09952f2a74fe182d9
222 lines
6.9 KiB
C#
222 lines
6.9 KiB
C#
//
|
|
// 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
|
|
}
|
|
}
|