Skip to content

Commit

Permalink
Updates to foreignkey retrieval
Browse files Browse the repository at this point in the history
  • Loading branch information
mattjcowan committed Sep 12, 2024
1 parent f498fcf commit bf4b329
Show file tree
Hide file tree
Showing 6 changed files with 536 additions and 201 deletions.
130 changes: 56 additions & 74 deletions src/DapperMatic/Providers/MySql/MySqlExtensions.ForeignKeyMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,12 +141,12 @@ public async Task<IEnumerable<ForeignKey>> GetForeignKeysAsync(
(_, tableName, _) = NormalizeNames(schemaName, tableName);

var where = string.IsNullOrWhiteSpace(nameFilter)
? ""
: $"{ToAlphaNumericString(nameFilter)}".Replace("*", "%");
? ""
: $"{ToAlphaNumericString(nameFilter)}".Replace("*", "%");

var sql =
$@"SELECT
kcu.CONSTRAINT_NAME as constraing_name,
kcu.CONSTRAINT_NAME as constraint_name,
kcu.TABLE_NAME as table_name,
kcu.COLUMN_NAME as column_name,
kcu.REFERENCED_TABLE_NAME as referenced_table_name,
Expand All @@ -155,54 +155,54 @@ public async Task<IEnumerable<ForeignKey>> GetForeignKeysAsync(
rc.UPDATE_RULE as update_rule
FROM information_schema.KEY_COLUMN_USAGE kcu
INNER JOIN information_schema.referential_constraints rc ON kcu.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
WHERE kcu.TABLE_SCHEMA = DATABASE()";
if (string.IsNullOrWhiteSpace(tableName))
sql += $@" AND kcu.TABLE_NAME = @tableName AND REFERENCED_TABLE_NAME IS NOT NULL";
WHERE kcu.TABLE_SCHEMA = DATABASE() AND kcu.REFERENCED_TABLE_NAME IS NOT NULL";
if (!string.IsNullOrWhiteSpace(tableName))
sql += $@" AND kcu.TABLE_NAME = @tableName";
if (!string.IsNullOrWhiteSpace(where))
sql += $@" AND kcu.CONSTRAINT_NAME LIKE @where";
sql += " ORDER BY kcu.TABLE_NAME, kcu.CONSTRAINT_NAME";

var results =
await QueryAsync<(string constraint_name, string table_name, string column_name, string referenced_table_name, string referenced_column_name, string delete_rule, string update_rule)>(
db,
sql,
new { tableName, where },
tx
)
.ConfigureAwait(false);
var results = await QueryAsync<(
string constraint_name,
string table_name,
string column_name,
string referenced_table_name,
string referenced_column_name,
string delete_rule,
string update_rule
)>(db, sql, new { tableName, where }, tx)
.ConfigureAwait(false);

return results.Select(
r =>
return results.Select(r =>
{
var deleteRule = r.delete_rule switch
{
var deleteRule = r.delete_rule switch
{
"NO ACTION" => ReferentialAction.NoAction,
"CASCADE" => ReferentialAction.Cascade,
"SET NULL" => ReferentialAction.SetNull,
_ => ReferentialAction.NoAction
};
var updateRule = r.update_rule switch
{
"NO ACTION" => ReferentialAction.NoAction,
"CASCADE" => ReferentialAction.Cascade,
"SET NULL" => ReferentialAction.SetNull,
_ => ReferentialAction.NoAction
};
return new ForeignKey(
null,
r.constraint_name,
r.table_name,
r.column_name,
r.referenced_table_name,
r.referenced_column_name,
deleteRule,
updateRule
);
}
);
"NO ACTION" => ReferentialAction.NoAction,
"CASCADE" => ReferentialAction.Cascade,
"SET NULL" => ReferentialAction.SetNull,
_ => ReferentialAction.NoAction
};
var updateRule = r.update_rule switch
{
"NO ACTION" => ReferentialAction.NoAction,
"CASCADE" => ReferentialAction.Cascade,
"SET NULL" => ReferentialAction.SetNull,
_ => ReferentialAction.NoAction
};
return new ForeignKey(
null,
r.constraint_name,
r.table_name,
r.column_name,
r.referenced_table_name,
r.referenced_column_name,
deleteRule,
updateRule
);
});
}

