mirror of
https://github.com/HeidiSQL/HeidiSQL.git
synced 2025-08-06 18:24:26 +08:00
Separate join in TDBConnection.GetTableForeignKeys into two separate and faster queries. Closes #852
This commit is contained in:
@ -361,6 +361,7 @@ type
|
|||||||
FKeepAliveTimer: TTimer;
|
FKeepAliveTimer: TTimer;
|
||||||
FFavorites: TStringList;
|
FFavorites: TStringList;
|
||||||
FPrefetchResults: TDBQueryList;
|
FPrefetchResults: TDBQueryList;
|
||||||
|
FForeignKeyQueriesFailed: Boolean;
|
||||||
procedure SetActive(Value: Boolean); virtual; abstract;
|
procedure SetActive(Value: Boolean); virtual; abstract;
|
||||||
procedure DoBeforeConnect; virtual;
|
procedure DoBeforeConnect; virtual;
|
||||||
procedure DoAfterConnect; virtual;
|
procedure DoAfterConnect; virtual;
|
||||||
@ -1683,6 +1684,7 @@ begin
|
|||||||
FCurrentUserHostCombination := '';
|
FCurrentUserHostCombination := '';
|
||||||
FKeepAliveTimer := TTimer.Create(Self);
|
FKeepAliveTimer := TTimer.Create(Self);
|
||||||
FFavorites := TStringList.Create;
|
FFavorites := TStringList.Create;
|
||||||
|
FForeignKeyQueriesFailed := False;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
@ -4891,42 +4893,64 @@ end;
|
|||||||
|
|
||||||
function TDBConnection.GetTableForeignKeys(Table: TDBObject): TForeignKeyList;
|
function TDBConnection.GetTableForeignKeys(Table: TDBObject): TForeignKeyList;
|
||||||
var
|
var
|
||||||
ForeignQuery: TDBQuery;
|
ForeignQuery, ColQuery: TDBQuery;
|
||||||
ForeignKey: TForeignKey;
|
ForeignKey: TForeignKey;
|
||||||
begin
|
begin
|
||||||
// Generic: query table foreign keys from IS.?
|
// Generic: query table foreign keys from IS.?
|
||||||
Result := TForeignKeyList.Create(True);
|
Result := TForeignKeyList.Create(True);
|
||||||
|
if FForeignKeyQueriesFailed then begin
|
||||||
|
Log(lcDebug, 'Avoid foreign key retrieval with queries which failed before');
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
try
|
try
|
||||||
ForeignQuery := GetResults('SELECT k.*, r.UPDATE_RULE, r.DELETE_RULE'+
|
// Combine two IS tables by hand, not by JOIN, as this is too slow. See #852
|
||||||
' FROM information_schema.KEY_COLUMN_USAGE AS k, information_schema.REFERENTIAL_CONSTRAINTS AS r'+
|
ForeignQuery := GetResults('SELECT *'+
|
||||||
|
' FROM information_schema.REFERENTIAL_CONSTRAINTS'+
|
||||||
' WHERE'+
|
' WHERE'+
|
||||||
' k.CONSTRAINT_SCHEMA='+EscapeString(Table.Database)+
|
' CONSTRAINT_SCHEMA='+EscapeString(Table.Database)+
|
||||||
' AND k.TABLE_NAME='+EscapeString(Table.Name)+
|
' AND TABLE_NAME='+EscapeString(Table.Name)+
|
||||||
' AND k.REFERENCED_TABLE_NAME IS NOT NULL'+
|
' AND REFERENCED_TABLE_NAME IS NOT NULL'
|
||||||
' AND k.CONSTRAINT_SCHEMA = r.CONSTRAINT_SCHEMA'+
|
|
||||||
' AND k.TABLE_NAME=r.TABLE_NAME'+
|
|
||||||
' AND k.CONSTRAINT_NAME=r.CONSTRAINT_NAME'
|
|
||||||
);
|
);
|
||||||
ForeignKey := nil;
|
ColQuery := GetResults('SELECT *'+
|
||||||
while not ForeignQuery.Eof do begin
|
' FROM information_schema.KEY_COLUMN_USAGE'+
|
||||||
if (not Assigned(ForeignKey)) or (ForeignKey.KeyName <> ForeignQuery.Col('CONSTRAINT_NAME')) then begin
|
' WHERE'+
|
||||||
|
' CONSTRAINT_SCHEMA='+EscapeString(Table.Database)+
|
||||||
|
' AND TABLE_NAME='+EscapeString(Table.Name)+
|
||||||
|
' AND REFERENCED_TABLE_NAME IS NOT NULL'
|
||||||
|
);
|
||||||
|
try
|
||||||
|
while not ForeignQuery.Eof do begin
|
||||||
ForeignKey := TForeignKey.Create(Self);
|
ForeignKey := TForeignKey.Create(Self);
|
||||||
Result.Add(ForeignKey);
|
Result.Add(ForeignKey);
|
||||||
ForeignKey.KeyName := ForeignQuery.Col('CONSTRAINT_NAME');
|
ForeignKey.KeyName := ForeignQuery.Col('CONSTRAINT_NAME');
|
||||||
ForeignKey.OldKeyName := ForeignKey.KeyName;
|
ForeignKey.OldKeyName := ForeignKey.KeyName;
|
||||||
ForeignKey.ReferenceTable := ForeignQuery.Col('REFERENCED_TABLE_SCHEMA') +
|
ForeignKey.ReferenceTable := ForeignQuery.Col('UNIQUE_CONSTRAINT_SCHEMA') +
|
||||||
'.' + ForeignQuery.Col('REFERENCED_TABLE_NAME');
|
'.' + ForeignQuery.Col('REFERENCED_TABLE_NAME');
|
||||||
ForeignKey.OnUpdate := ForeignQuery.Col('UPDATE_RULE');
|
ForeignKey.OnUpdate := ForeignQuery.Col('UPDATE_RULE');
|
||||||
ForeignKey.OnDelete := ForeignQuery.Col('DELETE_RULE');
|
ForeignKey.OnDelete := ForeignQuery.Col('DELETE_RULE');
|
||||||
|
while not ColQuery.Eof do begin
|
||||||
|
if ColQuery.Col('CONSTRAINT_NAME') = ForeignQuery.Col('CONSTRAINT_NAME') then begin
|
||||||
|
ForeignKey.Columns.Add(ColQuery.Col('COLUMN_NAME'));
|
||||||
|
ForeignKey.ForeignColumns.Add(ColQuery.Col('REFERENCED_COLUMN_NAME'));
|
||||||
|
end;
|
||||||
|
ColQuery.Next;
|
||||||
|
end;
|
||||||
|
ColQuery.First;
|
||||||
|
ForeignQuery.Next;
|
||||||
end;
|
end;
|
||||||
ForeignKey.Columns.Add(ForeignQuery.Col('COLUMN_NAME'));
|
ForeignQuery.Free;
|
||||||
ForeignKey.ForeignColumns.Add(ForeignQuery.Col('REFERENCED_COLUMN_NAME'));
|
ColQuery.Free;
|
||||||
ForeignQuery.Next;
|
except
|
||||||
|
// Don't silence errors here:
|
||||||
|
on E:EDbError do
|
||||||
|
Log(lcError, E.Message);
|
||||||
end;
|
end;
|
||||||
ForeignQuery.Free;
|
|
||||||
except
|
except
|
||||||
// Silently ignore non existent IS tables and/or columns
|
// Silently ignore non existent IS tables and/or columns
|
||||||
on E:EDbError do;
|
// And remember to not fire such queries again here
|
||||||
|
on E:EDbError do begin
|
||||||
|
FForeignKeyQueriesFailed := True;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user