From aa6ed7d9ddf29ad8157d063e1f6eba11d4cf58c0 Mon Sep 17 00:00:00 2001 From: Ansgar Becker Date: Fri, 14 Nov 2014 17:09:08 +0000 Subject: [PATCH] Fix non working addition of new columns in MySQL. See http://www.heidisql.com/forum.php?t=16948 PostgreSQL: Detect all array style types as unknown type, e.g. TEXT[]. --- out/locale/en/LC_MESSAGES/default.po | 10 ++- source/dbconnection.pas | 119 +++++++++++++++------------ source/grideditlinks.pas | 4 +- source/table_editor.pas | 2 +- 4 files changed, 80 insertions(+), 55 deletions(-) diff --git a/out/locale/en/LC_MESSAGES/default.po b/out/locale/en/LC_MESSAGES/default.po index b4befb44..fa91bdc5 100644 --- a/out/locale/en/LC_MESSAGES/default.po +++ b/out/locale/en/LC_MESSAGES/default.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: HeidiSQL\n" "POT-Creation-Date: 2012-11-05 21:40\n" -"PO-Revision-Date: 2014-11-13 19:40+0100\n" +"PO-Revision-Date: 2014-11-14 18:02+0100\n" "Last-Translator: Ansgar Becker \n" "Language-Team: English (http://www.transifex.com/projects/p/heidisql/" "language/en/)\n" @@ -5818,3 +5818,11 @@ msgstr "Unknown datatype oid #%d. Fall back to %s." #. Error happening when facing unknown native data types, mainly in PostgreSQL tables and routines. Name of column or argument passed here as a hint for the user. msgid "Unknown datatype oid #%d for \"%s\". Fall back to %s." msgstr "Unknown datatype oid #%d for \"%s\". Fall back to %s." + +#. Error happening when facing unknown named data types. +msgid "Unknown datatype \"%s\" for \"%s\". Fall back to %s." +msgstr "Unknown datatype \"%s\" for \"%s\". Fall back to %s." + +#. Error happening when facing unknown named data types. Name of column or argument passed here as a hint for the user. +msgid "Unknown datatype \"%s\". Fall back to %s." +msgstr "Unknown datatype \"%s\". Fall back to %s." diff --git a/source/dbconnection.pas b/source/dbconnection.pas index 6cdf2a69..287f285a 100644 --- a/source/dbconnection.pas +++ b/source/dbconnection.pas @@ -347,7 +347,6 @@ type function ExtractIdentifier(var SQL: String): String; procedure ClearCache(IncludeDBObjects: Boolean); procedure FetchDbObjects(db: String; var Cache: TDBObjectList); virtual; abstract; - function NativeToNamedColumnType(NativeType: Integer; Identifier: String=''): TDBDatatype; procedure SetObjectNamesInSelectedDB; procedure SetLockedByThread(Value: TThread); virtual; procedure KeepAliveTimerEvent(Sender: TObject); @@ -387,7 +386,8 @@ type procedure ParseViewStructure(CreateCode: String; DBObj: TDBObject; Columns: TTableColumnList; var Algorithm, Definer, SQLSecurity, CheckOption, SelectCode: String); procedure ParseRoutineStructure(Obj: TDBObject; Parameters: TRoutineParamList); - function GetDatatypeByName(var DataType: String; DeleteFromSource: Boolean): TDBDatatype; + function GetDatatypeByName(var DataType: String; DeleteFromSource: Boolean; Identifier: String=''): TDBDatatype; + function GetDatatypeByNativeType(NativeType: Integer; Identifier: String=''): TDBDatatype; function ApplyLimitClause(QueryType, QueryBody: String; Limit, Offset: Int64): String; function LikeClauseTail: String; property Parameters: TConnectionParameters read FParameters write FParameters; @@ -1428,12 +1428,12 @@ begin end; -function TDBConnection.GetDatatypeByName(var DataType: String; DeleteFromSource: Boolean): TDBDatatype; +function TDBConnection.GetDatatypeByName(var DataType: String; DeleteFromSource: Boolean; Identifier: String=''): TDBDatatype; var i: Integer; Match: Boolean; rx: TRegExpr; - Types: String; + Types, tmp: String; begin rx := TRegExpr.Create; rx.ModifierI := True; @@ -1442,18 +1442,62 @@ begin Types := FDatatypes[i].Name; if FDatatypes[i].Names <> '' then Types := Types + '|' + FDatatypes[i].Names; - rx.Expression := '^(\"?)('+Types+')(\"?)(\s|\()'; - Match := rx.Exec(Datatype); + rx.Expression := '^('+Types+')\b(\[\])?'; + Match := rx.Exec(DataType); if Match then begin - if DeleteFromSource then - Delete(DataType, 1, rx.MatchLen[1]+rx.MatchLen[2]+rx.MatchLen[3]); - Result := FDatatypes[i]; + if (FParameters.NetTypeGroup = ngPgSQL) and (rx.MatchLen[2] > 0) then begin + // TODO: detect array style datatypes, e.g. TEXT[] + end else begin + if DeleteFromSource then + Delete(DataType, 1, rx.MatchLen[1]); + Result := FDatatypes[i]; + break; + end; + end; + end; + if (not Match) and (FParameters.NetTypeGroup = ngPgSQL) then begin + // Fall back to unknown type + Result := Datatypes[0]; + rx.Expression := '^(\S+)'; + if rx.Exec(DataType) then + tmp := rx.Match[1] + else + tmp := DataType; + if Identifier <> '' then + Log(lcError, f_('Unknown datatype "%s" for "%s". Fall back to %s.', [tmp, Identifier, Result.Name])) + else + Log(lcError, f_('Unknown datatype "%s". Fall back to %s.', [tmp, Result.Name])); + end; + rx.Free; +end; + + +function TDBConnection.GetDatatypeByNativeType(NativeType: Integer; Identifier: String=''): TDBDatatype; +var + i: Integer; + rx: TRegExpr; + TypeFound: Boolean; +begin + rx := TRegExpr.Create; + TypeFound := False; + for i:=0 to High(Datatypes) do begin + if Datatypes[i].NativeTypes = '' then + Continue; + rx.Expression := '\b('+Datatypes[i].NativeTypes+')\b'; + if rx.Exec(IntToStr(NativeType)) then begin + Result := Datatypes[i]; + TypeFound := True; break; end; end; - rx.Free; - if (not Match) and (FParameters.NetTypeGroup = ngPgSQL) then - Result := FDatatypes[0]; + if not TypeFound then begin + // Fall back to unknown type + Result := Datatypes[0]; + if Identifier <> '' then + Log(lcError, f_('Unknown datatype oid #%d for "%s". Fall back to %s.', [NativeType, Identifier, Result.Name])) + else + Log(lcError, f_('Unknown datatype oid #%d. Fall back to %s.', [NativeType, Result.Name])); + end; end; @@ -2537,7 +2581,7 @@ end; function TDBConnection.GetCreateCode(Database, Schema, Name: String; NodeType: TListNodeType): String; var Cols, Keys, ProcDetails: TDBQuery; - ConstraintName, MaxLen, ArgDataType: String; + ConstraintName, MaxLen, DataType: String; ColNames, ArgNames, ArgTypes, Arguments: TStringList; Rows: TStringList; i: Integer; @@ -2563,7 +2607,7 @@ begin Cols := GetResults('SELECT '+ ' DISTINCT a.attname AS column_name, '+ ' a.attnum, '+ - ' a.atttypid, '+ // Data type oid. See NativeToNamedColumnType() + ' a.atttypid, '+ // Data type oid. See GetDatatypeByNativeType() ' FORMAT_TYPE(a.atttypid, a.atttypmod) AS data_type, '+ ' CASE a.attnotnull WHEN false THEN '+EscapeString('YES')+' ELSE '+EscapeString('NO')+' END AS IS_NULLABLE, '+ ' com.description AS column_comment, '+ @@ -2592,7 +2636,9 @@ begin while not Cols.Eof do begin if Cols.ColExists('atttypid') then Log(lcDebug, 'Column "'+Cols.Col('COLUMN_NAME')+'" => oid #'+Cols.Col('atttypid')); - Result := Result + CRLF + #9 + QuoteIdent(Cols.Col('COLUMN_NAME')) + ' ' + UpperCase(Cols.Col('DATA_TYPE')); + DataType := Cols.Col('DATA_TYPE'); + DataType := DataType.ToUpper.DeQuotedString('"'); + Result := Result + CRLF + #9 + QuoteIdent(Cols.Col('COLUMN_NAME')) + ' ' + DataType; if not Cols.IsNull('CHARACTER_MAXIMUM_LENGTH') then begin MaxLen := Cols.Col('CHARACTER_MAXIMUM_LENGTH'); if MaxLen = '-1' then @@ -2724,13 +2770,13 @@ begin Arguments := TStringList.Create; for i:=0 to ArgNames.Count-1 do begin if ArgTypes.Count > i then - ArgDataType := NativeToNamedColumnType(MakeInt(ArgTypes[i]), ArgNames[i]).Name + DataType := GetDatatypeByNativeType(MakeInt(ArgTypes[i]), ArgNames[i]).Name else - ArgDataType := ''; - Arguments.Add(ArgNames[i] + ' ' + ArgDataType); + DataType := ''; + Arguments.Add(ArgNames[i] + ' ' + DataType); end; Result := Result + '(' + implodestr(', ', Arguments) + ') '+ - 'RETURNS '+NativeToNamedColumnType(MakeInt(ProcDetails.Col('prorettype'))).Name+' '+ + 'RETURNS '+GetDatatypeByNativeType(MakeInt(ProcDetails.Col('prorettype'))).Name+' '+ 'AS $$ '+ProcDetails.Col('prosrc')+' $$' // TODO: 'LANGUAGE SQL IMMUTABLE STRICT' ; @@ -4246,35 +4292,6 @@ begin end; -function TDBConnection.NativeToNamedColumnType(NativeType: Integer; Identifier: String=''): TDBDatatype; -var - i: Integer; - rx: TRegExpr; - TypeFound: Boolean; -begin - rx := TRegExpr.Create; - TypeFound := False; - for i:=0 to High(Datatypes) do begin - if Datatypes[i].NativeTypes = '' then - Continue; - rx.Expression := '\b('+Datatypes[i].NativeTypes+')\b'; - if rx.Exec(IntToStr(NativeType)) then begin - Result := Datatypes[i]; - TypeFound := True; - break; - end; - end; - if not TypeFound then begin - // Fall back to unknown type - Result := Datatypes[0]; - if Identifier <> '' then - Log(lcError, f_('Unknown datatype oid #%d for "%s". Fall back to %s.', [NativeType, Identifier, Result.Name])) - else - Log(lcError, f_('Unknown datatype oid #%d. Fall back to %s.', [NativeType, Result.Name])); - end; -end; - - procedure TDBConnection.SetObjectNamesInSelectedDB; var i: Integer; @@ -4476,7 +4493,7 @@ begin Col.LengthCustomized := False; // Datatype - Col.DataType := GetDatatypeByName(ColSpec, True); + Col.DataType := GetDatatypeByName(ColSpec, True, Col.Name); Col.OldDataType := Col.DataType; // Length / Set @@ -4726,7 +4743,7 @@ begin Col.Name := Results.Col('COLUMN_NAME'); Col.AllowNull := UpperCase(Results.Col('IS_NULLABLE')) = 'YES'; DataType := Results.Col('DATA_TYPE'); - Col.DataType := GetDatatypeByName(DataType, False); + Col.DataType := GetDatatypeByName(DataType, False, Col.Name); 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 @@ -5241,7 +5258,7 @@ begin FColumnOrgNames.Add(FColumnNames[FColumnNames.Count-1]); FieldTypeOID := PQftype(LastResult, i); TypeFound := False; - FColumnTypes[i] := FConnection.NativeToNamedColumnType(FieldTypeOID, FColumnNames[FColumnNames.Count-1]); + FColumnTypes[i] := FConnection.GetDatatypeByNativeType(FieldTypeOID, FColumnNames[FColumnNames.Count-1]); end; rx.Free; FRecNo := -1; diff --git a/source/grideditlinks.pas b/source/grideditlinks.pas index e172315e..366a6aa2 100644 --- a/source/grideditlinks.pas +++ b/source/grideditlinks.pas @@ -1505,7 +1505,7 @@ begin FTreeSelect.Font.Size := FCellFont.Size; // Find and select current datatype in tree - dt := FTableColumn.Connection.GetDataTypeByName(FCellText, False); + dt := FTableColumn.Connection.GetDataTypeByName(FCellText, False, FTableColumn.Name); CatNode := FTreeSelect.GetFirst; while Assigned(CatNode) do begin // Since recent update to VT 5.2.1 we need to initialize root nodes by hand for some reason: @@ -1629,7 +1629,7 @@ begin FMemoHelp.Width := Min(250, FTreeSelect.Left); FMemoHelp.Left := FTreeSelect.Left - FMemoHelp.Width + (Integer(FTreeSelect.Indent) Div 2); FMemoHelp.Top := FTreeSelect.Top + R.Top + 3; - FMemoHelp.Text := FTableColumn.Connection.GetDatatypeByName(NodeText, False).Description; + FMemoHelp.Text := FTableColumn.Connection.GetDatatypeByName(NodeText, False, FTableColumn.Name).Description; // Calc height of memo bmp := TBitMap.Create; bmp.Canvas.Font.Assign(FMemoHelp.Font); diff --git a/source/table_editor.pas b/source/table_editor.pas index 6691fbbe..35dd7ef0 100644 --- a/source/table_editor.pas +++ b/source/table_editor.pas @@ -1190,7 +1190,7 @@ begin Col.Name := NewText; end; 2: begin // Data type - Col.DataType := DBObject.Connection.GetDatatypeByName(NewText, False); + Col.DataType := DBObject.Connection.GetDatatypeByName(NewText, False, Col.Name); // Reset length/set for column types which don't support that if not Col.DataType.HasLength then Col.LengthSet := '';