public Task<IEnumerable<string>> GetForeignKeyNamesAsync(
public async Task<IEnumerable<string>> GetForeignKeyNamesAsync(
IDbConnection db,
string? tableName,
string? nameFilter = null,
Expand All @@ -211,41 +211,23 @@ public Task<IEnumerable<string>> GetForeignKeyNamesAsync(
CancellationToken cancellationToken = default
)
{
if (string.IsNullOrWhiteSpace(tableName))
throw new ArgumentException("Table name must be specified.", nameof(tableName));

(_, tableName, _) = NormalizeNames(schemaName, tableName);

if (string.IsNullOrWhiteSpace(nameFilter))
{
return QueryAsync<string>(
db,
$@"SELECT CONSTRAINT_NAME
FROM information_schema.TABLE_CONSTRAINTS
WHERE TABLE_SCHEMA = DATABASE() AND
TABLE_NAME = @tableName AND
CONSTRAINT_TYPE = 'FOREIGN KEY'
ORDER BY CONSTRAINT_NAME",
new { tableName },
tx
);
}
else
{
var where = $"{ToAlphaNumericString(nameFilter)}".Replace("*", "%");
return QueryAsync<string>(
db,
$@"SELECT CONSTRAINT_NAME
var where = string.IsNullOrWhiteSpace(nameFilter)
? null
: $"{ToAlphaNumericString(nameFilter)}".Replace("*", "%");

var sql =
$@"SELECT CONSTRAINT_NAME
FROM information_schema.TABLE_CONSTRAINTS
WHERE TABLE_SCHEMA = DATABASE() AND
TABLE_NAME = @tableName AND
CONSTRAINT_TYPE = 'FOREIGN KEY' AND
CONSTRAINT_NAME LIKE @where
ORDER BY CONSTRAINT_NAME",
new { tableName, where },
tx
);
}
CONSTRAINT_TYPE = 'FOREIGN KEY'"
+ (string.IsNullOrWhiteSpace(tableName) ? "" : " AND TABLE_NAME = @tableName")
+ (string.IsNullOrWhiteSpace(where) ? "" : " AND CONSTRAINT_NAME LIKE @where")
+ @" ORDER BY CONSTRAINT_NAME";

return await QueryAsync<string>(db, sql, new { tableName, where }, tx)
.ConfigureAwait(false);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,12 @@ FROM information_schema.table_constraints
table_name = @tableName AND
constraint_name = @foreignKeyName AND
constraint_type = 'FOREIGN KEY'",
new { schemaName, tableName, foreignKeyName },
new
{
schemaName,
tableName,
foreignKeyName
},
tx
)
.ConfigureAwait(false);
Expand All @@ -54,7 +59,12 @@ FROM information_schema.key_column_usage
table_name = @tableName AND
column_name = @columnName
)",
new { schemaName, tableName, columnName },
new
{
schemaName,
tableName,
columnName
},
tx
)
.ConfigureAwait(false);
Expand Down Expand Up @@ -130,7 +140,7 @@ ON DELETE {onDelete}
return true;
}

