mirror of
https://github.com/HeidiSQL/HeidiSQL.git
synced 2026-03-13 09:24:25 +08:00
Get column names from VIEW per IS.COLUMNS, not per SHOW COLUMNS, to support MSSQL here. Fixes empty grid results for MSSQL VIEWs. See comment #46 of issue #3212.
This commit is contained in:
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: HeidiSQL\n"
|
||||
"POT-Creation-Date: 2012-11-05 21:40\n"
|
||||
"PO-Revision-Date: 2013-03-09 08:32+0100\n"
|
||||
"PO-Revision-Date: 2013-08-04 20:55+0100\n"
|
||||
"Last-Translator: Ansgar Becker <heidisql@anse.de>\n"
|
||||
"Language-Team: English (http://www.transifex.com/projects/p/heidisql/"
|
||||
"language/en/)\n"
|
||||
@@ -5634,3 +5634,6 @@ msgstr "Clear data tab filter"
|
||||
|
||||
msgid "Error when updating query history: %s"
|
||||
msgstr "Error when updating query history: %s"
|
||||
|
||||
msgid "Could not find table or view %s.%s. Please refresh database tree."
|
||||
msgstr "Could not find table or view %s.%s. Please refresh database tree."
|
||||
|
||||
@@ -136,7 +136,7 @@ begin
|
||||
FForeignKeys.Clear;
|
||||
case FDBObj.NodeType of
|
||||
lntTable: FConnection.ParseTableStructure(FDBObj.CreateCode, FColumns, FKeys, FForeignKeys);
|
||||
lntView: FConnection.ParseViewStructure(FDBObj.CreateCode, FDBObj.Name, FColumns, Dummy, Dummy, Dummy, Dummy, Dummy);
|
||||
lntView: FConnection.ParseViewStructure(FDBObj.CreateCode, FDBObj, FColumns, Dummy, Dummy, Dummy, Dummy, Dummy);
|
||||
else raise Exception.CreateFmt(_('Neither table nor view: %s'), [FDBObj.Name]);
|
||||
end;
|
||||
|
||||
|
||||
@@ -344,7 +344,7 @@ type
|
||||
procedure ClearDbObjects(db: String);
|
||||
procedure ClearAllDbObjects;
|
||||
procedure ParseTableStructure(CreateTable: String; Columns: TTableColumnList; Keys: TTableKeyList; ForeignKeys: TForeignKeyList);
|
||||
procedure ParseViewStructure(CreateCode, ViewName: String; Columns: TTableColumnList;
|
||||
procedure ParseViewStructure(CreateCode: String; DBObj: TDBObject; Columns: TTableColumnList;
|
||||
var Algorithm, Definer, SQLSecurity, CheckOption, SelectCode: String);
|
||||
procedure ParseRoutineStructure(Obj: TDBObject; Parameters: TRoutineParamList);
|
||||
function GetDatatypeByName(Datatype: String): TDBDatatype;
|
||||
@@ -1780,9 +1780,10 @@ end;
|
||||
function TMySQLConnection.GetCreateViewCode(Database, Name: String): String;
|
||||
var
|
||||
ViewIS: TDBQuery;
|
||||
ViewName, Algorithm, CheckOption, SelectCode, Definer, SQLSecurity: String;
|
||||
Algorithm, CheckOption, SelectCode, Definer, SQLSecurity: String;
|
||||
AlternativeSelectCode: String;
|
||||
rx: TRegExpr;
|
||||
Obj: TDBObject;
|
||||
begin
|
||||
// Get CREATE VIEW code, which can throw privilege errors and errors due to
|
||||
// references to renamed or deleted columns
|
||||
@@ -1809,7 +1810,8 @@ begin
|
||||
rx.Expression := '\nsource\=(.+)\n\w+\=';
|
||||
if rx.Exec(AlternativeSelectCode) then begin
|
||||
// Put pieces of CREATE VIEW together
|
||||
ParseViewStructure(Result, ViewName, nil,
|
||||
Obj := FindObject(Database, Name);
|
||||
ParseViewStructure(Result, Obj, nil,
|
||||
Algorithm, Definer, SQLSecurity, CheckOption, SelectCode);
|
||||
AlternativeSelectCode := UnescapeString(rx.Match[1]);
|
||||
Result := 'CREATE ';
|
||||
@@ -1817,7 +1819,7 @@ begin
|
||||
Result := Result + 'ALGORITHM='+Uppercase(Algorithm)+' ';
|
||||
if Definer <> '' then
|
||||
Result := Result + 'DEFINER='+QuoteIdent(Definer, True, '@')+' ';
|
||||
Result := Result + 'VIEW '+QuoteIdent(Database)+'.'+QuoteIdent(Name)+' AS '+AlternativeSelectCode+' ';
|
||||
Result := Result + 'VIEW '+Obj.QuotedDbAndTableName+' AS '+AlternativeSelectCode+' ';
|
||||
// WITH .. CHECK OPTION is already contained in the source
|
||||
end;
|
||||
rx.Free;
|
||||
@@ -3527,13 +3529,13 @@ begin
|
||||
end;
|
||||
|
||||
|
||||
procedure TDBConnection.ParseViewStructure(CreateCode, ViewName: String; Columns: TTableColumnList;
|
||||
procedure TDBConnection.ParseViewStructure(CreateCode: String; DBObj: TDBObject; Columns: TTableColumnList;
|
||||
var Algorithm, Definer, SQLSecurity, CheckOption, SelectCode: String);
|
||||
var
|
||||
rx: TRegExpr;
|
||||
Col: TTableColumn;
|
||||
Results: TDBQuery;
|
||||
DbName, DbAndViewName: String;
|
||||
SchemaClause: String;
|
||||
begin
|
||||
if CreateCode <> '' then begin
|
||||
// CREATE
|
||||
@@ -3557,10 +3559,6 @@ begin
|
||||
if rx.Exec(CreateCode) then begin
|
||||
Algorithm := rx.Match[3];
|
||||
Definer := DeQuoteIdent(rx.Match[5], '@');
|
||||
// When exporting a view we need the db name for the below SHOW COLUMNS query,
|
||||
// if the connection is on a different db currently
|
||||
DbName := DeQuoteIdent(rx.Match[8]);
|
||||
ViewName := DeQuoteIdent(rx.Match[9]);
|
||||
CheckOption := Trim(rx.Match[13]);
|
||||
SelectCode := rx.Match[11];
|
||||
end else
|
||||
@@ -3568,31 +3566,51 @@ begin
|
||||
rx.Free;
|
||||
end;
|
||||
|
||||
// Views reveal their columns only with a SHOW COLUMNS query.
|
||||
// No keys available in views - SHOW KEYS always returns an empty result
|
||||
if Assigned(Columns) and (Parameters.NetTypeGroup=ngMySQL) then begin
|
||||
if Assigned(Columns) then begin
|
||||
Columns.Clear;
|
||||
rx := TRegExpr.Create;
|
||||
rx.Expression := '^(\w+)(\((.+)\))?';
|
||||
if DbName <> '' then
|
||||
DbAndViewName := QuoteIdent(DbName)+'.';
|
||||
DbAndViewName := DbAndViewName + QuoteIdent(ViewName);
|
||||
Results := GetResults('SHOW /*!32332 FULL */ COLUMNS FROM '+DbAndViewName);
|
||||
rx.Expression := '(\((.+)\))?(\s+unsigned)?';
|
||||
SchemaClause := '';
|
||||
if DBObj.Schema <> '' then
|
||||
SchemaClause := 'AND v.TABLE_SCHEMA='+EscapeString(DBObj.Schema);
|
||||
Results := GetResults('SELECT c.* '+
|
||||
'FROM INFORMATION_SCHEMA.VIEWS AS v '+
|
||||
'JOIN INFORMATION_SCHEMA.COLUMNS AS c ON '+
|
||||
' c.TABLE_CATALOG=v.TABLE_CATALOG '+
|
||||
' AND c.TABLE_SCHEMA=v.TABLE_SCHEMA '+
|
||||
' AND c.TABLE_NAME=v.TABLE_NAME '+
|
||||
'WHERE '+
|
||||
' v.TABLE_NAME='+EscapeString(DBObj.Name)+' '+
|
||||
' AND v.'+GetSQLSpecifity(spISTableSchemaCol)+'='+EscapeString(DBObj.Database)+' '+
|
||||
SchemaClause
|
||||
);
|
||||
while not Results.Eof do begin
|
||||
Col := TTableColumn.Create(Self);
|
||||
Columns.Add(Col);
|
||||
Col.Name := Results.Col('Field');
|
||||
Col.AllowNull := Results.Col('Null') = 'YES';
|
||||
if rx.Exec(Results.Col('Type')) then begin
|
||||
Col.DataType := GetDatatypeByName(rx.Match[1]);
|
||||
Col.LengthSet := rx.Match[3];
|
||||
Col.Name := Results.Col('COLUMN_NAME');
|
||||
Col.AllowNull := UpperCase(Results.Col('IS_NULLABLE')) = 'YES';
|
||||
Col.DataType := GetDatatypeByName(Results.Col('DATA_TYPE'));
|
||||
if Results.ColExists('COLUMN_TYPE') then begin
|
||||
// Use MySQL's proprietary column_type - the only way to get SET and ENUM values
|
||||
if rx.Exec(Results.Col('COLUMN_TYPE')) then begin
|
||||
Col.LengthSet := rx.Match[2];
|
||||
Col.Unsigned := (Col.DataType.Category in [dtcInteger, dtcReal]) and (rx.Match[3] <> '');
|
||||
end;
|
||||
end else begin
|
||||
if not Results.IsNull('CHARACTER_MAXIMUM_LENGTH') then begin
|
||||
Col.LengthSet := Results.Col('CHARACTER_MAXIMUM_LENGTH');
|
||||
end else if not Results.IsNull('NUMERIC_PRECISION') then begin
|
||||
Col.LengthSet := Results.Col('NUMERIC_PRECISION');
|
||||
if not Results.IsNull('NUMERIC_SCALE') then
|
||||
Col.LengthSet := Col.LengthSet + ',' + Results.Col('NUMERIC_SCALE');
|
||||
end;
|
||||
if Col.LengthSet = '-1' then
|
||||
Col.LengthSet := 'max';
|
||||
end;
|
||||
Col.Unsigned := (Col.DataType.Category = dtcInteger) and (Pos('unsigned', Results.Col('Type')) > 0);
|
||||
Col.AllowNull := UpperCase(Results.Col('Null')) = 'YES';
|
||||
Col.Collation := Results.Col('Collation', True);
|
||||
Col.Comment := Results.Col('Comment', True);
|
||||
Col.DefaultText := Results.Col('Default');
|
||||
if Results.IsNull('Default') then begin
|
||||
Col.Collation := Results.Col('COLLATION_NAME');
|
||||
Col.Comment := Results.Col('COLUMN_COMMENT', True);
|
||||
Col.DefaultText := Results.Col('COLUMN_DEFAULT');
|
||||
if Results.IsNull('COLUMN_DEFAULT') then begin
|
||||
if Col.AllowNull then
|
||||
Col.DefaultType := cdtNull
|
||||
else
|
||||
@@ -4416,45 +4434,41 @@ end;
|
||||
|
||||
procedure TDBQuery.PrepareEditing;
|
||||
var
|
||||
CreateCode, Dummy, DB, ObjName, Schema: String;
|
||||
CreateCode, Dummy, DB: String;
|
||||
DBObjects: TDBObjectList;
|
||||
Obj: TDBObject;
|
||||
ObjType: TListNodeType;
|
||||
LObj, Obj: TDBObject;
|
||||
begin
|
||||
// Try to fetch column names and keys
|
||||
if FEditingPrepared then
|
||||
Exit;
|
||||
// This is probably a VIEW, so column names need to be fetched differently
|
||||
|
||||
if FDBObject <> nil then begin
|
||||
Schema := FDBObject.Schema;
|
||||
ObjType := FDBObject.NodeType;
|
||||
ObjName := FDBObject.Name;
|
||||
end else begin
|
||||
Obj := nil;
|
||||
if FDBObject <> nil then
|
||||
Obj := FDBObject
|
||||
else begin
|
||||
DB := DatabaseName;
|
||||
if DB = '' then
|
||||
DB := Connection.Database;
|
||||
DBObjects := Connection.GetDBObjects(DB);
|
||||
ObjType := lntTable;
|
||||
Schema := '';
|
||||
for Obj in DBObjects do begin
|
||||
if (Obj.NodeType in [lntTable, lntView]) and (Obj.Name = TableName) then begin
|
||||
ObjType := Obj.NodeType;
|
||||
ObjName := Obj.Name;
|
||||
Schema := Obj.Schema;
|
||||
for LObj in DBObjects do begin
|
||||
if (LObj.NodeType in [lntTable, lntView]) and (LObj.Name = TableName) then begin
|
||||
Obj := LObj;
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
if Obj = nil then
|
||||
raise EDatabaseError.Create(f_('Could not find table or view %s.%s. Please refresh database tree.', [DB, TableName]));
|
||||
end;
|
||||
CreateCode := Connection.GetCreateCode(DatabaseName, Schema, ObjName, ObjType);
|
||||
CreateCode := Connection.GetCreateCode(Obj.Database, Obj.Schema, Obj.Name, Obj.NodeType);
|
||||
FColumns := TTableColumnList.Create;
|
||||
FKeys := TTableKeyList.Create;
|
||||
FForeignKeys := TForeignKeyList.Create;
|
||||
case ObjType of
|
||||
case Obj.NodeType of
|
||||
lntTable:
|
||||
Connection.ParseTableStructure(CreateCode, FColumns, FKeys, FForeignKeys);
|
||||
lntView:
|
||||
Connection.ParseViewStructure(CreateCode, ObjName, FColumns, Dummy, Dummy, Dummy, Dummy, Dummy);
|
||||
Connection.ParseViewStructure(CreateCode, Obj, FColumns, Dummy, Dummy, Dummy, Dummy, Dummy);
|
||||
end;
|
||||
FreeAndNil(FUpdateData);
|
||||
FUpdateData := TUpdateData.Create(True);
|
||||
|
||||
@@ -256,7 +256,7 @@ begin
|
||||
if (Obj.Database=comboDatabase.Text) and (Obj.Name=comboTable.Text) then begin
|
||||
case Obj.NodeType of
|
||||
lntTable: Obj.Connection.ParseTableStructure(Obj.CreateCode, Columns, nil, nil);
|
||||
lntView: Obj.Connection.ParseViewStructure(Obj.CreateCode, Obj.Name, Columns, DummyStr, DummyStr, DummyStr, DummyStr, DummyStr);
|
||||
lntView: Obj.Connection.ParseViewStructure(Obj.CreateCode, Obj, Columns, DummyStr, DummyStr, DummyStr, DummyStr, DummyStr);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
@@ -5069,7 +5069,7 @@ var
|
||||
lntTable:
|
||||
Conn.ParseTableStructure(Obj.CreateCode, Columns, nil, nil);
|
||||
lntView:
|
||||
Conn.ParseViewStructure(Obj.CreateCode, Obj.Name, Columns, Dummy, Dummy, Dummy, Dummy, Dummy);
|
||||
Conn.ParseViewStructure(Obj.CreateCode, Obj, Columns, Dummy, Dummy, Dummy, Dummy, Dummy);
|
||||
end;
|
||||
for Col in Columns do begin
|
||||
Proposal.InsertList.Add(Col.Name);
|
||||
@@ -7375,7 +7375,7 @@ begin
|
||||
lntTable:
|
||||
FActiveDbObj.Connection.ParseTableStructure(FActiveDbObj.CreateCode, SelectedTableColumns, SelectedTableKeys, SelectedTableForeignKeys);
|
||||
lntView:
|
||||
FActiveDbObj.Connection.ParseViewStructure(FActiveDbObj.CreateCode, FActiveDbObj.Name, SelectedTableColumns, DummyStr, DummyStr, DummyStr, DummyStr, DummyStr);
|
||||
FActiveDbObj.Connection.ParseViewStructure(FActiveDbObj.CreateCode, FActiveDbObj, SelectedTableColumns, DummyStr, DummyStr, DummyStr, DummyStr, DummyStr);
|
||||
end;
|
||||
except on E:EDatabaseError do
|
||||
ErrorDialog(E.Message);
|
||||
|
||||
@@ -697,7 +697,7 @@ begin
|
||||
Columns := TTableColumnList.Create(True);
|
||||
case DBObj.NodeType of
|
||||
lntTable: DBObj.Connection.ParseTableStructure(DBObj.CreateCode, Columns, nil, nil);
|
||||
lntView: DBObj.Connection.ParseViewStructure(DBObj.CreateCode, DBObj.Name, Columns, Dummy, Dummy, Dummy, Dummy, Dummy);
|
||||
lntView: DBObj.Connection.ParseViewStructure(DBObj.CreateCode, DBObj, Columns, Dummy, Dummy, Dummy, Dummy, Dummy);
|
||||
else AddNotes(DBObj.Database, DBObj.Name, STRSKIPPED+'a '+LowerCase(DBObj.ObjType)+' does not contain rows.', '');
|
||||
end;
|
||||
if Columns.Count > 0 then begin
|
||||
@@ -1288,7 +1288,7 @@ begin
|
||||
if not FSecondExportPass then begin
|
||||
// Create temporary VIEW replacement
|
||||
ColumnList := TTableColumnList.Create(True);
|
||||
DBObj.Connection.ParseViewStructure(DBObj.CreateCode, DBObj.Name, ColumnList, Dummy, Dummy, Dummy, Dummy, Dummy);
|
||||
DBObj.Connection.ParseViewStructure(DBObj.CreateCode, DBObj, ColumnList, Dummy, Dummy, Dummy, Dummy, Dummy);
|
||||
Struc := '-- '+_('Creating temporary table to overcome VIEW dependency errors')+CRLF+
|
||||
'CREATE TABLE ';
|
||||
if ToDb then
|
||||
|
||||
@@ -72,7 +72,7 @@ begin
|
||||
if Obj.Name <> '' then begin
|
||||
// Edit mode
|
||||
editName.Text := Obj.Name;
|
||||
Obj.Connection.ParseViewStructure(Obj.CreateCode, Obj.Name, nil, Algorithm, Definer, SQLSecurity, CheckOption, SelectCode);
|
||||
Obj.Connection.ParseViewStructure(Obj.CreateCode, Obj, nil, Algorithm, Definer, SQLSecurity, CheckOption, SelectCode);
|
||||
comboDefiner.Text := Definer;
|
||||
rgAlgorithm.ItemIndex := rgAlgorithm.Items.IndexOf(Algorithm);
|
||||
rgCheck.ItemIndex := rgCheck.Items.IndexOf(CheckOption);
|
||||
|
||||
Reference in New Issue
Block a user