/// <summary> /// Initializes a new instance of the <see cref="TableDefinitionData" /> class. /// </summary> /// <param name="type">The type.</param> /// <param name="schemaID">The schema identifier.</param> /// <param name="name">The name.</param> /// <param name="ordinal">The ordinal.</param> /// <param name="columnName">Name of the column.</param> /// <param name="columnType">Type of the column.</param> /// <param name="columnSize">Size of the column.</param> /// <param name="isNullable">if set to <see langword="true" /> [is nullable].</param> /// <param name="tableTypeID">The ID of the associated <see cref="SqlType"/> if this table defines a <see cref="SqlType"/>.</param> public TableDefinitionData( SqlObjectType type, int schemaID, [NotNull] string name, int ordinal, [NotNull] string columnName, [NotNull] SqlType columnType, SqlTypeSize columnSize, bool isNullable, int?tableTypeID) { if (name == null) { throw new ArgumentNullException("name"); } if (columnName == null) { throw new ArgumentNullException("columnName"); } if (columnType == null) { throw new ArgumentNullException("columnType"); } Type = type; SchemaID = schemaID; Name = name; Column = new SqlColumn(ordinal, columnName, columnType, columnSize, isNullable); TableTypeID = tableTypeID; }
/// <summary> /// Initializes a new instance of the <see cref="ProgramDefinitionData" /> class. /// </summary> /// <param name="type">The type.</param> /// <param name="schemaID">The schema identifier.</param> /// <param name="name">The name.</param> /// <param name="ordinal">The ordinal.</param> /// <param name="parameterName">Name of the parameter.</param> /// <param name="parameterType">Type of the parameter.</param> /// <param name="parameterSize">Size of the parameter.</param> /// <param name="parameterDirection">The parameter direction.</param> /// <param name="isReadonly">if set to <see langword="true" /> [is readonly].</param> public ProgramDefinitionData( SqlObjectType type, int schemaID, [NotNull] string name, int ordinal, [NotNull] string parameterName, [NotNull] SqlType parameterType, SqlTypeSize parameterSize, ParameterDirection parameterDirection, bool isReadonly) { if (name == null) { throw new ArgumentNullException("name"); } if (parameterName == null) { throw new ArgumentNullException("parameterName"); } if (parameterType == null) { throw new ArgumentNullException("parameterType"); } Type = type; SchemaID = schemaID; Name = name; Parameter = new SqlProgramParameter( ordinal, parameterName, parameterType, parameterSize, parameterDirection, isReadonly); }
/// <summary> /// Initializes a new instance of the <see cref="SqlTableType"/> class. /// </summary> /// <param name="baseType">The base type.</param> /// <param name="sqlSchema">The schema name.</param> /// <param name="name">The table name.</param> /// <param name="defaultSize">The default size information.</param> /// <param name="isNullable"> /// If set to <see langword="true"/> the value can be <see langword="null"/>. /// </param> /// <param name="isUserDefined"> /// If set to <see langword="true"/> the value is a user defined type. /// </param> /// <param name="isClr"> /// If set to <see langword="true"/> the value is a CLR type. /// </param> // ReSharper disable once NotNullMemberIsNotInitialized internal SqlTableType( [CanBeNull] SqlType baseType, [NotNull] SqlSchema sqlSchema, [NotNull] string name, SqlTypeSize defaultSize, bool isNullable, bool isUserDefined, bool isClr) : base( baseType, sqlSchema, name, defaultSize, isNullable, isUserDefined, isClr, true) { }
/// <summary> /// Initializes a new instance of the <see cref="SqlProgramParameter"/> class. /// </summary> /// <param name="ordinal"> /// The zero-based index that is the parameter position. /// </param> /// <param name="name"> /// <para>The <see cref="DatabaseEntity.FullName">parameter name</see>.</para> /// <para>The name should include the obligatory '@'.</para> /// </param> /// <param name="type">The type information.</param> /// <param name="size">The size information.</param> /// <param name="direction">The parameter direction.</param> /// <param name="isReadOnly"> /// If set to <see langword="true"/> the parameter is <see cref="IsReadOnly">read-only.</see> /// </param> /// <exception cref="ArgumentNullException"><paramref name="name"/> or <paramref name="type"/> is <see langword="null" />.</exception> internal SqlProgramParameter( int ordinal, [NotNull] string name, [NotNull] SqlType type, SqlTypeSize size, ParameterDirection direction, bool isReadOnly) : base(name) { if (name == null) { throw new ArgumentNullException(nameof(name)); } if (type == null) { throw new ArgumentNullException(nameof(type)); } Ordinal = ordinal; IsReadOnly = isReadOnly; Direction = direction; Type = type.Size.Equals(size) ? type : new SqlType(type, size); }
/// <summary> /// Initializes a new instance of the <see cref="SqlColumn"/> class. /// </summary> /// <param name="ordinal">The zero-based ordinal of the column.</param> /// <param name="name">The column name.</param> /// <param name="type">The type of the column's data.</param> /// <param name="size">The size information.</param> /// <param name="isNullable"> /// If set to <see langword="true"/> then the column is nullable. /// </param> internal SqlColumn( int ordinal, [NotNull] string name, [NotNull] SqlType type, SqlTypeSize size, bool isNullable) : base(name) // ReSharper restore PossibleNullReferenceException { if (name == null) { throw new ArgumentNullException("name"); } if (type == null) { throw new ArgumentNullException("type"); } Ordinal = ordinal; IsNullable = isNullable; Type = type.Size.Equals(size) ? type : new SqlType(type, size); switch (Type.SqlDbType) { case SqlDbType.Binary: case SqlDbType.Char: case SqlDbType.VarBinary: case SqlDbType.VarChar: SqlMetaData = new SqlMetaData(name, Type.SqlDbType, Type.Size.MaximumLength); break; case SqlDbType.NChar: case SqlDbType.NVarChar: SqlMetaData = new SqlMetaData( name, Type.SqlDbType, Type.Size.MaximumLength > 0 ? Type.Size.MaximumLength / 2 : Type.Size.MaximumLength); break; case SqlDbType.Image: case SqlDbType.Text: case SqlDbType.NText: SqlMetaData = new SqlMetaData(name, Type.SqlDbType); break; case SqlDbType.Decimal: SqlMetaData = new SqlMetaData( name, Type.SqlDbType, Type.Size.Precision, Type.Size.Scale); break; case SqlDbType.Udt: switch (Type.Name) { case "geography": SqlMetaData = new SqlMetaData( name, Type.SqlDbType, typeof(SqlGeography)); break; case "geometry": SqlMetaData = new SqlMetaData( name, Type.SqlDbType, typeof(SqlGeometry)); break; case "hierarchyid": SqlMetaData = new SqlMetaData( name, Type.SqlDbType, typeof(SqlHierarchyId)); break; default: SqlMetaData = new SqlMetaData(name, Type.SqlDbType); break; } break; default: SqlMetaData = new SqlMetaData(name, Type.SqlDbType); break; } }
private async Task <DatabaseSchema> Load(bool forceReload, CancellationToken cancellationToken) { Instant requested = TimeHelpers.Clock.Now; using (await _lock.LockAsync(cancellationToken).ConfigureAwait(false)) { // Check to see if the currently loaded schema is acceptable. CurrentSchema current = _current; // ReSharper disable once ConditionIsAlwaysTrueOrFalse if ((current != null) && (!forceReload || (current.Loaded > requested))) { // Rethrow load errors. if (current.ExceptionDispatchInfo != null) { current.ExceptionDispatchInfo.Throw(); } Debug.Assert(current.Schema != null); return(this); } // Create dictionaries Dictionary <int, SqlSchema> sqlSchemas = new Dictionary <int, SqlSchema>(); Dictionary <int, SqlType> typesByID = new Dictionary <int, SqlType>(); Dictionary <string, SqlType> typesByName = new Dictionary <string, SqlType>(StringComparer.InvariantCultureIgnoreCase); Dictionary <string, SqlProgramDefinition> programDefinitions = new Dictionary <string, SqlProgramDefinition>(StringComparer.InvariantCultureIgnoreCase); Dictionary <string, SqlTableDefinition> tables = new Dictionary <string, SqlTableDefinition>(StringComparer.InvariantCultureIgnoreCase); try { // Open a connection using (SqlConnection sqlConnection = new SqlConnection(ConnectionString)) { // ReSharper disable once PossibleNullReferenceException await sqlConnection.OpenAsync(cancellationToken).ConfigureAwait(false); Version version; if (!Version.TryParse(sqlConnection.ServerVersion, out version)) { throw new DatabaseSchemaException( () => Resources.DatabaseSchema_Load_CouldNotParseVersionInformation); } Debug.Assert(version != null); if (version.Major < 9) { throw new DatabaseSchemaException( () => Resources.DatabaseSchema_Load_VersionNotSupported, version); } string sql = version.Major == 9 ? SQLResources.RetrieveSchema9 : SQLResources.RetrieveSchema10; // Create the command first, as we will reuse on each connection. using ( SqlCommand command = new SqlCommand(sql, sqlConnection) { CommandType = CommandType.Text }) // Execute command using (SqlDataReader reader = // ReSharper disable once PossibleNullReferenceException await command.ExecuteReaderAsync(CommandBehavior.SequentialAccess, cancellationToken) .ConfigureAwait(false)) { /* * Load SQL Schemas */ while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false)) { SqlSchema sqlSchema = new SqlSchema(reader.GetInt32(0), reader.GetString(1)); sqlSchemas.Add(sqlSchema.ID, sqlSchema); } if (sqlSchemas.Count < 1) { throw new DatabaseSchemaException( () => Resources.DatabaseSchema_Load_CouldNotRetrieveSchemas); } /* * Load types */ if (!(await reader.NextResultAsync(cancellationToken).ConfigureAwait(false))) { throw new DatabaseSchemaException( () => Resources.DatabaseSchema_Load_RanOutOfResultsRetrievingTypes); } while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false)) { int schemaId = reader.GetInt32(0); SqlSchema sqlSchema; if (!sqlSchemas.TryGetValue(schemaId, out sqlSchema) || (sqlSchema == null)) { throw new DatabaseSchemaException( () => Resources.DatabaseSchema_Load_CouldNotFindSchema, schemaId); } int id = reader.GetInt32(1); string name = reader.GetString(2).ToLower(); SqlType baseType; if (reader.IsDBNull(3)) { baseType = null; } else { // NB SQL returns types in dependency order // i.e. base types are always seen first, so this code is much easier. int baseId = reader.GetInt32(3); typesByID.TryGetValue(baseId, out baseType); } short maxLength = reader.GetInt16(4); byte precision = reader.GetByte(5); byte scale = reader.GetByte(6); bool isNullable = reader.GetBoolean(7); bool isUserDefined = reader.GetBoolean(8); bool isCLR = reader.GetBoolean(9); bool isTable = reader.GetBoolean(10); // Create type SqlType type = isTable ? new SqlTableType( baseType, sqlSchema, name, new SqlTypeSize(maxLength, precision, scale), isNullable, isUserDefined, isCLR) : new SqlType( baseType, sqlSchema, name, new SqlTypeSize(maxLength, precision, scale), isNullable, isUserDefined, isCLR); // Add to dictionary typesByName.Add(type.FullName, type); if (!typesByName.ContainsKey(type.Name)) { typesByName.Add(type.Name, type); } typesByID.Add(id, type); } if (typesByName.Count < 1) { throw new DatabaseSchemaException( () => Resources.DatabaseSchema_Load_CouldNotRetrieveTypes); } /* * Load program definitions */ if (!(await reader.NextResultAsync(cancellationToken).ConfigureAwait(false))) { throw new DatabaseSchemaException( () => Resources.DatabaseSchema_Load_RanOutOfResultsRetrievingPrograms); } List <ProgramDefinitionData> programDefinitionData = new List <ProgramDefinitionData>(); while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false)) { SqlObjectType type; string typeString = reader.GetString(0) ?? string.Empty; if (!ExtendedEnum <SqlObjectType> .TryParse(typeString, true, out type)) { throw new DatabaseSchemaException( () => Resources.DatabaseSchema_Load_CouldNotFindTypeWhenLoadingPrograms, typeString); } int schemaId = reader.GetInt32(1); SqlSchema sqlSchema; if (!sqlSchemas.TryGetValue(schemaId, out sqlSchema)) { throw new DatabaseSchemaException( () => Resources.DatabaseSchema_Load_CouldNotFindSchemaWhenLoadingPrograms, schemaId); } string name = reader.GetString(2).ToLower(); // If we have a null ordinal, we have no parameters. if (reader.IsDBNull(3)) { programDefinitionData.Add(new ProgramDefinitionData(type, schemaId, name)); continue; } int ordinal = reader.GetInt32(3); string parameterName = reader.GetString(4).ToLower(); int typeId = reader.GetInt32(5); SqlType parameterType; if (!typesByID.TryGetValue(typeId, out parameterType) || (parameterType == null)) { throw new DatabaseSchemaException( () => Resources.DatabaseSchema_Load_ParameterTypeNotFound, parameterName, typeId, name); } short maxLength = reader.GetInt16(6); byte precision = reader.GetByte(7); byte scale = reader.GetByte(8); SqlTypeSize parameterSize = new SqlTypeSize(maxLength, precision, scale); bool isOutput = reader.GetBoolean(9); ParameterDirection parameterDirection; if (!isOutput) { parameterDirection = ParameterDirection.Input; } else if (parameterName == string.Empty) { parameterDirection = ParameterDirection.ReturnValue; } else { parameterDirection = ParameterDirection.InputOutput; } bool parameterIsReadOnly = reader.GetBoolean(10); programDefinitionData.Add( new ProgramDefinitionData( type, schemaId, name, ordinal, parameterName, parameterType, parameterSize, parameterDirection, parameterIsReadOnly)); } // Create unique program definitions. foreach (SqlProgramDefinition program in programDefinitionData // ReSharper disable once PossibleNullReferenceException .GroupBy(d => d.ToString()) .Select( g => { Debug.Assert(g != null); // Get columns ordered by ordinal. SqlProgramParameter[] parameters = g // ReSharper disable once PossibleNullReferenceException .Select(d => d.Parameter) .Where(p => p != null) .OrderBy(p => p.Ordinal) .ToArray(); ProgramDefinitionData first = g.First(); Debug.Assert(first != null); Debug.Assert(first.Name != null); Debug.Assert(sqlSchemas != null); SqlSchema sqlSchema; if (!sqlSchemas.TryGetValue(first.SchemaID, out sqlSchema)) { throw new DatabaseSchemaException( () => Resources .DatabaseSchema_Load_CouldNotFindSchemaLoadingTablesAndViews, first.SchemaID); } Debug.Assert(sqlSchema != null); return(new SqlProgramDefinition( first.Type, sqlSchema, first.Name, parameters)); })) { Debug.Assert(program != null); programDefinitions[program.FullName] = program; if (!programDefinitions.ContainsKey(program.Name)) { programDefinitions.Add(program.Name, program); } } /* * Load tables and views */ if (!(await reader.NextResultAsync(cancellationToken).ConfigureAwait(false))) { throw new DatabaseSchemaException( () => Resources.DatabaseSchema_Load_RanOutOfTablesAndViews); } // Read raw data in. List <TableDefinitionData> tableDefinitionData = new List <TableDefinitionData>(); while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false)) { SqlObjectType type; string typeString = reader.GetString(0) ?? string.Empty; if (!ExtendedEnum <SqlObjectType> .TryParse(typeString, true, out type)) { throw new DatabaseSchemaException( () => Resources.DatabaseSchema_Load_CouldNotFindObjectType, typeString); } int schemaId = reader.GetInt32(1); string name = reader.GetString(2).ToLower(); int ordinal = reader.GetInt32(3); string columnName = reader.GetString(4).ToLower(); int typeId = reader.GetInt32(5); SqlType sqlType; if (!typesByID.TryGetValue(typeId, out sqlType) || (sqlType == null)) { throw new DatabaseSchemaException( () => Resources.DatabaseSchema_Load_ColumnTypeNotFound, columnName, typeId, name); } short maxLength = reader.GetInt16(6); byte precision = reader.GetByte(7); byte scale = reader.GetByte(8); SqlTypeSize sqlTypeSize = new SqlTypeSize(maxLength, precision, scale); bool isNullable = reader.GetBoolean(9); int?tableType = reader.IsDBNull(10) ? null : (int?)reader.GetInt32(10); tableDefinitionData.Add( new TableDefinitionData( type, schemaId, name, ordinal, columnName, sqlType, sqlTypeSize, isNullable, tableType)); } // Create unique table definitions. foreach (SqlTableDefinition table in tableDefinitionData // ReSharper disable once PossibleNullReferenceException .GroupBy(d => d.ToString()) .Select( g => { Debug.Assert(g != null); // Get columns ordered by ordinal. SqlColumn[] columns = g // ReSharper disable PossibleNullReferenceException .Select(d => d.Column) .OrderBy(c => c.Ordinal) // ReSharper restore PossibleNullReferenceException .ToArray(); Debug.Assert(columns.Length > 0); TableDefinitionData first = g.First(); Debug.Assert(first != null); Debug.Assert(first.Name != null); Debug.Assert(sqlSchemas != null); SqlSchema sqlSchema; if (!sqlSchemas.TryGetValue(first.SchemaID, out sqlSchema)) { throw new DatabaseSchemaException( () => Resources .DatabaseSchema_Load_CouldNotFindSchemaLoadingTablesAndViews, first.SchemaID); } Debug.Assert(sqlSchema != null); SqlTableType tableType; if (first.TableTypeID != null) { Debug.Assert(typesByID != null); SqlType tType; if (!typesByID.TryGetValue(first.TableTypeID.Value, out tType)) { throw new DatabaseSchemaException( () => Resources.DatabaseSchema_Load_TableTypeNotFound, first.TableTypeID.Value, first.Name); } tableType = tType as SqlTableType; if (tableType == null) { throw new DatabaseSchemaException( () => Resources.DatabaseSchema_Load_TypeNotTableType, first.TableTypeID.Value, first.Name); } } else { tableType = null; } return(new SqlTableDefinition( first.Type, sqlSchema, first.Name, columns, tableType)); })) { Debug.Assert(table != null); tables[table.FullName] = table; if (!tables.ContainsKey(table.Name)) { tables.Add(table.Name, table); } } } } // Update the current schema. _current = new CurrentSchema(Schema.GetOrAdd(sqlSchemas, programDefinitions, tables, typesByName)); // Always return this return(this); } // In the event of an error we don't set the loaded flag - this allows retries. catch (DatabaseSchemaException databaseSchemaException) { // Capture the exception in the current schema. _current = new CurrentSchema(ExceptionDispatchInfo.Capture(databaseSchemaException)); throw; } catch (Exception exception) { // Wrap exception in Database exception. DatabaseSchemaException databaseSchemaException = new DatabaseSchemaException( exception, LoggingLevel.Critical, () => Resources.DatabaseSchema_Load_ErrorOccurred); // Capture the exception in the current schema. _current = new CurrentSchema(ExceptionDispatchInfo.Capture(databaseSchemaException)); throw databaseSchemaException; } } }