Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 40f4ddd

Browse files
saurabh500danmoseley
authored andcommitted
Adding GetSchemaTable() on SqlDataReader (#19864)
* Adding GetSchemaTable to SqlDataReader * Adding tests * Fixing Typo in test name * Make tableName descriptive in tests * Addressing PR comments
1 parent 463cb50 commit 40f4ddd

File tree

5 files changed

+438
-0
lines changed

5 files changed

+438
-0
lines changed

src/System.Data.SqlClient/src/System/Data/SqlClient/SqlDataReader.cs

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
using System.Xml;
1717

1818
using Microsoft.SqlServer.Server;
19+
using System.Globalization;
1920

2021
namespace System.Data.SqlClient
2122
{
@@ -395,6 +396,266 @@ internal void Bind(TdsParserStateObject stateObj)
395396
_defaultLCID = _parser.DefaultLCID;
396397
}
397398

399+
internal DataTable BuildSchemaTable()
400+
{
401+
_SqlMetaDataSet md = this.MetaData;
402+
Debug.Assert(null != md, "BuildSchemaTable - unexpected null metadata information");
403+
404+
DataTable schemaTable = new DataTable("SchemaTable");
405+
schemaTable.Locale = CultureInfo.InvariantCulture;
406+
schemaTable.MinimumCapacity = md.Length;
407+
408+
DataColumn columnName = new DataColumn(SchemaTableColumn.ColumnName, typeof(System.String));
409+
DataColumn ordinal = new DataColumn(SchemaTableColumn.ColumnOrdinal, typeof(System.Int32));
410+
DataColumn size = new DataColumn(SchemaTableColumn.ColumnSize, typeof(System.Int32));
411+
DataColumn precision = new DataColumn(SchemaTableColumn.NumericPrecision, typeof(System.Int16));
412+
DataColumn scale = new DataColumn(SchemaTableColumn.NumericScale, typeof(System.Int16));
413+
414+
DataColumn dataType = new DataColumn(SchemaTableColumn.DataType, typeof(System.Type));
415+
DataColumn providerSpecificDataType = new DataColumn(SchemaTableOptionalColumn.ProviderSpecificDataType, typeof(System.Type));
416+
DataColumn nonVersionedProviderType = new DataColumn(SchemaTableColumn.NonVersionedProviderType, typeof(System.Int32));
417+
DataColumn providerType = new DataColumn(SchemaTableColumn.ProviderType, typeof(System.Int32));
418+
419+
DataColumn isLong = new DataColumn(SchemaTableColumn.IsLong, typeof(System.Boolean));
420+
DataColumn allowDBNull = new DataColumn(SchemaTableColumn.AllowDBNull, typeof(System.Boolean));
421+
DataColumn isReadOnly = new DataColumn(SchemaTableOptionalColumn.IsReadOnly, typeof(System.Boolean));
422+
DataColumn isRowVersion = new DataColumn(SchemaTableOptionalColumn.IsRowVersion, typeof(System.Boolean));
423+
424+
DataColumn isUnique = new DataColumn(SchemaTableColumn.IsUnique, typeof(System.Boolean));
425+
DataColumn isKey = new DataColumn(SchemaTableColumn.IsKey, typeof(System.Boolean));
426+
DataColumn isAutoIncrement = new DataColumn(SchemaTableOptionalColumn.IsAutoIncrement, typeof(System.Boolean));
427+
DataColumn isHidden = new DataColumn(SchemaTableOptionalColumn.IsHidden, typeof(System.Boolean));
428+
429+
DataColumn baseCatalogName = new DataColumn(SchemaTableOptionalColumn.BaseCatalogName, typeof(System.String));
430+
DataColumn baseSchemaName = new DataColumn(SchemaTableColumn.BaseSchemaName, typeof(System.String));
431+
DataColumn baseTableName = new DataColumn(SchemaTableColumn.BaseTableName, typeof(System.String));
432+
DataColumn baseColumnName = new DataColumn(SchemaTableColumn.BaseColumnName, typeof(System.String));
433+
434+
// unique to SqlClient
435+
DataColumn baseServerName = new DataColumn(SchemaTableOptionalColumn.BaseServerName, typeof(System.String));
436+
DataColumn isAliased = new DataColumn(SchemaTableColumn.IsAliased, typeof(System.Boolean));
437+
DataColumn isExpression = new DataColumn(SchemaTableColumn.IsExpression, typeof(System.Boolean));
438+
DataColumn isIdentity = new DataColumn("IsIdentity", typeof(System.Boolean));
439+
DataColumn dataTypeName = new DataColumn("DataTypeName", typeof(System.String));
440+
DataColumn udtAssemblyQualifiedName = new DataColumn("UdtAssemblyQualifiedName", typeof(System.String));
441+
// Xml metadata specific
442+
DataColumn xmlSchemaCollectionDatabase = new DataColumn("XmlSchemaCollectionDatabase", typeof(System.String));
443+
DataColumn xmlSchemaCollectionOwningSchema = new DataColumn("XmlSchemaCollectionOwningSchema", typeof(System.String));
444+
DataColumn xmlSchemaCollectionName = new DataColumn("XmlSchemaCollectionName", typeof(System.String));
445+
// SparseColumnSet
446+
DataColumn isColumnSet = new DataColumn("IsColumnSet", typeof(System.Boolean));
447+
448+
ordinal.DefaultValue = 0;
449+
isLong.DefaultValue = false;
450+
451+
DataColumnCollection columns = schemaTable.Columns;
452+
453+
// must maintain order for backward compatibility
454+
columns.Add(columnName);
455+
columns.Add(ordinal);
456+
columns.Add(size);
457+
columns.Add(precision);
458+
columns.Add(scale);
459+
columns.Add(isUnique);
460+
columns.Add(isKey);
461+
columns.Add(baseServerName);
462+
columns.Add(baseCatalogName);
463+
columns.Add(baseColumnName);
464+
columns.Add(baseSchemaName);
465+
columns.Add(baseTableName);
466+
columns.Add(dataType);
467+
columns.Add(allowDBNull);
468+
columns.Add(providerType);
469+
columns.Add(isAliased);
470+
columns.Add(isExpression);
471+
columns.Add(isIdentity);
472+
columns.Add(isAutoIncrement);
473+
columns.Add(isRowVersion);
474+
columns.Add(isHidden);
475+
columns.Add(isLong);
476+
columns.Add(isReadOnly);
477+
columns.Add(providerSpecificDataType);
478+
columns.Add(dataTypeName);
479+
columns.Add(xmlSchemaCollectionDatabase);
480+
columns.Add(xmlSchemaCollectionOwningSchema);
481+
columns.Add(xmlSchemaCollectionName);
482+
columns.Add(udtAssemblyQualifiedName);
483+
columns.Add(nonVersionedProviderType);
484+
columns.Add(isColumnSet);
485+
486+
for (int i = 0; i < md.Length; i++)
487+
{
488+
_SqlMetaData col = md[i];
489+
DataRow schemaRow = schemaTable.NewRow();
490+
491+
schemaRow[columnName] = col.column;
492+
schemaRow[ordinal] = col.ordinal;
493+
//
494+
// be sure to return character count for string types, byte count otherwise
495+
// col.length is always byte count so for unicode types, half the length
496+
//
497+
// For MAX and XML datatypes, we get 0x7fffffff from the server. Do not divide this.
498+
schemaRow[size] = (col.metaType.IsSizeInCharacters && (col.length != 0x7fffffff)) ? (col.length / 2) : col.length;
499+
500+
501+
schemaRow[dataType] = GetFieldTypeInternal(col);
502+
schemaRow[providerSpecificDataType] = GetProviderSpecificFieldTypeInternal(col);
503+
schemaRow[nonVersionedProviderType] = (int)col.type; // SqlDbType enum value - does not change with TypeSystem.
504+
schemaRow[dataTypeName] = GetDataTypeNameInternal(col);
505+
506+
if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && col.IsNewKatmaiDateTimeType)
507+
{
508+
schemaRow[providerType] = SqlDbType.NVarChar;
509+
switch (col.type)
510+
{
511+
case SqlDbType.Date:
512+
schemaRow[size] = TdsEnums.WHIDBEY_DATE_LENGTH;
513+
break;
514+
case SqlDbType.Time:
515+
Debug.Assert(TdsEnums.UNKNOWN_PRECISION_SCALE == col.scale || (0 <= col.scale && col.scale <= 7), "Invalid scale for Time column: " + col.scale);
516+
schemaRow[size] = TdsEnums.WHIDBEY_TIME_LENGTH[TdsEnums.UNKNOWN_PRECISION_SCALE != col.scale ? col.scale : col.metaType.Scale];
517+
break;
518+
case SqlDbType.DateTime2:
519+
Debug.Assert(TdsEnums.UNKNOWN_PRECISION_SCALE == col.scale || (0 <= col.scale && col.scale <= 7), "Invalid scale for DateTime2 column: " + col.scale);
520+
schemaRow[size] = TdsEnums.WHIDBEY_DATETIME2_LENGTH[TdsEnums.UNKNOWN_PRECISION_SCALE != col.scale ? col.scale : col.metaType.Scale];
521+
break;
522+
case SqlDbType.DateTimeOffset:
523+
Debug.Assert(TdsEnums.UNKNOWN_PRECISION_SCALE == col.scale || (0 <= col.scale && col.scale <= 7), "Invalid scale for DateTimeOffset column: " + col.scale);
524+
schemaRow[size] = TdsEnums.WHIDBEY_DATETIMEOFFSET_LENGTH[TdsEnums.UNKNOWN_PRECISION_SCALE != col.scale ? col.scale : col.metaType.Scale];
525+
break;
526+
}
527+
}
528+
else if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && col.IsLargeUdt)
529+
{
530+
if (_typeSystem == SqlConnectionString.TypeSystem.SQLServer2005)
531+
{
532+
schemaRow[providerType] = SqlDbType.VarBinary;
533+
}
534+
else
535+
{
536+
// TypeSystem.SQLServer2000
537+
schemaRow[providerType] = SqlDbType.Image;
538+
}
539+
}
540+
else if (_typeSystem != SqlConnectionString.TypeSystem.SQLServer2000)
541+
{
542+
// TypeSystem.SQLServer2005 and above
543+
544+
// SqlDbType enum value - always the actual type for SQLServer2005.
545+
schemaRow[providerType] = (int)col.type;
546+
547+
if (col.type == SqlDbType.Udt)
548+
{ // Additional metadata for UDTs.
549+
Debug.Assert(Connection.IsKatmaiOrNewer, "Invalid Column type received from the server");
550+
schemaRow[udtAssemblyQualifiedName] = col.udtAssemblyQualifiedName;
551+
}
552+
else if (col.type == SqlDbType.Xml)
553+
{ // Additional metadata for Xml.
554+
Debug.Assert(Connection.IsKatmaiOrNewer, "Invalid DataType (Xml) for the column");
555+
schemaRow[xmlSchemaCollectionDatabase] = col.xmlSchemaCollectionDatabase;
556+
schemaRow[xmlSchemaCollectionOwningSchema] = col.xmlSchemaCollectionOwningSchema;
557+
schemaRow[xmlSchemaCollectionName] = col.xmlSchemaCollectionName;
558+
}
559+
}
560+
else
561+
{
562+
// TypeSystem.SQLServer2000
563+
564+
// SqlDbType enum value - variable for certain types when SQLServer2000.
565+
schemaRow[providerType] = GetVersionedMetaType(col.metaType).SqlDbType;
566+
}
567+
568+
if (TdsEnums.UNKNOWN_PRECISION_SCALE != col.precision)
569+
{
570+
schemaRow[precision] = col.precision;
571+
}
572+
else
573+
{
574+
schemaRow[precision] = col.metaType.Precision;
575+
}
576+
577+
if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && col.IsNewKatmaiDateTimeType)
578+
{
579+
schemaRow[scale] = MetaType.MetaNVarChar.Scale;
580+
}
581+
else if (TdsEnums.UNKNOWN_PRECISION_SCALE != col.scale)
582+
{
583+
schemaRow[scale] = col.scale;
584+
}
585+
else
586+
{
587+
schemaRow[scale] = col.metaType.Scale;
588+
}
589+
590+
schemaRow[allowDBNull] = col.isNullable;
591+
592+
// If no ColInfo token received, do not set value, leave as null.
593+
if (_browseModeInfoConsumed)
594+
{
595+
schemaRow[isAliased] = col.isDifferentName;
596+
schemaRow[isKey] = col.isKey;
597+
schemaRow[isHidden] = col.isHidden;
598+
schemaRow[isExpression] = col.isExpression;
599+
}
600+
601+
schemaRow[isIdentity] = col.isIdentity;
602+
schemaRow[isAutoIncrement] = col.isIdentity;
603+
604+
605+
schemaRow[isLong] = col.metaType.IsLong;
606+
607+
// mark unique for timestamp columns
608+
if (SqlDbType.Timestamp == col.type)
609+
{
610+
schemaRow[isUnique] = true;
611+
schemaRow[isRowVersion] = true;
612+
}
613+
else
614+
{
615+
schemaRow[isUnique] = false;
616+
schemaRow[isRowVersion] = false;
617+
}
618+
619+
schemaRow[isReadOnly] = (0 == col.updatability);
620+
schemaRow[isColumnSet] = col.isColumnSet;
621+
622+
if (!string.IsNullOrEmpty(col.serverName))
623+
{
624+
schemaRow[baseServerName] = col.serverName;
625+
}
626+
if (!string.IsNullOrEmpty(col.catalogName))
627+
{
628+
schemaRow[baseCatalogName] = col.catalogName;
629+
}
630+
if (!string.IsNullOrEmpty(col.schemaName))
631+
{
632+
schemaRow[baseSchemaName] = col.schemaName;
633+
}
634+
if (!string.IsNullOrEmpty(col.tableName))
635+
{
636+
schemaRow[baseTableName] = col.tableName;
637+
}
638+
if (!string.IsNullOrEmpty(col.baseColumn))
639+
{
640+
schemaRow[baseColumnName] = col.baseColumn;
641+
}
642+
else if (!string.IsNullOrEmpty(col.column))
643+
{
644+
schemaRow[baseColumnName] = col.column;
645+
}
646+
647+
schemaTable.Rows.Add(schemaRow);
648+
schemaRow.AcceptChanges();
649+
}
650+
651+
// mark all columns as readonly
652+
foreach (DataColumn column in columns)
653+
{
654+
column.ReadOnly = true; // MDAC 70943
655+
}
656+
657+
return schemaTable;
658+
}
398659

