* Fix microseconds in MSSQL date/time data types, hidden in data and query grids.

* Add support for microsecond precision of MSSQL date/time types in table editor, show these in "Length/Set" column
* See http://www.heidisql.com/forum.php?t=17728
This commit is contained in:
Ansgar Becker
2015-03-01 18:21:59 +00:00
parent 74197fc600
commit 5a5eaabeef
3 changed files with 56 additions and 38 deletions

View File

@ -2111,7 +2111,7 @@ begin
if ((ServerVersionInt >= 50300) and Parameters.IsMariaDB) or if ((ServerVersionInt >= 50300) and Parameters.IsMariaDB) or
((ServerVersionInt >= 50604) and (not Parameters.IsMariaDB)) then begin ((ServerVersionInt >= 50604) and (not Parameters.IsMariaDB)) then begin
for i:=Low(FDatatypes) to High(FDatatypes) do begin for i:=Low(FDatatypes) to High(FDatatypes) do begin
if FDatatypes[i].Index in [dtDatetime, dtTime, dtTimestamp] then if FDatatypes[i].Index in [dtDatetime, dtDatetime2, dtTime, dtTimestamp] then
FDatatypes[i].HasLength := True; FDatatypes[i].HasLength := True;
end; end;
end; end;
@ -2644,13 +2644,20 @@ begin
DataType := Cols.Col('DATA_TYPE'); DataType := Cols.Col('DATA_TYPE');
DataType := DataType.ToUpper.DeQuotedString('"'); DataType := DataType.ToUpper.DeQuotedString('"');
Result := Result + CRLF + #9 + QuoteIdent(Cols.Col('COLUMN_NAME')) + ' ' + DataType; Result := Result + CRLF + #9 + QuoteIdent(Cols.Col('COLUMN_NAME')) + ' ' + DataType;
MaxLen := '';
if not Cols.IsNull('CHARACTER_MAXIMUM_LENGTH') then begin if not Cols.IsNull('CHARACTER_MAXIMUM_LENGTH') then begin
MaxLen := Cols.Col('CHARACTER_MAXIMUM_LENGTH'); MaxLen := Cols.Col('CHARACTER_MAXIMUM_LENGTH');
if MaxLen = '-1' then if MaxLen = '-1' then
Result := Result + '(max)' MaxLen := 'max';
else end else if not Cols.IsNull('NUMERIC_PRECISION') then begin
Result := Result + '(' + MaxLen + ')'; MaxLen := Cols.Col('NUMERIC_PRECISION');
if not Cols.IsNull('NUMERIC_SCALE') then
MaxLen := MaxLen + ',' + Cols.Col('NUMERIC_SCALE');
end else if not Cols.IsNull('DATETIME_PRECISION') then begin
MaxLen := Cols.Col('DATETIME_PRECISION');
end; end;
if not MaxLen.IsEmpty then
Result := Result + '(' + MaxLen + ')';
if Cols.Col('IS_NULLABLE') = 'NO' then if Cols.Col('IS_NULLABLE') = 'NO' then
Result := Result + ' NOT'; Result := Result + ' NOT';
Result := Result + ' NULL'; Result := Result + ' NULL';
@ -3827,33 +3834,11 @@ end;
function TDBConnection.GetDateTimeValue(Input: String; Datatype: TDBDatatypeIndex): String; function TDBConnection.GetDateTimeValue(Input: String; Datatype: TDBDatatypeIndex): String;
var
dt: TDateTime;
begin begin
// Return date/time string value as expected by server // Return date/time string value as expected by server
case Parameters.NetTypeGroup of // Not sure why there was a conversion required in earlier versions.
ngMSSQL: begin
try
dt := StrToDateTime(Input);
case Datatype of
dtDate:
Result := SysUtils.FormatDateTime('yyyy"-"mm"-"dd', dt);
dtTime:
Result := SysUtils.FormatDateTime('hh":"nn":"ss', dt);
dtYear:
Result := SysUtils.FormatDateTime('yyyy', dt);
dtDatetime:
Result := SysUtils.FormatDateTime('yyyy"-"mm"-"dd hh":"nn":"ss', dt);
end;
except
on E:EConvertError do
Result := Input; Result := Input;
end; end;
end;
else
Result := Input;
end;
end;
@ -5554,6 +5539,8 @@ begin
case Datatype(Column).Category of case Datatype(Column).Category of
dtcReal: dtcReal:
Result := FloatToStr(FCurrentResults.Fields[Column].AsExtended, FFormatSettings); Result := FloatToStr(FCurrentResults.Fields[Column].AsExtended, FFormatSettings);
dtcTemporal:
Result := FormatDateTime(Datatype(Column).Format, FCurrentResults.Fields[Column].AsFloat);
else else
Result := FCurrentResults.Fields[Column].AsString; Result := FCurrentResults.Fields[Column].AsString;
end; end;
@ -5792,9 +5779,10 @@ begin
break; break;
end; end;
end; end;
if idx = -1 then if idx > -1 then
raise EDatabaseError.CreateFmt(_('Column "%s" not available.'), [Column]); Result := IsNull(idx)
Result := IsNull(idx); else
Result := True;
end; end;

