internal Task WriteBulkCopyValue(object value, SqlMetaDataPriv metadata, TdsParserStateObject stateObj, bool isSqlType, bool isDataFeed, bool isNull)
{
Debug.Assert(!isSqlType || value is INullable, "isSqlType is true, but value can not be type cast to an INullable");
Debug.Assert(!isDataFeed ^ value is DataFeed, "Incorrect value for isDataFeed");
Encoding saveEncoding = _defaultEncoding;
SqlCollation saveCollation = _defaultCollation;
int saveCodePage = _defaultCodePage;
int saveLCID = _defaultLCID;
Task resultTask = null;
Task internalWriteTask = null;
if (!(State == TdsParserState.OpenNotLoggedIn || State == TdsParserState.OpenLoggedIn))
{
throw ADP.ClosedConnectionError();
}
try
{
if (metadata.encoding != null)
{
_defaultEncoding = metadata.encoding;
}
if (metadata.collation != null)
{
_defaultCollation = metadata.collation;
_defaultLCID = _defaultCollation.LCID;
}
_defaultCodePage = metadata.codePage;
MetaType metatype = metadata.metaType;
int ccb = 0;
int ccbStringBytes = 0;
if (isNull)
{
// For UDT, remember we treat as binary even though it is a PLP
if (metatype.IsPlp && (metatype.NullableType != TdsEnums.SQLUDT || metatype.IsLong))
{
WriteLong(unchecked((long)TdsEnums.SQL_PLP_NULL), stateObj);
}
else if (!metatype.IsFixed && !metatype.IsLong && !metatype.IsVarTime)
{
WriteShort(TdsEnums.VARNULL, stateObj);
}
else
{
stateObj.WriteByte(TdsEnums.FIXEDNULL);
}
return resultTask;
}
if (!isDataFeed)
{
switch (metatype.NullableType)
{
case TdsEnums.SQLBIGBINARY:
case TdsEnums.SQLBIGVARBINARY:
case TdsEnums.SQLIMAGE:
case TdsEnums.SQLUDT:
ccb = (isSqlType) ? ((SqlBinary)value).Length : ((byte[])value).Length;
break;
case TdsEnums.SQLUNIQUEID:
ccb = GUID_SIZE; // that's a constant for guid
break;
case TdsEnums.SQLBIGCHAR:
case TdsEnums.SQLBIGVARCHAR:
case TdsEnums.SQLTEXT:
if (null == _defaultEncoding)
{
ThrowUnsupportedCollationEncountered(null); // stateObject only when reading
}
string stringValue = null;
if (isSqlType)
{
stringValue = ((SqlString)value).Value;
}
else
{
stringValue = (string)value;
}
ccb = stringValue.Length;
ccbStringBytes = _defaultEncoding.GetByteCount(stringValue);
break;
case TdsEnums.SQLNCHAR:
case TdsEnums.SQLNVARCHAR:
case TdsEnums.SQLNTEXT:
ccb = ((isSqlType) ? ((SqlString)value).Value.Length : ((string)value).Length) * 2;
break;
case TdsEnums.SQLXMLTYPE:
// Value here could be string or XmlReader
if (value is XmlReader)
{
value = MetaType.GetStringFromXml((XmlReader)value);
}
ccb = ((isSqlType) ? ((SqlString)value).Value.Length : ((string)value).Length) * 2;
break;
default:
ccb = metadata.length;
break;
}
}
else
{
Debug.Assert(metatype.IsLong &&
((metatype.SqlDbType == SqlDbType.VarBinary && value is StreamDataFeed) ||
((metatype.SqlDbType == SqlDbType.VarChar || metatype.SqlDbType == SqlDbType.NVarChar) && value is TextDataFeed) ||
(metatype.SqlDbType == SqlDbType.Xml && value is XmlDataFeed)),
"Stream data feed should only be assigned to VarBinary(max), Text data feed should only be assigned to [N]VarChar(max), Xml data feed should only be assigned to XML(max)");
}
// Expected the text length in data stream for bulk copy of text, ntext, or image data.
//
if (metatype.IsLong)
{
switch (metatype.SqlDbType)
{
case SqlDbType.Text:
case SqlDbType.NText:
case SqlDbType.Image:
stateObj.WriteByteArray(s_longDataHeader, s_longDataHeader.Length, 0);
WriteTokenLength(metadata.tdsType, ccbStringBytes == 0 ? ccb : ccbStringBytes, stateObj);
break;
case SqlDbType.VarChar:
case SqlDbType.NVarChar:
case SqlDbType.VarBinary:
case SqlDbType.Xml:
case SqlDbType.Udt:
// plp data
WriteUnsignedLong(TdsEnums.SQL_PLP_UNKNOWNLEN, stateObj);
break;
}
}
else
{
WriteTokenLength(metadata.tdsType, ccbStringBytes == 0 ? ccb : ccbStringBytes, stateObj);
}
if (isSqlType)
{
internalWriteTask = WriteSqlValue(value, metatype, ccb, ccbStringBytes, 0, stateObj);
}
else if (metatype.SqlDbType != SqlDbType.Udt || metatype.IsLong)
{
internalWriteTask = WriteValue(value, metatype, metadata.scale, ccb, ccbStringBytes, 0, stateObj, metadata.length, isDataFeed);
if ((internalWriteTask == null) && (_asyncWrite))
{
internalWriteTask = stateObj.WaitForAccumulatedWrites();
}
Debug.Assert(_asyncWrite || stateObj.WaitForAccumulatedWrites() == null, "Should not have accumulated writes when writing sync");
}
else
{
WriteShort(ccb, stateObj);
internalWriteTask = stateObj.WriteByteArray((byte[])value, ccb, 0);
}
#if DEBUG
//In DEBUG mode, when SetAlwaysTaskOnWrite is true, we create a task. Allows us to verify async execution paths.
if (_asyncWrite && internalWriteTask == null && SqlBulkCopy.SetAlwaysTaskOnWrite == true)
{
internalWriteTask = Task.FromResult<object>(null);
}
#endif
if (internalWriteTask != null)
{ //i.e. the write was async.
resultTask = WriteBulkCopyValueSetupContinuation(internalWriteTask, saveEncoding, saveCollation, saveCodePage, saveLCID);
}
}
finally
{
if (internalWriteTask == null)
{
_defaultEncoding = saveEncoding;
_defaultCollation = saveCollation;
_defaultCodePage = saveCodePage;
_defaultLCID = saveLCID;
}
}
return resultTask;
}