public Task<IEnumerable<ForeignKey>> GetForeignKeysAsync(
public async Task<IEnumerable<ForeignKey>> GetForeignKeysAsync(
IDbConnection db,
string? tableName,
string? nameFilter = null,
Expand All @@ -139,10 +149,116 @@ public Task<IEnumerable<ForeignKey>> GetForeignKeysAsync(
CancellationToken cancellationToken = default
)
{
throw new NotImplementedException();
(schemaName, tableName, _) = NormalizeNames(schemaName, tableName);

var where = string.IsNullOrWhiteSpace(nameFilter)
? null
: $"{ToAlphaNumericString(nameFilter)}".Replace("*", "%");

var sql =
$@"SELECT c.conname AS constraint_name,
sch.nspname AS schema_name,
tbl.relname AS table_name,
string_agg(col.attname, ',' ORDER BY u.attposition) AS column_names,
f_sch.nspname AS referenced_schema_name,
f_tbl.relname AS referenced_table_name,
string_agg(f_col.attname, ',' ORDER BY f_u.attposition) AS referenced_column_names,
CASE
WHEN c.confdeltype = 'c' THEN 'CASCADE'
WHEN c.confdeltype = 'n' THEN 'SET NULL'
WHEN c.confdeltype = 'r' THEN 'RESTRICT'
ELSE 'NO ACTION'
end as delete_rule,
CASE
WHEN c.confupdtype = 'c' THEN 'CASCADE'
WHEN c.confupdtype = 'n' THEN 'SET NULL'
WHEN c.confupdtype = 'r' THEN 'RESTRICT'
ELSE 'NO ACTION'
end as update_rule,
pg_get_constraintdef(c.oid) AS definition
FROM pg_constraint c
LEFT JOIN LATERAL UNNEST(c.conkey) WITH ORDINALITY AS u(attnum, attposition) ON TRUE
LEFT JOIN LATERAL UNNEST(c.confkey) WITH ORDINALITY AS f_u(attnum, attposition) ON f_u.attposition = u.attposition
JOIN pg_class tbl ON tbl.oid = c.conrelid
JOIN pg_namespace sch ON sch.oid = tbl.relnamespace
LEFT JOIN pg_attribute col ON (col.attrelid = tbl.oid AND col.attnum = u.attnum)
LEFT JOIN pg_class f_tbl ON f_tbl.oid = c.confrelid
LEFT JOIN pg_namespace f_sch ON f_sch.oid = f_tbl.relnamespace
LEFT JOIN pg_attribute f_col ON (f_col.attrelid = f_tbl.oid AND f_col.attnum = f_u.attnum)
where c.contype = 'f'";
if (!string.IsNullOrWhiteSpace(schemaName))
sql += $@" AND sch.nspname = @schemaName";
if (!string.IsNullOrWhiteSpace(tableName))
sql += $@" AND tbl.relname = @tableName";
if (!string.IsNullOrWhiteSpace(where))
sql += $@" AND c.conname LIKE @where";
sql +=
$@" GROUP BY schema_name, table_name, constraint_name, referenced_schema_name, referenced_table_name, definition, delete_rule, update_rule
ORDER BY schema_name, table_name, constraint_name";

var results = await QueryAsync<(
string constraint_name,
string schema_name,
string table_name,
string column_names,
string referenced_schema_name,
string referenced_table_name,
string referenced_column_names,
string delete_rule,
string update_rule,
string definition
)>(
db,
sql,
new
{
schemaName,
tableName,
where
},
tx
)
.ConfigureAwait(false);

return results.Select(r =>
{
var deleteRule = r.delete_rule switch
{
"CASCADE" => ReferentialAction.Cascade,
"SET NULL" => ReferentialAction.SetNull,
"NO ACTION" => ReferentialAction.NoAction,
_ => ReferentialAction.NoAction
};
var updateRule = r.update_rule switch
{
"CASCADE" => ReferentialAction.Cascade,
"SET NULL" => ReferentialAction.SetNull,
"NO ACTION" => ReferentialAction.NoAction,
_ => ReferentialAction.NoAction
};
var columnNames = r.column_names.Split(
',',
StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries
);
var referencedColumnNames = r.referenced_column_names.Split(
',',
StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries
);

return new ForeignKey(
r.schema_name,
r.constraint_name,
r.table_name,
columnNames.First(),
r.referenced_table_name,
referencedColumnNames.First(),
deleteRule,
updateRule
);
});
}

public Task<IEnumerable<string>> GetForeignKeyNamesAsync(
public async Task<IEnumerable<string>> GetForeignKeyNamesAsync(
IDbConnection db,
string? tableName,
string? nameFilter = null,
Expand All @@ -153,31 +269,36 @@ public Task<IEnumerable<string>> GetForeignKeyNamesAsync(
{
(schemaName, tableName, _) = NormalizeNames(schemaName, tableName);

if (string.IsNullOrWhiteSpace(nameFilter))
{
return QueryAsync<string>(
db,
$@"SELECT conname
FROM pg_constraint
WHERE conrelid = '{schemaName}.{tableName}'::regclass
AND contype = 'f'",
transaction: tx
);
}
else
{
var where = $"{ToAlphaNumericString(nameFilter)}".Replace("*", "%");
return QueryAsync<string>(
var where = string.IsNullOrWhiteSpace(nameFilter)
? null
: $"{ToAlphaNumericString(nameFilter)}".Replace("*", "%");

var sql =
$@"SELECT c.conname
FROM pg_constraint c
JOIN pg_class tbl ON tbl.oid = c.conrelid
JOIN pg_namespace sch ON sch.oid = tbl.relnamespace
where c.contype = 'f'";
if (!string.IsNullOrWhiteSpace(schemaName))
sql += $@" AND sch.nspname = @schemaName";
if (!string.IsNullOrWhiteSpace(tableName))
sql += $@" AND tbl.relname = @tableName";
if (!string.IsNullOrWhiteSpace(where))
sql += $@" AND c.conname LIKE @where";
sql += @" ORDER BY conname";

return await QueryAsync<string>(
db,
$@"SELECT conname
FROM pg_constraint
WHERE conrelid = '{schemaName}.{tableName}'::regclass
AND contype = 'f'
AND conname LIKE @where",
new { schemaName, tableName, where },
transaction: tx
);
}
sql,
new
{
schemaName,
tableName,
where
},
tx
)
.ConfigureAwait(false);
}

public async Task<bool> DropForeignKeyIfExistsAsync(
Expand Down Expand Up @@ -241,7 +362,12 @@ FROM pg_attribute
AND attname = @columnName
)
)",
new { schemaName, tableName, columnName },
new
{
schemaName,
tableName,
columnName
},
tx
)
.ConfigureAwait(false);
Expand Down
Loading

0 comments on commit bf4b329

Please sign in to comment.