in src/Core/Services/MetadataProviders/SqlMetadataProvider.cs [1267:1346]
private async Task PopulateSourceDefinitionAsync(
string entityName,
string schemaName,
string tableName,
SourceDefinition sourceDefinition,
string[]? runtimeConfigKeyFields)
{
DataTable dataTable = await GetTableWithSchemaFromDataSetAsync(entityName, schemaName, tableName);
List<DataColumn> primaryKeys = new(dataTable.PrimaryKey);
if (runtimeConfigKeyFields is null || runtimeConfigKeyFields.Length == 0)
{
sourceDefinition.PrimaryKey = new(primaryKeys.Select(primaryKey => primaryKey.ColumnName));
}
else
{
sourceDefinition.PrimaryKey = new(runtimeConfigKeyFields);
}
if (sourceDefinition.PrimaryKey.Count == 0)
{
throw new DataApiBuilderException(
message: $"Primary key not configured on the given database object {tableName}",
statusCode: HttpStatusCode.ServiceUnavailable,
subStatusCode: DataApiBuilderException.SubStatusCodes.ErrorInInitialization);
}
_entities.TryGetValue(entityName, out Entity? entity);
if (GetDatabaseType() is DatabaseType.MSSQL && entity is not null && entity.Source.Type is EntitySourceType.Table)
{
await PopulateTriggerMetadataForTable(entityName, schemaName, tableName, sourceDefinition);
}
using DataTableReader reader = new(dataTable);
DataTable schemaTable = reader.GetSchemaTable();
RuntimeConfig runtimeConfig = _runtimeConfigProvider.GetConfig();
foreach (DataRow columnInfoFromAdapter in schemaTable.Rows)
{
string columnName = columnInfoFromAdapter["ColumnName"].ToString()!;
if (runtimeConfig.IsGraphQLEnabled
&& entity is not null
&& IsGraphQLReservedName(entity, columnName, graphQLEnabledGlobally: runtimeConfig.IsGraphQLEnabled))
{
throw new DataApiBuilderException(
message: $"The column '{columnName}' violates GraphQL name restrictions.",
statusCode: HttpStatusCode.ServiceUnavailable,
subStatusCode: DataApiBuilderException.SubStatusCodes.ErrorInInitialization);
}
ColumnDefinition column = new()
{
IsNullable = (bool)columnInfoFromAdapter["AllowDBNull"],
IsAutoGenerated = (bool)columnInfoFromAdapter["IsAutoIncrement"],
SystemType = (Type)columnInfoFromAdapter["DataType"],
// An auto-increment column is also considered as a read-only column. For other types of read-only columns,
// the flag is populated later via PopulateColumnDefinitionsWithReadOnlyFlag() method.
IsReadOnly = (bool)columnInfoFromAdapter["IsAutoIncrement"]
};
// Tests may try to add the same column simultaneously
// hence we use TryAdd here.
// If the addition fails, it is assumed the column definition
// has already been added and need not error out.
sourceDefinition.Columns.TryAdd(columnName, column);
}
DataTable columnsInTable = await GetColumnsAsync(schemaName, tableName);
PopulateColumnDefinitionWithHasDefaultAndDbType(
sourceDefinition,
columnsInTable);
if (entity is not null && entity.Source.Type is EntitySourceType.Table)
{
// For MySql, database name is equivalent to schema name.
string schemaOrDatabaseName = GetDatabaseType() is DatabaseType.MySQL ? GetDatabaseName() : schemaName;
await PopulateColumnDefinitionsWithReadOnlyFlag(tableName, schemaOrDatabaseName, sourceDefinition);
}
}