-
-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #101 from RJSonnenberg/main
Add MySql support to SqlHydra.Cli
- Loading branch information
Showing
6 changed files
with
270 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
module SqlHydra.MySql.AppInfo | ||
|
||
open SqlHydra.Domain | ||
|
||
let info = | ||
{ | ||
AppInfo.Name = "SqlHydra.MySql" | ||
AppInfo.DefaultReaderType = "System.Data.Common.DbDataReader" // "System.Data.IDataReader" | ||
AppInfo.DefaultProvider = "MySql.Data" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
module SqlHydra.MySql.MySqlDataTypes | ||
|
||
open System.Data | ||
open MySql.Data.MySqlClient | ||
open SqlHydra.Domain | ||
|
||
let private r : System.Data.Common.DbDataReader = null | ||
|
||
/// A list of supported column type mappings | ||
let supportedTypeMappings = // https://dev.mysql.com/doc/refman/9.0/en/data-types.html | ||
[// ColumnTypeAlias ClrType DbType ProviderDbType | ||
"bit", "int16", DbType.Int16, Some (nameof MySqlDbType.Bit), nameof r.GetInt16 | ||
"tinyint", "int16", DbType.Int16, Some (nameof MySqlDbType.Int16), nameof r.GetInt16 | ||
"bool", "int16", DbType.Boolean, Some (nameof MySqlDbType.Int16), nameof r.GetBoolean | ||
"boolean", "int16", DbType.Boolean, Some (nameof MySqlDbType.Int16), nameof r.GetBoolean | ||
"smallint", "int16", DbType.Int16, Some (nameof MySqlDbType.Int16), nameof r.GetInt16 | ||
"mediumint", "int", DbType.Int32, Some (nameof MySqlDbType.Int24), nameof r.GetInt32 | ||
"int", "int", DbType.Int32, Some (nameof MySqlDbType.Int32), nameof r.GetInt32 | ||
"integer", "int", DbType.Int32, Some (nameof MySqlDbType.Int32), nameof r.GetInt32 | ||
"bigint", "int64", DbType.Int64, Some (nameof MySqlDbType.Int64), nameof r.GetInt64 | ||
"decimal", "decimal", DbType.Decimal, Some (nameof MySqlDbType.Decimal), nameof r.GetDecimal | ||
"dec", "decimal", DbType.Decimal, Some (nameof MySqlDbType.Decimal), nameof r.GetDecimal | ||
"float", "float", DbType.Single, Some (nameof MySqlDbType.Float), nameof r.GetFloat | ||
"double", "double", DbType.Double, Some (nameof MySqlDbType.Double), nameof r.GetDouble | ||
"double precision", "double", DbType.Double, Some (nameof MySqlDbType.Double), nameof r.GetDouble | ||
"char", "string", DbType.String, Some (nameof MySqlDbType.String), nameof r.GetString | ||
"varchar", "string", DbType.String, Some (nameof MySqlDbType.VarChar), nameof r.GetString | ||
"nvarchar", "string", DbType.String, Some (nameof MySqlDbType.VarChar), nameof r.GetString | ||
"binary", "byte[]", DbType.Binary, Some (nameof MySqlDbType.Binary), nameof r.GetFieldValue | ||
"char BYTE", "byte[]", DbType.Binary, Some (nameof MySqlDbType.Binary), nameof r.GetFieldValue | ||
"varbinary", "byte[]", DbType.Binary, Some (nameof MySqlDbType.VarBinary), nameof r.GetFieldValue | ||
"tinyblob", "byte[]", DbType.Binary, Some (nameof MySqlDbType.TinyBlob), nameof r.GetFieldValue | ||
"blob", "byte[]", DbType.Binary, Some (nameof MySqlDbType.Blob), nameof r.GetFieldValue | ||
"mediumblob", "byte[]", DbType.Binary, Some (nameof MySqlDbType.MediumBlob), nameof r.GetFieldValue | ||
"longblob", "byte[]", DbType.Binary, Some (nameof MySqlDbType.LongBlob), nameof r.GetFieldValue | ||
"tinytext", "string", DbType.String, Some (nameof MySqlDbType.TinyText), nameof r.GetString | ||
"text", "string", DbType.String, Some (nameof MySqlDbType.Text), nameof r.GetString | ||
"mediumtext", "string", DbType.String, Some (nameof MySqlDbType.MediumText), nameof r.GetString | ||
"longtext", "string", DbType.String, Some (nameof MySqlDbType.LongText), nameof r.GetString | ||
"enum", "string", DbType.String, Some (nameof MySqlDbType.Enum), nameof r.GetString | ||
"set", "string", DbType.String, Some (nameof MySqlDbType.Set), nameof r.GetString | ||
"json", "string", DbType.String, Some (nameof MySqlDbType.JSON), nameof r.GetString | ||
"date", "System.DateOnly", DbType.DateTime, Some (nameof MySqlDbType.Date), "GetDateOnly" | ||
"time", "System.TimeOnly", DbType.Time, Some (nameof MySqlDbType.Time), "GetTimeOnly" | ||
"datetime", "System.DateTime", DbType.DateTime, Some (nameof MySqlDbType.DateTime), nameof r.GetDateTime | ||
"timestamp", "System.DateTime", DbType.DateTime, Some (nameof MySqlDbType.Timestamp), nameof r.GetDateTime | ||
"year", "int16", DbType.Int16, Some (nameof MySqlDbType.Year), nameof r.GetInt16 | ||
// skipped unsupported | ||
"bool", "bool", DbType.Boolean, Some (nameof MySqlDbType.Int16), nameof r.GetBoolean | ||
"boolean", "bool", DbType.Boolean, Some (nameof MySqlDbType.Int16), nameof r.GetBoolean | ||
"float4", "float", DbType.Single, Some (nameof MySqlDbType.Float), nameof r.GetFloat | ||
"float8", "double", DbType.Double, Some (nameof MySqlDbType.Double), nameof r.GetDouble | ||
"numeric", "decimal", DbType.Decimal, Some (nameof MySqlDbType.Decimal), nameof r.GetDecimal | ||
"long", "string", DbType.String, Some (nameof MySqlDbType.MediumText), nameof r.GetString | ||
] | ||
|
||
let typeMappingsByName = | ||
supportedTypeMappings | ||
|
||
|> List.map (fun (columnTypeAlias, clrType, dbType, providerDbType, readerMethod) -> | ||
columnTypeAlias, | ||
{ | ||
TypeMapping.ColumnTypeAlias = columnTypeAlias | ||
TypeMapping.ClrType = clrType | ||
TypeMapping.DbType = dbType | ||
TypeMapping.ProviderDbType = providerDbType | ||
TypeMapping.ReaderMethod = readerMethod | ||
} | ||
) | ||
|> Map.ofList | ||
|
||
let tryFindTypeMapping (providerTypeName: string) = | ||
typeMappingsByName.TryFind (providerTypeName.ToLower().Trim()) | ||
|
||
let primitiveTypeReaders = | ||
supportedTypeMappings | ||
|> List.map(fun (_, clrType, _, _, readerMethod) -> | ||
{ PrimitiveTypeReader.ClrType = clrType; PrimitiveTypeReader.ReaderMethod = readerMethod } | ||
) | ||
|> List.distinctBy (fun ptr -> ptr.ClrType) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
module SqlHydra.MySql.MySqlSchemaProvider | ||
|
||
open System.Data | ||
open MySql.Data | ||
open SqlHydra.Domain | ||
open SqlHydra | ||
|
||
let getSchema (cfg: Config) : Schema = | ||
use conn = new MySqlClient.MySqlConnection(cfg.ConnectionString) | ||
conn.Open() | ||
|
||
let sTables = conn.GetSchema("Tables", cfg.Filters.TryGetRestrictionsByKey("Tables")) | ||
let sColumns = conn.GetSchema("Columns", cfg.Filters.TryGetRestrictionsByKey("Columns")) | ||
|
||
let pks = | ||
let sql = | ||
""" | ||
SELECT | ||
tc.table_schema, | ||
tc.constraint_name, | ||
tc.table_name, | ||
kcu.column_name | ||
FROM | ||
information_schema.table_constraints AS tc | ||
JOIN information_schema.key_column_usage AS kcu | ||
ON tc.constraint_name = kcu.constraint_name | ||
AND tc.table_schema = kcu.table_schema | ||
WHERE | ||
tc.constraint_type = 'PRIMARY KEY'; | ||
""" | ||
|
||
use cmd = new MySqlClient.MySqlCommand(sql, conn) | ||
use rdr = cmd.ExecuteReader() | ||
[ | ||
while rdr.Read() do | ||
rdr.["TABLE_SCHEMA"] :?> string, | ||
rdr.["TABLE_NAME"] :?> string, | ||
rdr.["COLUMN_NAME"] :?> string | ||
] | ||
|> Set.ofList | ||
|
||
let allColumns = | ||
sColumns.Rows | ||
|> Seq.cast<DataRow> | ||
|> Seq.map (fun col -> | ||
{| | ||
TableCatalog = col["TABLE_CATALOG"] :?> string | ||
TableSchema = col["TABLE_SCHEMA"] :?> string | ||
TableName = col["TABLE_NAME"] :?> string | ||
ColumnName = col["COLUMN_NAME"] :?> string | ||
ProviderTypeName = col["DATA_TYPE"] :?> string | ||
OrdinalPosition = col["ORDINAL_POSITION"] :?> uint64 | ||
IsNullable = | ||
match col["IS_NULLABLE"] :?> string with | ||
| "YES" -> true | ||
| _ -> false | ||
IsPK = | ||
match col["COLUMN_KEY"] :?> string with | ||
| "PRI" -> true | ||
| _ -> false | ||
|} | ||
) | ||
|> Seq.sortBy (fun column -> column.OrdinalPosition) | ||
|
||
let tables = | ||
sTables.Rows | ||
|> Seq.cast<DataRow> | ||
|> Seq.map (fun tbl -> | ||
{| | ||
Catalog = tbl["TABLE_CATALOG"] :?> string | ||
Schema = tbl["TABLE_SCHEMA"] :?> string | ||
Name = tbl["TABLE_NAME"] :?> string | ||
Type = tbl["TABLE_TYPE"] :?> string | ||
|} | ||
) | ||
|> Seq.filter (fun tbl -> tbl.Type <> "SYSTEM_TABLE") | ||
|> SchemaFilters.filterTables cfg.Filters | ||
|> Seq.choose (fun tbl -> | ||
let tableColumns = | ||
allColumns | ||
|> Seq.filter (fun col -> | ||
col.TableCatalog = tbl.Catalog && | ||
col.TableSchema = tbl.Schema && | ||
col.TableName = tbl.Name | ||
) | ||
|
||
let supportedColumns = | ||
tableColumns | ||
|> Seq.choose (fun col -> | ||
MySqlDataTypes.tryFindTypeMapping(col.ProviderTypeName) | ||
|> Option.map (fun typeMapping -> | ||
{ | ||
Column.Name = col.ColumnName | ||
Column.IsNullable = col.IsNullable | ||
Column.TypeMapping = typeMapping | ||
Column.IsPK = pks.Contains(col.TableSchema, col.TableName, col.ColumnName) | ||
} | ||
) | ||
) | ||
|> Seq.toList | ||
|
||
let filteredColumns = | ||
supportedColumns | ||
|> SchemaFilters.filterColumns cfg.Filters tbl.Schema tbl.Name | ||
|> Seq.toList | ||
|
||
if filteredColumns |> Seq.isEmpty then | ||
None | ||
else | ||
Some { | ||
Table.Catalog = tbl.Catalog | ||
Table.Schema = tbl.Schema | ||
Table.Name = tbl.Name | ||
Table.Type = if tbl.Type = "table" then TableType.Table else TableType.View | ||
Table.Columns = filteredColumns | ||
Table.TotalColumns = tableColumns |> Seq.length | ||
} | ||
) | ||
|> Seq.toList | ||
|
||
{ | ||
Tables = tables | ||
Enums = [] | ||
PrimitiveTypeReaders = MySqlDataTypes.primitiveTypeReaders | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.