8000 Add `Foreign Keys`, `Indexes`, `IndexColumns` schema collections (#1419) · mysql-net/MySqlConnector@9992b5d · GitHub
[go: up one dir, main page]

Skip to content

Commit 9992b5d

Browse files
bgraingerEvgeni Alesinskyy
and
Evgeni Alesinskyy
authored
Add Foreign Keys, Indexes, IndexColumns schema collections (#1419)
Signed-off-by: Evgeni Alesinskyy <ealesinskyy@exocad.com> Signed-off-by: Bradley Grainger <bgrainger@gmail.com> Co-authored-by: Evgeni Alesinskyy <ealesinskyy@exocad.com>
1 parent ab05700 commit 9992b5d

File tree

10 files changed

+528
-21
lines changed

10 files changed

+528
-21
lines changed

docs/content/overview/schema-collections.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
lastmod: 2023-01-28
2+
lastmod: 2023-12-15
33
date: 2021-04-24
44
title: Schema Collections
55
customtitle: "Supported Schema Collections"
@@ -45,3 +45,6 @@ weight: 80
4545
* `Triggers`
4646
* `UserPrivileges`
4747
* `Views`
48+
* `Foreign Keys`[information about foreign keys in the server's SQL syntax](../schema/foreign keys/)
49+
* `Indexes`[information about indexes in the server's SQL syntax](../schema/indexes/)
50+
* `IndexColumns`[information about indexes in the server's SQL syntax](../schema/indexcolumns/)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
---
2+
date: 2022-07-10
3+
lastmod: 2023-12-15
4+
title: Foreign Keys Schema
5+
---
6+
7+
# Foreign Keys Schema
8+
9+
The `Foreign Keys` schema provides information about foreign keys in the server's SQL syntax.
10+
11+
Column Name | Data Type | Description
12+
--- | --- | ---
13+
CONSTRAINT_CATALOG | string |
14+
CONSTRAINT_SCHEMA | string |
15+
CONSTRAINT_NAME | string |
16+
TABLE_CATALOG | string |
17+
TABLE_SCHEMA | string |
18+
TABLE_NAME | string |
19+
MATCH_OPTION | string |
20+
UPDATE_RULE | string |
21+
DELETE_RULE | string |
22+
REFERENCED_TABLE_CATALOG | string |
23+
REFERENCED_TABLE_SCHEMA | string |
24+
REFERENCED_TABLE_NAME | string |
25+
26+
The following restrictions are supported:
27+
28+
Restriction Name | Restriction Default | Restriction Number
29+
--- | --- | --:
30+
Catalog | TABLE_CATALOG | 1
31+
Schema | TABLE_SCHEMA | 2
32+
Table | TABLE_NAME | 3
33+
Column | COLUMN_NAME | 4
34+
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
date: 2022-07-10
3+
lastmod: 2023-12-15
4+
title: IndexColumns Schema
5+
---
6+
7+
# IndexColumns Schema
8+
9+
The `IndexColumns` schema provides information about indexes in the server's SQL syntax.
10+
11+
Column Name | Data Type | Description
12+
--- | --- | ---
13+
INDEX_CATALOG | string |
14+
INDEX_SCHEMA | string |
15+
INDEX_NAME | string |
16+
TABLE_NAME | string |
17+
COLUMN_NAME | string |
18+
ORDINAL_POSITION | int |
19+
SORT_ORDER | string |
20+
21+
The following restrictions are supported:
22+
23+
Restriction Name | Restriction Default | Restriction Number
24+
--- | --- | --:
25+
Catalog | TABLE_CATALOG | 1
26+
Schema | TABLE_SCHEMA | 2
27+
Table | TABLE_NAME | 3
28+
Constraint | CONSTRAINT_NAME | 4
29+
Column | COLUMN_NAME | 5
30+
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
date: 2022-07-10
3+
lastmod: 2023-12-15
4+
title: Indexes Schema
5+
---
6+
7+
# Indexes Schema
8+
9+
The `Indexes` schema provides information about indexes in the server's SQL syntax.
10+
11+
Column Name | Data Type | Description
12+
--- | --- | ---
13+
INDEX_CATALOG | string |
14+
INDEX_SCHEMA | string |
15+
INDEX_NAME | string |
16+
TABLE_NAME | string |
17+
UNIQUE | bool |
18+
PRIMARY | bool |
19+
TYPE | string |
20+
COMMENT | string |
21+
22+
The following restrictions are supported:
23+
24+
Restriction Name | Restriction Default | Restriction Number
25+
--- | --- | --:
26+
Catalog | TABLE_CATALOG | 1
27+
Schema | TABLE_SCHEMA | 2
28+
Table | TABLE_NAME | 3
29+
Column | COLUMN_NAME | 4
30+

src/MySqlConnector/Core/SchemaProvider.cs

Lines changed: 123 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Globalization;
2+
using System.Text;
23
using MySqlConnector.Protocol.Serialization;
34

45
namespace MySqlConnector.Core;
@@ -382,6 +383,22 @@ private static void DoFillReservedWords(DataTable dataTable)
382383
}
383384

384385
private async Task FillDataTableAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, List<KeyValuePair<string, string>>? columns, CancellationToken cancellationToken)
386+
{
387+
await FillDataTableAsync(ioBehavior, dataTable, command =>
388+
{
389+
#pragma warning disable CA2100
390+
command.CommandText = "SELECT " + string.Join(", ", dataTable.Columns.Cast<DataColumn>().Select(static x => x!.ColumnName)) + " FROM INFORMATION_SCHEMA." + tableName;
391+
#pragma warning restore CA2100
392+
if (columns is { Count: > 0 })
393+
{
394+
command.CommandText += " WHERE " + string.Join(" AND ", columns.Select(x => $@"{x.Key} = @{x.Key}"));
395+
foreach (var column in columns)
396+
command.Parameters.AddWithValue("@" + column.Key, column.Value);
397+
}
398+
}, cancellationToken).ConfigureAwait(false);
399+
}
400+
401+
private async Task FillDataTableAsync(IOBehavior ioBehavior, DataTable dataTable, Action<MySqlCommand> configureCommand, CancellationToken cancellationToken)
385402
{
386403
Action? close = null;
387404
if (connection.State != ConnectionState.Open)
@@ -408,15 +425,7 @@ private async Task FillDataTableAsync(IOBehavior ioBehavior, DataTable dataTable
408425

409426
using (var command = connection.CreateCommand())
410427
{
411-
#pragma warning disable CA2100
412-
command.CommandText = "SELECT " + string.Join(", ", dataTable.Columns.Cast<DataColumn>().Select(static x => x!.ColumnName)) + " FROM INFORMATION_SCHEMA." + tableName;
413-
#pragma warning restore CA2100
414-
if (columns is { Count: > 0 })
415-
{
416-
command.CommandText += " WHERE " + string.Join(" AND ", columns.Select(x => $@"{x.Key} = @{x.Key}"));
417-
foreach (var column in columns)
418-
command.Parameters.AddWithValue("@" + column.Key, column.Value);
419-
}
428+
configureCommand(command);
420429

421430
using var reader = await command.ExecuteReaderAsync(default, ioBehavior, cancellationToken).ConfigureAwait(false);
422431
while (await reader.ReadAsync(ioBehavior, cancellationToken).ConfigureAwait(false))
@@ -429,4 +438,109 @@ private async Task FillDataTableAsync(IOBehavior ioBehavior, DataTable dataTable
429438

430439
close?.Invoke();
431440
}
441+
442+
private Task DoFillForeignKeysAsync(IOBehavior ioBehavior, DataTable dataTable, string?[]? restrictionValues, CancellationToken cancellationToken) =>
443+
FillDataTableAsync(IOBehavior.Synchronous, dataTable, command =>
444+
{
445+
command.CommandText = """
446+
SELECT rc.constraint_catalog, rc.constraint_schema,
447+
rc.constraint_name, kcu.table_catalog, kcu.table_schema, rc.table_name,
448+
rc.match_option, rc.update_rule, rc.delete_rule,
449+
NULL as referenced_table_catalog,
450+
kcu.referenced_table_schema, rc.referenced_table_name
451+
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc
452+
LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu ON
453+
kcu.constraint_catalog <=> rc.constraint_catalog AND
454+
kcu.constraint_schema <=> rc.constraint_schema AND
455+
kcu.constraint_name <=> rc.constraint_name
456+
WHERE 1=1 AND kcu.ORDINAL_POSITION=1
457+
""";
458+
459+
if (restrictionValues is not null)
460+
{
461+
var where = "";
462+
if (restrictionValues.Length >= 2 && !string.IsNullOrEmpty(restrictionValues[1]))
463+
{
464+
where += " AND rc.constraint_schema LIKE @schema";
465+
command.Parameters.AddWithValue("@schema", restrictionValues[1]);
466+
}
467+
if (restrictionValues.Length >= 3 && !string.IsNullOrEmpty(restrictionValues[2]))
468+
{
469+
where += " AND rc.table_name LIKE @table";
470+
command.Parameters.AddWithValue("@table", restrictionValues[2]);
471+
}
472+
if (restrictionValues.Length >= 4 && !string.IsNullOrEmpty(restrictionValues[3]))
473+
{
474+
where += " AND rc.constraint_name LIKE @constraint";
475+
command.Parameters.AddWithValue("@constraint", restrictionValues[3]);
476+
}
477+
command.CommandText += where;
478+
}
479+
}, cancellationToken);
480+
481+
private Task DoFillIndexesAsync(IOBehavior ioBehavior, DataTable dataTable, string?[]? restrictionValues, CancellationToken cancellationToken) =>
482+
FillDataTableAsync(ioBehavior, dataTable, command =>
483+
{
484+
command.CommandText = """
485+
SELECT DISTINCT null AS INDEX_CATALOG, INDEX_SCHEMA,
486+
INDEX_NAME, TABLE_NAME,
487+
!NON_UNIQUE as `UNIQUE`,
488+
INDEX_NAME='PRIMARY' as `PRIMARY`,
489+
INDEX_TYPE as TYPE, COMMENT
490+
FROM INFORMATION_SCHEMA.STATISTICS
491+
WHERE 1=1
492+
""";
493+
494+
if (restrictionValues is not null)
495+
{
496+
var where = "";
497+
if (restrictionValues.Length >= 2 && !string.IsNullOrEmpty(restrictionValues[1]))
498+
{
499+
where += " AND INDEX_SCHEMA LIKE @schema";
500+
command.Parameters.AddWithValue("@schema", restrictionValues[1]);
501+
}
502+
if (restrictionValues.Length >= 3 && !string.IsNullOrEmpty(restrictionValues[2]))
503+
{
504+
where += " AND TABLE_NAME LIKE @table";
505+
command.Parameters.AddWithValue("@table", restrictionValues[2]);
506+
}
507+
508+
command.CommandText += where;
509+
}
510+
}, cancellationToken);
511+
512+
private Task DoFillIndexColumnsAsync(IOBehavior ioBehavior, DataTable dataTable, string?[]? restrictionValues, CancellationToken cancellationToken) =>
513+
FillDataTableAsync(ioBehavior, dataTable, command =>
514+
{
515+
command.CommandText = """
516+
SELECT null AS INDEX_CATALOG, INDEX_SCHEMA,
517+
INDEX_NAME, TABLE_NAME,
518+
COLUMN_NAME,
519+
SEQ_IN_INDEX as `ORDINAL_POSITION`,
520+
COLLATION as SORT_ORDER
521+
FROM INFORMATION_SCHEMA.STATISTICS
522+
WHERE 1=1
523+
""";
524+
525+
if (restrictionValues is not null)
526+
{
527+
var where = "";
528+
if (restrictionValues.Length >= 2 && !string.IsNullOrEmpty(restrictionValues[1]))
529+
{
530+
where += " AND INDEX_SCHEMA LIKE @schema";
531+
command.Parameters.AddWithValue("@schema", restrictionValues[1]);
532+
}
533+
if (restrictionValues.Length >= 3 && !string.IsNullOrEmpty(restrictionValues[2]))
534+
{
535+
where += " AND TABLE_NAME LIKE @table";
536+
command.Parameters.AddWithValue("@table", restrictionValues[2]);
537+
}
538+
if (restrictionValues.Length >= 4 && !string.IsNullOrEmpty(restrictionValues[3]))
539+
{
540+
where += " AND INDEX_NAME LIKE @index";
541+
command.Parameters.AddWithValue("@index", restrictionValues[3]);
542+
}
543+
command.CommandText += where;
544+
}
545+
}, cancellationToken);
432546
}

src/MySqlConnector/Core/SchemaProvider.g.cs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ public async ValueTask<DataTable> GetSchemaAsync(IOBehavior ioBehavior, string c
7575
await FillUserPrivilegesAsync(ioBehavior, dataTable, "UserPrivileges", restrictionValues, cancellationToken).ConfigureAwait(false);
7676
else if (string.Equals(collectionName, "Views", StringComparison.OrdinalIgnoreCase))
7777
await FillViewsAsync(ioBehavior, dataTable, "Views", restrictionValues, cancellationToken).ConfigureAwait(false);
78+
else if (string.Equals(collectionName, "Foreign Keys", StringComparison.OrdinalIgnoreCase))
79+
await FillForeignKeysAsync(ioBehavior, dataTable, "Foreign Keys", restrictionValues, cancellationToken).ConfigureAwait(false);
80+
else if (string.Equals(collectionName, "Indexes", StringComparison.OrdinalIgnoreCase))
81+
await FillIndexesAsync(ioBehavior, dataTable, "Indexes", restrictionValues, cancellationToken).ConfigureAwait(false);
82+
else if (string.Equals(collectionName, "IndexColumns", StringComparison.OrdinalIgnoreCase))
83+
await FillIndexColumnsAsync(ioBehavior, dataTable, "IndexColumns", restrictionValues, cancellationToken).ConfigureAwait(false);
7884
else
7985
throw new ArgumentException($"Invalid collection name: '{collectionName}'.", nameof(collectionName));
8086

@@ -123,6 +129,9 @@ private Task FillMetaDataCollectionsAsync(IOBehavior ioBehavior, DataTable dataT
123129
dataTable.Rows.Add("Triggers", 0, 3);
124130
dataTable.Rows.Add("UserPrivileges", 0, 0);
125131
dataTable.Rows.Add("Views", 0, 3);
132+
dataTable.Rows.Add("Foreign Keys", 4, 0);
133+
dataTable.Rows.Add("Indexes", 4, 0);
134+
dataTable.Rows.Add("IndexColumns", 5, 0);
126135

127136
return Task.CompletedTask;
128137
}
@@ -628,6 +637,19 @@ private Task FillRestrictionsAsync(IOBehavior ioBehavior, DataTable dataTable, s
628637
dataTable.Rows.Add("Tables", "Schema", "TABLE_SCHEMA", 2);
629638
dataTable.Rows.Add("Tables", "Table", "TABLE_NAME", 3);
630639
dataTable.Rows.Add("Tables", "TableType", "TABLE_TYPE", 4);
640+
dataTable.Rows.Add("Foreign Keys", "Catalog", "TABLE_CATALOG", 1);
641+
dataTable.Rows.Add("Foreign Keys", "Schema", "TABLE_SCHEMA", 2);
642+
dataTable.Rows.Add("Foreign Keys", "Table", "TABLE_NAME", 3);
643+
dataTable.Rows.Add("Foreign Keys", "Column", "COLUMN_NAME", 4);
644+
dataTable.Rows.Add("Indexes", "Catalog", "TABLE_CATALOG", 1);
645+
dataTable.Rows.Add("Indexes", "Schema", "TABLE_SCHEMA", 2);
646+
dataTable.Rows.Add("Indexes", "Table", "TABLE_NAME", 3);
647+
dataTable.Rows.Add("Indexes", "Column", "COLUMN_NAME", 4);
648+
dataTable.Rows.Add("IndexColumns", "Catalog", "TABLE_CATALOG", 1);
649+
dataTable.Rows.Add("IndexColumns", "Schema", "TABLE_SCHEMA", 2);
650+
dataTable.Rows.Add("IndexColumns", "Table", "TABLE_NAME", 3);
651+
dataTable.Rows.Add("IndexColumns", "Constraint", "CONSTRAINT_NAME", 4);
652+
dataTable.Rows.Add("IndexColumns", "Column", "COLUMN_NAME", 5);
631653

632654
return Task.CompletedTask;
633655
}
@@ -832,4 +854,70 @@ private async Task FillViewsAsync(IOBehavior ioBehavior, DataTable dataTable, st
832854
await FillDataTableAsync(ioBehavior, dataTable, "VIEWS", null, cancellationToken).ConfigureAwait(false);
833855
}
834856

857+
private async Task FillForeignKeysAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
858+
{
859+
if (restrictionValues is { Length: > 4 })
860+
throw new ArgumentException("More than 4 restrictionValues are not supported for schema 'Foreign Keys'.", nameof(restrictionValues));
861+
862+
dataTable.TableName = tableName;
863+
dataTable.Columns.AddRange(
864+
[
865+
new("CONSTRAINT_CATALOG", typeof(string)),
866+
new("CONSTRAINT_SCHEMA", typeof(string)),
867+
new("CONSTRAINT_NAME", typeof(string)),
868+
new("TABLE_CATALOG", typeof(string)),
869+
new("TABLE_SCHEMA", typeof(string)),
870+
new("TABLE_NAME", typeof(string)),
871+
new("MATCH_OPTION", typeof(string)),
872+
new("UPDATE_RULE", typeof(string)),
873+
new("DELETE_RULE", typeof(string)),
874+
new("REFERENCED_TABLE_CATALOG", typeof(string)),
875+
new("REFERENCED_TABLE_SCHEMA", typeof(string)),
876+
new("REFERENCED_TABLE_NAME", typeof(string)),
877+
]);
878+
879+
await DoFillForeignKeysAsync(ioBehavior, dataTable, restrictionValues, cancellationToken).ConfigureAwait(false);
880+
}
881+
882+
private async Task FillIndexesAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
883+
{
884+
if (restrictionValues is { Length: > 4 })
885+
throw new ArgumentException("More than 4 restrictionValues are not supported for schema 'Indexes'.", nameof(restrictionValues));
886+
887+
dataTable.TableName = tableName;
888+
dataTable.Columns.AddRange(
889+
[
890+
new("INDEX_CATALOG", typeof(string)),
891+
new("INDEX_SCHEMA", typeof(string)),
892+
new("INDEX_NAME", typeof(string)),
893+
new("TABLE_NAME", typeof(string)),
894+
new("UNIQUE", typeof(bool)),
895+
new("PRIMARY", typeof(bool)),
896+
new("TYPE", typeof(string)),
897+
new("COMMENT", typeof(string)),
898+
]);
899+
900+
await DoFillIndexesAsync(ioBehavior, dataTable, restrictionValues, cancellationToken).ConfigureAwait(false);
901+
}
902+
903+
private async Task FillIndexColumnsAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
904+
{
905+
if (restrictionValues is { Length: > 5 })
906+
throw new ArgumentException("More than 5 restrictionValues are not supported for schema 'IndexColumns'.", nameof(restrictionValues));
907+
908+
dataTable.TableName = tableName;
909+
dataTable.Columns.AddRange(
910+
[
911+
new("INDEX_CATALOG", typeof(string)),
912+
new("INDEX_SCHEMA", typeof(string)),
913+
new("INDEX_NAME", typeof(string)),
914+
new("TABLE_NAME", typeof(string)),
915+
new("COLUMN_NAME", typeof(string)),
916+
new("ORDINAL_POSITION", typeof(int)),
917+
new("SORT_ORDER", typeof(string)),
918+
]);
919+
920+
await DoFillIndexColumnsAsync(ioBehavior, dataTable, restrictionValues, cancellationToken).ConfigureAwait(false);
921+
}
922+
835923
}

0 commit comments

Comments
 (0)
0