399660
internal void Cancel(SqlCommand command)
400661
{
@@ -1063,6 +1324,27 @@ override public int GetProviderSpecificValues(object[] values)
10631324
return GetSqlValues(values);
10641325
}
10651326

1327+
public override DataTable GetSchemaTable()
1328+
{
1329+
SqlStatistics statistics = null;
1330+
try
1331+
{
1332+
statistics = SqlStatistics.StartTimer(Statistics);
1333+
if (null == _metaData || null == _metaData.schemaTable)
1334+
{
1335+
if (null != this.MetaData)
1336+
{
1337+
_metaData.schemaTable = BuildSchemaTable();
1338+
Debug.Assert(null != _metaData.schemaTable, "No schema information yet!");
1339+
}
1340+
}
1341+
return _metaData?.schemaTable;
1342+
}
1343+
finally
1344+
{
1345+
SqlStatistics.StopTimer(statistics);
1346+
}
1347+
}
10661348

10671349
override public bool GetBoolean(int i)
10681350
{

src/System.Data.SqlClient/src/System/Data/SqlClient/TdsParser.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3537,6 +3537,7 @@ private bool TryCommonProcessMetaData(TdsParserStateObject stateObj, _SqlMetaDat
35373537
return false;
35383538
}
35393539

3540+
col.isColumnSet = (TdsEnums.IsColumnSet == (flags & TdsEnums.IsColumnSet));
35403541

35413542
byte tdsType;
35423543
if (!stateObj.TryReadByte(out tdsType))

src/System.Data.SqlClient/src/System/Data/SqlClient/TdsParserHelperClasses.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ sealed internal class _SqlMetaData : SqlMetaDataPriv
274274
internal bool isHidden;
275275
internal bool isExpression;
276276
internal bool isIdentity;
277+
internal bool isColumnSet;
277278
internal string baseColumn;
278279
internal _SqlMetaData(int ordinal) : base()
279280
{
@@ -336,6 +337,7 @@ public object Clone()
336337
result.isKey = isKey;
337338
result.isHidden = isHidden;
338339
result.isIdentity = isIdentity;
340+
result.isColumnSet = isColumnSet;
339341
return result;
340342
}
341343
}
@@ -345,6 +347,7 @@ sealed internal class _SqlMetaDataSet
345347
internal ushort id; // for altrow-columns only
346348
internal int[] indexMap;
347349
internal int visibleColumns;
350+
internal DataTable schemaTable;
348351
private readonly _SqlMetaData[] _metaDataArray;
349352
internal ReadOnlyCollection<DbColumn> dbColumnSchema;
350353

0 commit comments

Comments
 (0)