View File

@ -8,7 +8,7 @@ uses
Windows, Forms, Graphics, Messages, VirtualTrees, ComCtrls, SysUtils, Classes, Windows, Forms, Graphics, Messages, VirtualTrees, ComCtrls, SysUtils, Classes,
StdCtrls, ExtCtrls, CheckLst, Controls, Types, Dialogs, Menus, Mask, DateUtils, Math, StdCtrls, ExtCtrls, CheckLst, Controls, Types, Dialogs, Menus, Mask, DateUtils, Math,
dbconnection, mysql_structures, helpers, texteditor, bineditor, gnugettext, dbconnection, mysql_structures, helpers, texteditor, bineditor, gnugettext,
StrUtils, System.UITypes; StrUtils, System.UITypes, SynRegExpr;
type type
// Radio buttons and checkboxes which do not pass <Enter> key to their parent control // Radio buttons and checkboxes which do not pass <Enter> key to their parent control
@ -562,7 +562,7 @@ begin
case FTableColumn.DataType.Index of case FTableColumn.DataType.Index of
dtDate: dtDate:
FMaskEdit.EditMask := '0000-00-00;1; '; FMaskEdit.EditMask := '0000-00-00;1; ';
dtDatetime, dtTimestamp, dtInt, dtBigint: begin dtDatetime, dtDatetime2, dtTimestamp, dtInt, dtBigint: begin
if MicroSecondsPrecision > 0 then if MicroSecondsPrecision > 0 then
FMaskEdit.EditMask := '0000-00-00 00\:00\:00.'+StringOfChar('0', MicroSecondsPrecision)+';1; ' FMaskEdit.EditMask := '0000-00-00 00\:00\:00.'+StringOfChar('0', MicroSecondsPrecision)+';1; '
else else
@ -738,7 +738,7 @@ begin
text := DateToStr(d); text := DateToStr(d);
end; end;
dtDateTime, dtTimestamp, dtInt, dtBigint: begin dtDateTime, dtDateTime2, dtTimestamp, dtInt, dtBigint: begin
dt := StrToDateTime(FMaskEdit.Text); dt := StrToDateTime(FMaskEdit.Text);
case FMaskEdit.SelStart of case FMaskEdit.SelStart of
0..3: dt := IncYear(dt, Offset); 0..3: dt := IncYear(dt, Offset);
@ -803,8 +803,22 @@ end;
function TDateTimeEditorLink.MicroSecondsPrecision: Integer; function TDateTimeEditorLink.MicroSecondsPrecision: Integer;
var
rx: TRegExpr;
begin begin
Result := MakeInt(FTableColumn.LengthSet); if not FTableColumn.LengthSet.IsEmpty then
Result := MakeInt(FTableColumn.LengthSet)
else begin
// Find default length of supported microseconds in datatype definition
// See mysql_structures
rx := TRegExpr.Create;
rx.Expression := '\.([^\.]+)$';
if rx.Exec(FTableColumn.DataType.Format) then
Result := rx.MatchLen[1]
else
Result := 0;
rx.Free;
end;
// No microseconds for UNIX timestamp columns // No microseconds for UNIX timestamp columns
if FTableColumn.DataType.Index in [dtInt, dtBigint] then if FTableColumn.DataType.Index in [dtInt, dtBigint] then
Result := 0; Result := 0;

