diff --git a/src/SQLProvider.Common/Operators.fs b/src/SQLProvider.Common/Operators.fs
index 612db768..2c1aa866 100644
--- a/src/SQLProvider.Common/Operators.fs
+++ b/src/SQLProvider.Common/Operators.fs
@@ -2,23 +2,43 @@ namespace FSharp.Data.Sql
open System.Linq
+///
+/// Represents SQL conditional operators used in WHERE clauses and query expressions.
+/// These operators are translated to their SQL equivalents when building database queries.
+///
[]
type ConditionOperator =
+ /// SQL LIKE operator for pattern matching with wildcards
| Like
+ /// SQL NOT LIKE operator for negated pattern matching
| NotLike
+ /// SQL equality operator (=)
| Equal
+ /// SQL inequality operator (<>)
| NotEqual
+ /// SQL greater than operator (>)
| GreaterThan
+ /// SQL less than operator (<)
| LessThan
+ /// SQL greater than or equal operator (>=)
| GreaterEqual
+ /// SQL less than or equal operator (<=)
| LessEqual
+ /// SQL IS NULL operator for checking null values
| IsNull
+ /// SQL IS NOT NULL operator for checking non-null values
| NotNull
+ /// SQL IN operator for membership testing
| In
+ /// SQL NOT IN operator for negated membership testing
| NotIn
+ /// SQL IN operator for nested subqueries
| NestedIn
+ /// SQL NOT IN operator for negated nested subqueries
| NestedNotIn
+ /// SQL EXISTS operator for subquery existence testing
| NestedExists
+ /// SQL NOT EXISTS operator for negated subquery existence testing
| NestedNotExists
with
override x.ToString() =
@@ -42,127 +62,273 @@ type ConditionOperator =
| NestedExists -> "EXISTS"
| NestedNotExists -> "NOT EXISTS"
+///
+/// Represents SQL aggregate operations for GROUP BY clauses and data summarization.
+/// Each operation includes the column name to aggregate.
+///
[]
type AggregateOperation = // Aggregate (column name if not default)
+/// Groups results by the specified key column
| KeyOp of key: string
+/// Finds the maximum value in the specified column
| MaxOp of max: string
+/// Finds the minimum value in the specified column
| MinOp of min: string
+/// Calculates the sum of values in the specified column
| SumOp of sum: string
+/// Calculates the average of values in the specified column
| AvgOp of avg: string
+/// Counts the number of rows in the specified column
| CountOp of count: string
+/// Counts the number of distinct values in the specified column
| CountDistOp of countDist: string
+/// Calculates the standard deviation of values in the specified column
| StdDevOp of std: string
+/// Calculates the variance of values in the specified column
| VarianceOp of var: string
+///
+/// Specifies where SELECT operations should be executed for query optimization.
+///
type SelectOperations =
+/// Execute the operation on the .NET client side
| DotNetSide = 0
+/// Execute the operation on the database server side
| DatabaseSide = 1
[]
module ColumnSchema =
type alias = string
+ ///
+ /// Represents WHERE clause conditions for SQL queries.
+ /// Supports complex boolean expressions with AND/OR operators and nested conditions.
+ ///
type Condition =
// this is (table alias * column name * operator * right hand value ) list * (the same again list)
// basically any AND or OR expression can have N terms and can have N nested condition children
// this is largely from my CRM type provider. I don't think in practice for the SQL provider
// you will ever have more than what is representable in a traditional binary expression tree, but
// changing it would be a lot of effort ;)
+ /// AND condition with a list of terms and optional nested conditions
| And of (alias * SqlColumnType * ConditionOperator * obj option) list * (Condition list) option
+ /// OR condition with a list of terms and optional nested conditions
| Or of (alias * SqlColumnType * ConditionOperator * obj option) list * (Condition list) option
+ /// Always evaluates to true
| ConstantTrue
+ /// Always evaluates to false
| ConstantFalse
+ /// Expression that cannot be translated to SQL
| NotSupported of System.Linq.Expressions.Expression
+ ///
+ /// Represents SQL functions and operations that can be applied to columns.
+ /// These operations are translated to database-specific SQL functions.
+ ///
and CanonicalOp =
//String functions
+ /// Extracts a substring from the specified position to the end
| Substring of SqlItemOrColumn
+ /// Extracts a substring with specified start position and length
| SubstringWithLength of SqlItemOrColumn*SqlItemOrColumn
+ /// Converts string to uppercase
| ToUpper
+ /// Converts string to lowercase
| ToLower
+ /// Removes leading and trailing whitespace
| Trim
+ /// Returns the length of a string
| Length
+ /// Replaces occurrences of a substring with another substring
| Replace of SqlItemOrColumn*SqlItemOrColumn
+ /// Returns the index of the first occurrence of a substring
| IndexOf of SqlItemOrColumn
+ /// Returns the index of a substring starting from a specified position
| IndexOfStart of SqlItemOrColumn*SqlItemOrColumn
// Date functions
+ /// Extracts the date part from a datetime value
| Date
+ /// Extracts the year from a datetime value
| Year
+ /// Extracts the month from a datetime value
| Month
+ /// Extracts the day from a datetime value
| Day
+ /// Extracts the hour from a datetime value
| Hour
+ /// Extracts the minute from a datetime value
| Minute
+ /// Extracts the second from a datetime value
| Second
+ /// Adds the specified number of years to a datetime value
| AddYears of SqlItemOrColumn
+ /// Adds the specified number of months to a datetime value
| AddMonths of int
+ /// Adds the specified number of days to a datetime value
| AddDays of SqlItemOrColumn
+ /// Adds the specified number of hours to a datetime value
| AddHours of float
+ /// Adds the specified number of minutes to a datetime value
| AddMinutes of SqlItemOrColumn
+ /// Adds the specified number of seconds to a datetime value
| AddSeconds of float
+ /// Calculates the difference in days between two datetime values
| DateDiffDays of SqlItemOrColumn
+ /// Calculates the difference in seconds between two datetime values
| DateDiffSecs of SqlItemOrColumn
// Numerical functions
+ /// Returns the absolute value of a number
| Abs
+ /// Returns the smallest integer greater than or equal to a number (ceiling)
| Ceil
+ /// Returns the largest integer less than or equal to a number (floor)
| Floor
+ /// Rounds a number to the nearest integer
| Round
+ /// Rounds a number to the specified number of decimal places
| RoundDecimals of int
+ /// Truncates a number to its integer part
| Truncate
+ /// Returns the square root of a number
| Sqrt
+ /// Returns the sine of an angle (in radians)
| Sin
+ /// Returns the cosine of an angle (in radians)
| Cos
+ /// Returns the tangent of an angle (in radians)
| Tan
+ /// Returns the arcsine of a number
| ASin
+ /// Returns the arccosine of a number
| ACos
+ /// Returns the arctangent of a number
| ATan
+ /// Raises a number to the power of another number
| Pow of SqlItemOrColumn
+ /// Raises a number to a constant power
| PowConst of SqlItemOrColumn
+ /// Returns the greatest value among the provided arguments
| Greatest of SqlItemOrColumn
+ /// Returns the smallest value among the provided arguments
| Least of SqlItemOrColumn
// Other
+ /// Basic mathematical operation with a constant value
| BasicMath of string*obj //operation, constant
+ /// Left-side basic mathematical operation with a constant value
| BasicMathLeft of string*obj //operation, constant
+ /// Basic mathematical operation between columns
| BasicMathOfColumns of string*string*SqlColumnType //operation, alias, column
+ /// SQL CASE expression with condition and false value
| CaseSql of Condition * SqlItemOrColumn // operation, if-false
+ /// SQL CASE expression with negated condition and true value
| CaseNotSql of Condition * SqlItemOrColumn // operation, if-true
+ /// SQL CASE expression with condition and two constant values
| CaseSqlPlain of Condition * obj * obj // with 2 constants
+ /// Casts a value to VARCHAR type
| CastVarchar
+ /// Casts a value to INTEGER type
| CastInt
+ ///
+ /// Represents a column with optional operations applied to it.
+ ///
and SqlColumnType =
+ /// A key column used for grouping or joining
| KeyColumn of string
+ /// A column with a canonical operation applied
| CanonicalOperation of CanonicalOp * SqlColumnType
+ /// A column used in aggregate operations
| GroupColumn of AggregateOperation * SqlColumnType
+ ///
+ /// Represents either a database column or a constant value in SQL expressions.
+ ///
// More recursion, because you mighn want to say e.g.
// where (x.Substring(x.IndexOf("."), (x.Length-x.IndexOf("."))
and SqlItemOrColumn =
+ /// A database column with table alias and column type
| SqlCol of string*SqlColumnType //alias*column
+ /// A constant value to be used in SQL expressions
| SqlConstant of obj
+ ///
+ /// Represents parameters for SQL projections (SELECT clause elements).
+ ///
type ProjectionParameter =
+ /// A direct entity column reference
| EntityColumn of string
+ /// A column with operations applied for computed expressions
| OperationColumn of string*SqlColumnType//name*operations
// Dummy operators, these are placeholders that are replaced in the expression tree traversal with special server-side operations such as In, Like
// The operators here are used to force the compiler to statically check against the correct types
+///
+/// Contains custom SQL operators for use in query expressions.
+/// These operators are translated to their SQL equivalents during query compilation.
+///
[]
module Operators =
- //// In
+ ///
+ /// SQL IN operator. Tests if a value exists in a sequence.
+ /// Example: where (customer.Country |=| ["USA"; "Canada"])
+ ///
+ /// The value to test
+ /// The sequence to search in
+ /// Always false at compile time - replaced with SQL IN during query execution
let (|=|) (a:'a) (b:'a seq) = false
- /// Not In
+
+ ///
+ /// SQL NOT IN operator. Tests if a value does not exist in a sequence.
+ /// Example: where (customer.Country |<>| ["USA"; "Canada"])
+ ///
+ /// The value to test
+ /// The sequence to search in
+ /// Always false at compile time - replaced with SQL NOT IN during query execution
let (|<>|) (a:'a) (b:'a seq) = false
- /// Like
+
+ ///
+ /// SQL LIKE operator for pattern matching with wildcards.
+ /// Example: where (customer.Name =% "John%")
+ ///
+ /// The value to test
+ /// The pattern to match (use % and _ as wildcards)
+ /// Always false at compile time - replaced with SQL LIKE during query execution
let (=%) (a:'a) (b:string) = false
- /// Not Like
+
+ ///
+ /// SQL NOT LIKE operator for negated pattern matching.
+ /// Example: where (customer.Name <>% "John%")
+ ///
+ /// The value to test
+ /// The pattern to reject
+ /// Always false at compile time - replaced with SQL NOT LIKE during query execution
let (<>%) (a:'a) (b:string) = false
- /// Left join
+
+ /// Left join helper function for internal use
let private leftJoin (a:'a) = a
+
+ ///
+ /// Left outer join operator. Performs a left join on a related table.
+ /// Example: for customer in ctx.Main.Customers do
+ /// for order in (!!) customer.``main.Orders by CustomerID`` do
+ ///
+ /// The related table queryable
+ /// A queryable that performs a left outer join
let (!!) (a:IQueryable<'a>) = query { for x in a do select (leftJoin x) }
- /// Standard Deviation
+ ///
+ /// Calculates the standard deviation of numeric values in a column.
+ /// Used in aggregate queries with groupBy.
+ ///
+ /// The column value
+ /// Always 1m at compile time - replaced with SQL STDDEV during query execution
let StdDev (a:'a) = 1m
- /// Variance
+ ///
+ /// Calculates the variance of numeric values in a column.
+ /// Used in aggregate queries with groupBy.
+ ///
+ /// The column value
+ /// Always 1m at compile time - replaced with SQL VARIANCE during query execution
let Variance (a:'a) = 1m
diff --git a/src/SQLProvider.Common/SqlRuntime.Async.fs b/src/SQLProvider.Common/SqlRuntime.Async.fs
index 9c8db3e3..3abf82cd 100644
--- a/src/SQLProvider.Common/SqlRuntime.Async.fs
+++ b/src/SQLProvider.Common/SqlRuntime.Async.fs
@@ -8,8 +8,18 @@ open FSharp.Data.Sql.Common
open FSharp.Data.Sql.Patterns
open System.Linq
+///
+/// Provides asynchronous operations for SQL queries.
+/// Use these functions when you need to execute queries asynchronously to avoid blocking threads.
+///
module AsyncOperations =
+ ///
+ /// Executes a queryable asynchronously and returns the results as a sequence.
+ /// This is useful for executing large queries without blocking the calling thread.
+ ///
+ /// The queryable to execute
+ /// A task that yields a sequence of results
let executeAsync (s:Linq.IQueryable<'T>) =
let inline yieldseq (en: IEnumerator<'T>) =
seq {
diff --git a/src/SQLProvider.Common/SqlRuntime.Common.fs b/src/SQLProvider.Common/SqlRuntime.Common.fs
index 1b80d0f3..76b8492c 100644
--- a/src/SQLProvider.Common/SqlRuntime.Common.fs
+++ b/src/SQLProvider.Common/SqlRuntime.Common.fs
@@ -16,27 +16,57 @@ open Microsoft.FSharp.Reflection
open System.Collections.Concurrent
open System.Runtime.Serialization
+///
+/// Specifies the database provider type for the SQL type provider.
+/// Each provider has its own specific implementation for SQL generation and data type mapping.
+///
type DatabaseProviderTypes =
+ /// Microsoft SQL Server using SqlClient provider
| MSSQLSERVER = 0
+ /// SQLite database using System.Data.SQLite or Microsoft.Data.Sqlite
| SQLITE = 1
+ /// PostgreSQL using Npgsql provider
| POSTGRESQL = 2
+ /// MySQL using MySql.Data or MySqlConnector provider
| MYSQL = 3
+ /// Oracle Database using Oracle.ManagedDataAccess provider
| ORACLE = 4
+ /// Microsoft Access using OleDb provider
| MSACCESS = 5
+ /// Generic ODBC provider for various databases
| ODBC = 6
+ /// Firebird database using FirebirdSql.Data.FirebirdClient
| FIREBIRD = 7
+ /// Microsoft SQL Server with dynamic schema loading
| MSSQLSERVER_DYNAMIC = 8
+ /// Microsoft SQL Server using SSDT/DacPac for schema
| MSSQLSERVER_SSDT = 9
+ /// DuckDB using DuckDB.NET provider
| DUCKDB = 10
+ /// External/custom database provider
| EXTERNAL = 11
-type RelationshipDirection = Children = 0 | Parents = 1
+/// Specifies the direction for relationship navigation.
+type RelationshipDirection =
+ /// Navigate to child records (foreign key relationships)
+ Children = 0
+ /// Navigate to parent records (primary key relationships)
+ | Parents = 1
+///
+/// Specifies how to handle case sensitivity when generating table and column names.
+///
type CaseSensitivityChange =
+ /// Keep original casing from the database
| ORIGINAL = 0
+ /// Convert all names to uppercase
| TOUPPER = 1
+ /// Convert all names to lowercase
| TOLOWER = 2
+///
+/// Specifies how to represent nullable database columns in the generated types.
+///
type NullableColumnType =
/// Nullable types are just Unchecked default. (Old false.)
| NO_OPTION = 0
@@ -45,7 +75,11 @@ type NullableColumnType =
/// ValueOption is more performant.
| VALUE_OPTION = 2
+///
+/// Specifies the quote character style for ODBC connections when dealing with table and column names containing spaces or special characters.
+///
type OdbcQuoteCharacter =
+ /// Use default quoting for the ODBC driver
| DEFAULT_QUOTE = 0
/// MySQL/Postgre style: `alias`
| GRAVE_ACCENT = 1
@@ -58,6 +92,10 @@ type OdbcQuoteCharacter =
/// Single quote: 'alias'
| APHOSTROPHE = 5
+///
+/// Specifies which SQLite library to use for connections.
+/// Different libraries may have different capabilities and platform support.
+///
type SQLiteLibrary =
/// .NET Framework default
| SystemDataSQLite = 0
@@ -68,8 +106,16 @@ type SQLiteLibrary =
/// Microsoft.Data.Sqlite. May support .NET Standard 2.0 contract in the future.
| MicrosoftDataSqlite = 3
+///
+/// Contains events for monitoring SQL query execution and debugging.
+/// Use these events to log, debug, or analyze the SQL queries generated by the type provider.
+///
module public QueryEvents =
+ ///
+ /// Contains information about a SQL command being executed, including the command text,
+ /// parameters, and connection information for debugging and monitoring purposes.
+ ///
type SqlEventData = {
/// The text of the SQL command being executed.
Command: string
@@ -112,8 +158,18 @@ module public QueryEvents =
let private sqlEvent = Event()
- /// This event fires immediately before the execution of every generated query.
+ ///
+ /// Event that fires immediately before the execution of every generated query.
/// Listen to this event to display or debug the content of your queries.
+ /// This is useful for logging, performance monitoring, and debugging SQL generation.
+ ///
+ ///
+ ///
+ /// QueryEvents.SqlQueryEvent.Add(fun event ->
+ /// printfn "Executing: %s" event.Command
+ /// printfn "Parameters: %A" event.Parameters)
+ ///
+ ///
[]
let SqlQueryEvent = sqlEvent.Publish
@@ -150,13 +206,26 @@ module public QueryEvents =
let internal PublishExpression(e) = expressionEvent.Trigger(e)
[]
+///
+/// Represents the current state of a database entity for change tracking purposes.
+/// Used internally by the SQL provider to determine what operations need to be performed during SubmitUpdates().
+///
type EntityState =
+ /// Entity has not been modified since it was loaded
| Unchanged
+ /// Entity is new and should be inserted into the database
| Created
+ /// Entity has been modified; contains list of changed column names
| Modified of string list
+ /// Entity is marked for deletion (soft delete)
| Delete
+ /// Entity has been deleted from the database
| Deleted
+///
+/// Specifies how to handle conflicts when inserting records with duplicate primary keys.
+/// Currently supported only on databases that have UPSERT capabilities.
+///
[]
type OnConflict =
/// Throws an exception if the primary key already exists (default behaviour).
@@ -168,18 +237,40 @@ type OnConflict =
/// Currently supported only on PostgreSQL 9.5+
| DoNothing
+///
+/// Attribute for mapping entity properties to database columns with different names.
+/// Use this when your database column name differs from your preferred F# property name.
+///
+///
+///
+/// [<MappedColumn("customer_id")>]
+/// member x.CustomerId = x.GetColumn<int>("customer_id")
+///
+///
type MappedColumnAttribute(name: string) =
inherit Attribute()
+ /// Gets the database column name this property maps to
member x.Name with get() = name
+/// Represents a result set as a sequence of rows, where each row is an array of column name-value pairs.
type ResultSet = seq<(string * obj)[]>
+
+/// Represents different types of result sets that can be returned from stored procedures.
type ReturnSetType =
+ /// A single scalar value with name and value
| ScalarResultSet of string * obj
+ /// A result set with name and data rows
| ResultSet of string * ResultSet
+
+/// Represents different types of return values from stored procedures and functions.
type ReturnValueType =
+ /// No return value (void)
| Unit
+ /// A single scalar value
| Scalar of string * obj
+ /// A single result set
| SingleResultSet of string * ResultSet
+ /// Multiple result sets
| Set of seq
/// Interface to hide internal members that are typically not needed by the user
@@ -478,6 +569,11 @@ type SqlEntity(dc: ISqlDataContext, tableName, columns: ColumnLookup, activeColu
override __.ShouldSerializeValue(_) = false })
|> Seq.cast |> Seq.toArray )
+///
+/// The main interface for SQL data context operations. This interface provides all the core functionality
+/// for querying, creating, updating, and deleting data through the SQL type provider.
+/// Users typically access this through the generated GetDataContext() method.
+///
and ISqlDataContext =
/// Connection string to database
abstract ConnectionString : string
@@ -520,8 +616,12 @@ and ISqlDataContext =
/// Context meant to be read operations only
abstract IsReadOnly : bool
-/// This is publically exposed and used in the runtime
+///
+/// Interface implemented by generated types that provide access to the underlying data context.
+/// This allows access to the ISqlDataContext for advanced operations.
+///
type IWithDataContext =
+ /// Gets the underlying SQL data context
abstract DataContext : ISqlDataContext
/// LinkData is for joins with SelectMany
diff --git a/src/SQLProvider.Common/SqlRuntime.Transactions.fs b/src/SQLProvider.Common/SqlRuntime.Transactions.fs
index 0de238dc..a0eebd82 100644
--- a/src/SQLProvider.Common/SqlRuntime.Transactions.fs
+++ b/src/SQLProvider.Common/SqlRuntime.Transactions.fs
@@ -2,24 +2,43 @@ namespace FSharp.Data.Sql.Transactions
open System
-/// Corresponds to the System.Transactions.IsolationLevel.
+///
+/// Transaction isolation level for database operations.
+/// Corresponds to the System.Transactions.IsolationLevel but provides SQL provider specific functionality.
+///
type IsolationLevel =
+ /// Highest isolation level - transactions are completely isolated from each other
| Serializable = 0
+ /// Prevents dirty reads and non-repeatable reads, but phantom reads can occur
| RepeatableRead = 1
+ /// Prevents dirty reads but allows non-repeatable reads and phantom reads (default for most databases)
| ReadCommitted = 2
+ /// Allows dirty reads, non-repeatable reads, and phantom reads (fastest but least safe)
| ReadUncommitted = 3
+ /// SQL Server specific - provides statement-level read consistency using versioning
| Snapshot = 4
+ /// Allows any level of isolation, including overlapping changes (SQL Server specific)
| Chaos = 5
+ /// Use the default isolation level of the database
| Unspecified = 6
+ /// Special value to indicate that no transaction should be created
| DontCreateTransaction = 99
+///
+/// Configuration options for database transactions.
/// Corresponds to the System.Transactions.TransactionOptions.
+///
[]
type TransactionOptions = {
+ /// Maximum time the transaction can remain active before timing out
Timeout : TimeSpan
+ /// The isolation level for the transaction
IsolationLevel : IsolationLevel
}
+///
+/// Utility functions for converting between different transaction isolation level representations.
+///
module TransactionUtils =
let internal toSystemTransactionsIsolationLevel isolationLevel =
match isolationLevel with
@@ -32,6 +51,12 @@ module TransactionUtils =
| IsolationLevel.Unspecified -> System.Transactions.IsolationLevel.Unspecified
| _ -> failwithf "Unhandled IsolationLevel value: %A." isolationLevel
+ ///
+ /// Converts SQL provider isolation level to System.Data.IsolationLevel.
+ /// Use this when you need to work directly with ADO.NET connection transactions.
+ ///
+ /// The SQL provider isolation level
+ /// The corresponding System.Data.IsolationLevel
let toSystemDataIsolationLevel isolationLevel =
match isolationLevel with
| IsolationLevel.Serializable -> System.Data.IsolationLevel.Serializable
diff --git a/src/SQLProvider.Common/SqlSchema.fs b/src/SQLProvider.Common/SqlSchema.fs
index 2f1812a0..a322d1c2 100644
--- a/src/SQLProvider.Common/SqlSchema.fs
+++ b/src/SQLProvider.Common/SqlSchema.fs
@@ -17,11 +17,19 @@ module internal Patterns =
| _ -> ValueNone
else ValueNone
+///
+/// Represents the mapping between database types and .NET CLR types.
+/// Used to translate between database-specific types and .NET types during query execution.
+///
[]
type TypeMapping =
+ /// The database provider's specific type name (e.g., "varchar", "int")
{ ProviderTypeName: string voption
+ /// The corresponding .NET CLR type name
ClrType: string
+ /// The database provider's numeric type identifier
ProviderType: int voption
+ /// The standard .NET DbType enumeration value
DbType: DbType }
with
static member Create(?clrType, ?dbType, ?providerTypeName, ?providerType) =
@@ -30,12 +38,21 @@ type TypeMapping =
ProviderTypeName = match providerTypeName with Some x -> ValueSome x | None -> ValueNone
ProviderType = match providerType with Some x -> ValueSome x | None -> ValueNone }
+///
+/// Represents a parameter used in SQL queries, stored procedures, or functions.
+/// Contains all metadata needed to properly bind .NET values to database parameters.
+///
[]
type QueryParameter =
+ /// The parameter name as used in the SQL query
{ Name: string
+ /// Type mapping information for converting between .NET and database types
TypeMapping: TypeMapping
+ /// Whether the parameter is input, output, or bidirectional
Direction: ParameterDirection
+ /// Maximum length for string/binary parameters
Length: int voption
+ /// Position of the parameter in the parameter list (zero-based)
Ordinal: int }
with
static member Create(name, ordinal, ?typeMapping, ?direction, ?length) =
@@ -45,15 +62,27 @@ type QueryParameter =
Direction = defaultArg direction ParameterDirection.Input
Length = match length with Some x -> ValueSome x | None -> ValueNone }
+///
+/// Represents a database table column with its metadata and characteristics.
+/// Contains all information needed to generate typed properties and handle data conversion.
+///
[]
type Column =
+ /// The column name as it appears in the database
{ Name: string
+ /// Type mapping information for converting between .NET and database types
TypeMapping: TypeMapping
+ /// True if this column is part of the table's primary key
IsPrimaryKey: bool
+ /// True if this column can contain NULL values
IsNullable: bool
+ /// True if this column has an auto-increment/identity specification
IsAutonumber: bool
+ /// True if this column has a default value defined
HasDefault: bool
+ /// True if this column is computed/calculated by the database
IsComputed: bool
+ /// Additional type information specific to the database provider
TypeInfo: string voption }
with
static member FromQueryParameter(q: QueryParameter) =
@@ -66,19 +95,36 @@ type Column =
IsComputed = false
TypeInfo = ValueNone }
+/// Lookup table for quickly finding column information by name.
type ColumnLookup = Map
+///
+/// Represents a foreign key relationship between two database tables.
+/// Used for automatic navigation property generation and join optimization.
+///
[]
type Relationship =
+ /// A descriptive name for this relationship
{ Name: string
+ /// The name of the table containing the primary key
PrimaryTable: string
+ /// The column name of the primary key
PrimaryKey: string
+ /// The name of the table containing the foreign key
ForeignTable: string
+ /// The column name of the foreign key
ForeignKey: string }
+///
+/// Represents the name of a stored procedure, including schema and package information.
+/// Handles different database naming conventions (e.g., Oracle packages, SQL Server schemas).
+///
type SprocName =
+ /// The procedure name
{ ProcName: string
+ /// The schema or owner name
Owner: string
+ /// The package name (primarily for Oracle)
PackageName: string }
with
member x.ToList() =
@@ -117,9 +163,16 @@ type Sproc =
typedefof.GetNestedTypes(BindingFlags.Public ||| BindingFlags.NonPublic)
|> Array.filter Microsoft.FSharp.Reflection.FSharpType.IsUnion
+///
+/// Represents a database table with its schema information.
+/// Provides methods for generating properly quoted table names for different database providers.
+///
type Table =
+ /// The schema name (may be empty for databases that don't use schemas)
{ Schema: string
+ /// The table name
Name: string
+ /// The table type (e.g., "TABLE", "VIEW", "SYSTEM TABLE")
Type: string }
with
static member CreateQuotedFullName(schema, name, startQuote, endQuote) =
diff --git a/src/SQLProvider.Common/Ssdt.DacpacParser.fs b/src/SQLProvider.Common/Ssdt.DacpacParser.fs
index 12dccc5c..066f4468 100644
--- a/src/SQLProvider.Common/Ssdt.DacpacParser.fs
+++ b/src/SQLProvider.Common/Ssdt.DacpacParser.fs
@@ -1,50 +1,94 @@
+///
+/// Module for parsing SQL Server Data Tools (SSDT) .dacpac files to extract database schema information.
+/// Provides functionality to read and parse the model.xml file contained within .dacpac packages.
+///
module FSharp.Data.Sql.Ssdt.DacpacParser
open System
open System.Xml
open System.IO.Compression
+/// Represents a database table column from SSDT schema.
type [] SsdtColumn = {
+ /// The fully qualified column name (schema.table.column)
FullName: string
+ /// The column name without qualification
Name: string
+ /// Extended description/comment for the column
Description: string
+ /// The SQL data type (e.g., varchar, int, datetime)
DataType: string
+ /// True if the column allows NULL values
AllowNulls: bool
+ /// True if the column is an identity/auto-increment column
IsIdentity: bool
+ /// True if the column has a default value constraint
HasDefault: bool
+ /// True if the column is computed/calculated
ComputedColumn: bool
}
+
+/// Represents a view column from SSDT schema.
type [] SsdtViewColumn = {
+ /// The fully qualified column name
FullName: string
+ /// Optional reference path for complex column definitions
ColumnRefPath: string voption
}
+
+/// Represents a comment annotation for schema documentation.
type [] CommentAnnotation = {
+ /// The column name the annotation applies to
Column: string
+ /// The data type information
DataType: string
+ /// Optional nullability information
Nullability: string voption
}
+
+/// Represents a database view from SSDT schema.
type SsdtView = {
+ /// The fully qualified view name (schema.view)
FullName: string
+ /// The schema name
Schema: string
+ /// The view name without schema
Name: string
+ /// Array of regular columns in the view
Columns: SsdtViewColumn array
+ /// Array of dynamically generated columns
DynamicColumns: SsdtViewColumn array
+ /// Array of comment annotations for documentation
Annotations: CommentAnnotation array
}
+
+/// Represents a column in a constraint definition.
type [] ConstraintColumn = {
+ /// The fully qualified column name
FullName: string
+ /// The column name without qualification
Name: string
}
+
+/// Represents a table reference in relationship definitions.
type RefTable = {
+ /// The fully qualified table name
FullName: string
+ /// The schema name
Schema: string
+ /// The table name without schema
Name: string
+ /// Array of columns involved in the reference
Columns: ConstraintColumn array
}
+/// Represents a foreign key relationship between tables.
type SsdtRelationship = {
+ /// The name of the relationship/constraint
Name: string
+ /// The table that defines the primary key
DefiningTable: RefTable
+ /// The table that contains the foreign key
ForeignTable: RefTable
}
@@ -138,7 +182,12 @@ module RegexParsers =
)
|> Seq.toArray
-/// Extracts model.xml from the given .dacpac file path.
+///
+/// Extracts the model.xml file from a SQL Server Data Tools (SSDT) .dacpac package.
+/// The model.xml contains the complete database schema definition.
+///
+/// Path to the .dacpac file
+/// The XML content of the model.xml file as a string
let extractModelXml (dacPacPath: string) =
use stream = new IO.FileStream(dacPacPath, IO.FileMode.Open, IO.FileAccess.Read)
use zip = new ZipArchive(stream, ZipArchiveMode.Read, false)
@@ -147,7 +196,13 @@ let extractModelXml (dacPacPath: string) =
use rdr = new IO.StreamReader(modelStream)
rdr.ReadToEnd()
-/// Returns a doc and node/nodes ns helper fns
+///
+/// Creates an XML document with namespace support and returns helper functions for node selection.
+/// This is specifically designed for parsing SSDT model.xml files with their Microsoft namespace.
+///
+/// The XML namespace URI
+/// The XML content to parse
+/// A tuple containing (XmlDocument, single node selector function, multiple nodes selector function)
let toXmlNamespaceDoc ns xml =
let doc = XmlDocument()
let nsMgr = XmlNamespaceManager(doc.NameTable)
@@ -162,6 +217,12 @@ let toXmlNamespaceDoc ns xml =
doc, node, nodes
+///
+/// Safely extracts an XML attribute value from a node, returning None if the attribute doesn't exist.
+///
+/// The attribute name to extract
+/// The XML node to extract from
+/// Some(attribute value) if found, None otherwise
let attMaybe (nm: string) (node: XmlNode) =
if isNull node.Attributes then None
else
@@ -170,10 +231,22 @@ let attMaybe (nm: string) (node: XmlNode) =
|> Seq.tryFind (fun a -> a.Name = nm)
|> Option.map (fun a -> a.Value)
+///
+/// Extracts an XML attribute value from a node, returning an empty string if the attribute doesn't exist.
+/// This is a convenience function for cases where a default empty value is acceptable.
+///
+/// The attribute name to extract
+/// The XML node to extract from
+/// The attribute value, or empty string if not found
let att (nm: string) (node: XmlNode) =
attMaybe nm node |> Option.defaultValue ""
-/// Parses the xml that is extracted from a .dacpac file.
+///
+/// Parses the model.xml content extracted from a SQL Server Data Tools (SSDT) .dacpac file.
+/// Extracts database schema information including tables, views, columns, relationships, and stored procedures.
+///
+/// The XML content from the model.xml file
+/// A parsed representation of the database schema
let parseXml(xml: string) =
let removeBrackets (s: string) = s.Replace("[", "").Replace("]", "")