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:
Ansgar Becker
2013-08-05 04:15:01 +00:00
parent 572d336d8b
commit 58b2a66800
7 changed files with 72 additions and 55 deletions

View File

@@ -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."

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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

View File

@@ -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);