View File

@ -183,7 +183,7 @@ type
// MySQL data types // MySQL data types
TDBDatatypeIndex = (dtTinyint, dtSmallint, dtMediumint, dtInt, dtBigint, dtSerial, dtBigSerial, TDBDatatypeIndex = (dtTinyint, dtSmallint, dtMediumint, dtInt, dtBigint, dtSerial, dtBigSerial,
dtFloat, dtDouble, dtDecimal, dtNumeric, dtReal, dtDoublePrecision, dtMoney, dtSmallmoney, dtFloat, dtDouble, dtDecimal, dtNumeric, dtReal, dtDoublePrecision, dtMoney, dtSmallmoney,
dtDate, dtTime, dtYear, dtDatetime, dtSmalldatetime, dtTimestamp, dtInterval, dtDate, dtTime, dtYear, dtDatetime, dtDatetime2, dtSmalldatetime, dtTimestamp, dtInterval,
dtChar, dtNchar, dtVarchar, dtNvarchar, dtTinytext, dtText, dtNtext, dtMediumtext, dtLongtext, dtChar, dtNchar, dtVarchar, dtNvarchar, dtTinytext, dtText, dtNtext, dtMediumtext, dtLongtext,
dtBinary, dtVarbinary, dtTinyblob, dtBlob, dtMediumblob, dtLongblob, dtImage, dtBinary, dtVarbinary, dtTinyblob, dtBlob, dtMediumblob, dtLongblob, dtImage,
dtEnum, dtSet, dtBit, dtVarBit, dtBool, dtJson, dtUnknown, dtEnum, dtSet, dtBit, dtVarBit, dtBool, dtJson, dtUnknown,
@ -207,6 +207,7 @@ type
HasBinary: Boolean; // Can be binary? HasBinary: Boolean; // Can be binary?
HasDefault: Boolean; // Can have a default value? HasDefault: Boolean; // Can have a default value?
DefLengthSet: String; // Should be set for types which require a length/set DefLengthSet: String; // Should be set for types which require a length/set
Format: String; // Used for date/time values when displaying and generating queries
Category: TDBDatatypeCategoryIndex; Category: TDBDatatypeCategoryIndex;
end; end;
@ -403,6 +404,7 @@ var
RequiresLength: False; RequiresLength: False;
HasBinary: False; HasBinary: False;
HasDefault: True; HasDefault: True;
Format: 'yyyy-mm-dd';
Category: dtcTemporal; Category: dtcTemporal;
), ),
( (
@ -417,6 +419,7 @@ var
RequiresLength: False; RequiresLength: False;
HasBinary: False; HasBinary: False;
HasDefault: True; HasDefault: True;
Format: 'hh:nn:ss';
Category: dtcTemporal; Category: dtcTemporal;
), ),
( (
@ -434,6 +437,7 @@ var
RequiresLength: False; RequiresLength: False;
HasBinary: False; HasBinary: False;
HasDefault: True; HasDefault: True;
Format: 'yyyy';
Category: dtcTemporal; Category: dtcTemporal;
), ),
( (
@ -449,6 +453,7 @@ var
RequiresLength: False; RequiresLength: False;
HasBinary: False; HasBinary: False;
HasDefault: True; HasDefault: True;
Format: 'yyyy-mm-dd hh:nn:ss';
Category: dtcTemporal; Category: dtcTemporal;
), ),
( (
@ -466,6 +471,7 @@ var
RequiresLength: False; RequiresLength: False;
HasBinary: False; HasBinary: False;
HasDefault: True; HasDefault: True;
Format: 'yyyy-mm-dd hh:nn:ss';
Category: dtcTemporal; Category: dtcTemporal;
), ),
( (
@ -941,6 +947,7 @@ var
RequiresLength: False; RequiresLength: False;
HasBinary: False; HasBinary: False;
HasDefault: True; HasDefault: True;
Format: 'hh:nn:ss';
Category: dtcTemporal; Category: dtcTemporal;
), ),
( (
@ -953,6 +960,7 @@ var
RequiresLength: False; RequiresLength: False;
HasBinary: False; HasBinary: False;
HasDefault: True; HasDefault: True;
Format: 'yyyy-mm-dd';
Category: dtcTemporal; Category: dtcTemporal;
), ),
( (
@ -963,16 +971,18 @@ var
RequiresLength: False; RequiresLength: False;
HasBinary: False; HasBinary: False;
HasDefault: True; HasDefault: True;
Format: 'yyyy-mm-dd hh:nn:ss.zzz';
Category: dtcTemporal; Category: dtcTemporal;
), ),
( (
Index: dtDatetime; Index: dtDatetime2;
Name: 'DATETIME2'; Name: 'DATETIME2';
Description: 'Date and time data from January 1,1 AD through December 31, 9999 AD, with an accuracy of three-hundredths of a second, or 3.33 milliseconds.'; Description: 'Date and time data from January 1,1 AD through December 31, 9999 AD, with an accuracy of three-hundredths of a second, or 3.33 milliseconds.';
HasLength: False; HasLength: True;
RequiresLength: False; RequiresLength: False;
HasBinary: False; HasBinary: False;
HasDefault: True; HasDefault: True;
Format: 'yyyy-mm-dd hh:nn:ss.zzzzzzz';
Category: dtcTemporal; Category: dtcTemporal;
), ),
( (
@ -983,6 +993,7 @@ var
RequiresLength: False; RequiresLength: False;
HasBinary: False; HasBinary: False;
HasDefault: True; HasDefault: True;
Format: 'yyyy-mm-dd hh:nn:ss';
Category: dtcTemporal; Category: dtcTemporal;
), ),
( (
@ -1329,6 +1340,7 @@ var
RequiresLength: False; RequiresLength: False;
HasBinary: False; HasBinary: False;
HasDefault: False; HasDefault: False;
Format: 'yyyy-mm-dd';
Category: dtcTemporal; Category: dtcTemporal;
), ),
( (
@ -1340,6 +1352,7 @@ var
RequiresLength: False; RequiresLength: False;
HasBinary: False; HasBinary: False;
HasDefault: False; HasDefault: False;
Format: 'hh:nn:ss';
Category: dtcTemporal; Category: dtcTemporal;
), ),
( (
@ -1352,6 +1365,7 @@ var
RequiresLength: False; RequiresLength: False;
HasBinary: False; HasBinary: False;
HasDefault: False; HasDefault: False;
Format: 'yyyy-mm-dd hh:nn:ss';
Category: dtcTemporal; Category: dtcTemporal;
), ),
( (
@ -1363,6 +1377,7 @@ var
RequiresLength: False; RequiresLength: False;
HasBinary: False; HasBinary: False;
HasDefault: False; HasDefault: False;
Format: 'yyyy-mm-dd';
Category: dtcTemporal; Category: dtcTemporal;
), ),
( (
@ -1374,6 +1389,7 @@ var
RequiresLength: False; RequiresLength: False;
HasBinary: False; HasBinary: False;
HasDefault: False; HasDefault: False;
Format: 'yyyy-mm-dd hh:nn:ss';
Category: dtcTemporal; Category: dtcTemporal;
), ),
( (