View Issue Details

IDProjectCategoryView StatusLast Update
0033919FPCDatabasepublic2018-06-29 11:40
ReporterOndrej PokornyAssigned ToMichael Van Canneyt 
PrioritynormalSeverityminorReproducibilityalways
Status closedResolutionfixed 
Product VersionProduct Build 
Target Version3.2.0Fixed in Version3.1.1 
Summary0033919: MySQL: fix performance issues
DescriptionThe attached patch fixes performance issues in MySQL code:

1.) InternalStrToFloat and InternalStrToCurrency:

Honestly, the "Tmp := Tmp + ???" in a loop is a bad joke...

2.) TConnectionName.MySQLWriteData:

SetString(Src, Source, Len); is executed even if Src is not used. Bye bye performance for big blobs and strings.
TagsNo tags attached.
Fixed in Revision39339.
FPCOldBugId
FPCTarget
Attached Files
  • mysqlconn-performance-01.patch (6,828 bytes)
    Index: packages/fcl-db/src/sqldb/mysql/mysqlconn.inc
    ===================================================================
    --- packages/fcl-db/src/sqldb/mysql/mysqlconn.inc	(revision 39317)
    +++ packages/fcl-db/src/sqldb/mysql/mysqlconn.inc	(working copy)
    @@ -894,56 +894,96 @@
       ABlobBuf^.BlobBuffer^.Size := len;
     end;
     
    -function InternalStrToInt(const S: string): integer;
    +function InternalStrToInt(C: pchar; Len: integer): integer;
    +  procedure r;
    +  begin
    +    raise EConvertError.CreateFmt('"%s" is not a valid digit', [C^]);
    +  end;
    +var
    +  I: Integer;
     begin
    -  if S = '' then
    -    Result := 0
    -  else
    -    Result := StrToInt(S);
    +  Result := 0;
    +  for I := 0 to Len-1 do
    +  begin
    +    if C^=#0 then
    +      break;
    +    case C^ of
    +      '0'..'9': Result := Result*10 + Ord(C^)-Ord('0');
    +    else
    +      r;
    +    end;
    +    Inc(C);
    +  end;
     end;
     
    -function InternalStrToFloat(const S: string): Extended;
    -
    +function InternalStrToInt64(C: pchar; Len: integer): LargeInt;
    +  procedure r;
    +  begin
    +    raise EConvertError.CreateFmt('"%s" is not a valid digit', [C^]);
    +  end;
     var
       I: Integer;
    -  Tmp: string;
    -
     begin
    -  Tmp := '';
    -  for I := 1 to Length(S) do
    -    begin
    -    if not (S[I] in ['0'..'9', '+', '-', 'E', 'e']) then
    -      Tmp := Tmp + FormatSettings.DecimalSeparator
    +  Result := 0;
    +  for I := 0 to Len-1 do
    +  begin
    +    if C^=#0 then
    +      break;
    +    case C^ of
    +      '0'..'9': Result := Result*10 + Ord(C^)-Ord('0');
         else
    -      Tmp := Tmp + S[I];
    +      r;
         end;
    -  Result := StrToFloat(Tmp);
    +    Inc(C);
    +  end;
     end;
     
    -function InternalStrToCurrency(const S: string): Currency;
    +function InternalStrToFloat(C: pchar; Len: integer; const Format: TFormatSettings): Extended;
     
     var
    -  I: Integer;
       Tmp: string;
     
     begin
    -  Tmp := '';
    -  for I := 1 to Length(S) do
    -    begin
    -    if not (S[I] in ['0'..'9', '+', '-', 'E', 'e']) then
    -      Tmp := Tmp + FormatSettings.DecimalSeparator
    -    else
    -      Tmp := Tmp + S[I];
    -    end;
    -  Result := StrToCurr(Tmp);
    +  SetString(Tmp, C, Len);
    +  if Tmp='' then
    +    Exit(0);
    +  Result := StrToFloat(Tmp, Format);
     end;
     
    -function InternalStrToDate(const S: string): TDateTime;
    +function InternalStrToCurrency(C: pchar; Len: integer; const Format: TFormatSettings): Currency;
     
     var
    +  Tmp: string;
    +
    +begin
    +  SetString(Tmp, C, Len);
    +  if Tmp='' then
    +    Exit(0);
    +  Result := StrToCurr(Tmp, Format);
    +end;
    +
    +function InternalStrToBCD(C: pchar; Len: integer; const Format: TFormatSettings): tBCD;
    +
    +var
    +  Tmp: string;
    +
    +begin
    +  SetString(Tmp, C, Len);
    +  if Tmp='' then
    +    Exit(0);
    +  Result := StrToBCD(Tmp, Format);
    +end;
    +
    +function InternalStrToDate(C: pchar; Len: integer): TDateTime;
    +
    +var
       EY, EM, ED: Word;
    +  S: string;
     
     begin
    +  SetString(S, C, Len);
    +  if S='' then
    +    Exit(0);
       EY := StrToInt(Copy(S,1,4));
       EM := StrToInt(Copy(S,6,2));
       ED := StrToInt(Copy(S,9,2));
    @@ -972,13 +1012,17 @@
     {$ENDIF}
     end;
     
    -function InternalStrToDateTime(const S: string): TDateTime;
    +function InternalStrToDateTime(C: pchar; Len: integer): TDateTime;
     
     var
       EY, EM, ED: Word;
       EH, EN, ES, EMS: Word;
    +  S: string;
     
     begin
    +  SetString(S, C, Len);
    +  if S='' then
    +    Exit(0);
       EY := StrToInt(Copy(S, 1, 4));
       EM := StrToInt(Copy(S, 6, 2));
       ED := StrToInt(Copy(S, 9, 2));
    @@ -993,13 +1037,17 @@
       Result := ComposeDateTime(Result, EncodeTime(EH, EN, ES, EMS));
     end;
     
    -function InternalStrToTime(const S: string): TDateTime;
    +function InternalStrToTime(C: pchar; Len: integer): TDateTime;
     
     var
       EH, EM, ES, EMS: Word;
       p: integer;
    +  S: string;
     
     begin
    +  SetString(S, C, Len);
    +  if S='' then
    +    Exit(0);
       p := 1;
       EH := StrToInt(ExtractSubstr(S, p, [':'])); //hours can be 2 or 3 digits
       EM := StrToInt(ExtractSubstr(S, p, [':']));
    @@ -1008,13 +1056,17 @@
       Result := EncodeTimeInterval(EH, EM, ES, EMS);
     end;
     
    -function InternalStrToTimeStamp(const S: string): TDateTime;
    +function InternalStrToTimeStamp(C: pchar; Len: integer): TDateTime;
     
     var
       EY, EM, ED: Word;
       EH, EN, ES, EMS: Word;
    +  S: string;
     
     begin
    +  SetString(S, C, Len);
    +  if S='' then
    +    Exit(0);
     {$IFNDEF mysql40}
       EY := StrToInt(Copy(S, 1, 4));
       EM := StrToInt(Copy(S, 6, 2));
    @@ -1050,7 +1102,6 @@
       VC: Currency;
       VD: TDateTime;
       VB: TBCD;
    -  Src : String;
     
     begin
       Result := False;
    @@ -1057,22 +1108,21 @@
       CreateBlob := False;
       if Source = Nil then // If the pointer is NULL, the field is NULL
         exit;
    -  SetString(Src, Source, Len);
     
       case FieldDef.DataType of
         ftSmallint:
           begin
    -      VS := InternalStrToInt(Src);
    +      VS := InternalStrToInt(Source, Len);
           Move(VS, Dest^, SizeOf(Smallint));
           end;
         ftWord:
           begin
    -      VW := InternalStrToInt(Src);
    +      VW := InternalStrToInt(Source, Len);
           Move(VW, Dest^, SizeOf(Word));
           end;
         ftInteger, ftAutoInc:
           begin
    -      VI := InternalStrToInt(Src);
    +      VI := InternalStrToInt(Source, Len);
           Move(VI, Dest^, SizeOf(Integer));
           end;
         ftLargeInt:
    @@ -1086,55 +1136,40 @@
             end
           else
           {$ENDIF}
    -        if Src <> '' then
    -          VL := StrToInt64(Src)
    -        else
    -          VL := 0;
    +      VL := InternalStrToInt64(Source, Len);
           Move(VL, Dest^, SizeOf(LargeInt));
           end;
         ftFloat:
           begin
    -      if Src <> '' then
    -        VF := InternalStrToFloat(Src)
    -      else
    -        VF := 0;
    +      VF := InternalStrToFloat(Source, Len, FSQLFormatSettings);
           Move(VF, Dest^, SizeOf(Double));
           end;
         ftBCD:
           begin
    -      VC := InternalStrToCurrency(Src);
    +      VC := InternalStrToCurrency(Source, Len, FSQLFormatSettings);
           Move(VC, Dest^, SizeOf(Currency));
           end;
         ftFmtBCD:
           begin
    -      VB := StrToBCD(Src, FSQLFormatSettings);
    +      VB := InternalStrToBCD(Source, Len, FSQLFormatSettings);
           Move(VB, Dest^, SizeOf(TBCD));
           end;
         ftDate:
           begin
    -      if Src <> '' then
    -        VD := InternalStrToDate(Src)
    -      else
    -        VD := 0;
    +      VD := InternalStrToDate(Source, Len);
           Move(VD, Dest^, SizeOf(TDateTime));
           end;
         ftTime:
           begin
    -      if Src <> '' then
    -        VD := InternalStrToTime(Src)
    -      else
    -        VD := 0;
    +      VD := InternalStrToTime(Source, Len);
           Move(VD, Dest^, SizeOf(TDateTime));
           end;
         ftDateTime:
           begin
    -      if Src <> '' then
    -        if AField^.ftype = FIELD_TYPE_TIMESTAMP then
    -          VD := InternalStrToTimeStamp(Src)
    -        else
    -          VD := InternalStrToDateTime(Src)
    +      if AField^.ftype = FIELD_TYPE_TIMESTAMP then
    +        VD := InternalStrToTimeStamp(Source, Len)
           else
    -        VD := 0;
    +        VD := InternalStrToDateTime(Source, Len);
           Move(VD, Dest^, SizeOf(TDateTime));
           end;
         ftString, ftFixedChar:
    
  • mysqlconn-performance-02.patch (9,590 bytes)
    Index: packages/fcl-db/src/sqldb/mysql/mysqlconn.inc
    ===================================================================
    --- packages/fcl-db/src/sqldb/mysql/mysqlconn.inc	(revision 39317)
    +++ packages/fcl-db/src/sqldb/mysql/mysqlconn.inc	(working copy)
    @@ -894,59 +894,97 @@
       ABlobBuf^.BlobBuffer^.Size := len;
     end;
     
    -function InternalStrToInt(const S: string): integer;
    +function InternalStrToInt(C: pchar; Len: integer): integer;
    +  procedure r;
    +  begin
    +    raise EConvertError.CreateFmt('"%s" is not a valid digit', [C^]);
    +  end;
    +var
    +  I: Integer;
     begin
    -  if S = '' then
    -    Result := 0
    -  else
    -    Result := StrToInt(S);
    +  Result := 0;
    +  for I := 1 to Len do
    +  begin
    +    case C^ of
    +      '0'..'9': Result := Result*10 + Ord(C^)-Ord('0');
    +      #0: break;
    +    else
    +      r;
    +    end;
    +    Inc(C);
    +  end;
     end;
     
    -function InternalStrToFloat(const S: string): Extended;
    -
    +function InternalStrToInt64(C: pchar; Len: integer): LargeInt;
    +  procedure r;
    +  begin
    +    raise EConvertError.CreateFmt('"%s" is not a valid digit', [C^]);
    +  end;
     var
       I: Integer;
    -  Tmp: string;
    -
     begin
    -  Tmp := '';
    -  for I := 1 to Length(S) do
    -    begin
    -    if not (S[I] in ['0'..'9', '+', '-', 'E', 'e']) then
    -      Tmp := Tmp + FormatSettings.DecimalSeparator
    +  Result := 0;
    +  for I := 1 to Len do
    +  begin
    +    case C^ of
    +      '0'..'9': Result := Result*10 + Ord(C^)-Ord('0');
    +      #0: break;
         else
    -      Tmp := Tmp + S[I];
    +      r;
         end;
    -  Result := StrToFloat(Tmp);
    +    Inc(C);
    +  end;
     end;
     
    -function InternalStrToCurrency(const S: string): Currency;
    +function InternalStrToFloat(C: pchar; Len: integer; const Format: TFormatSettings): Extended;
     
     var
    -  I: Integer;
       Tmp: string;
     
     begin
    -  Tmp := '';
    -  for I := 1 to Length(S) do
    -    begin
    -    if not (S[I] in ['0'..'9', '+', '-', 'E', 'e']) then
    -      Tmp := Tmp + FormatSettings.DecimalSeparator
    -    else
    -      Tmp := Tmp + S[I];
    -    end;
    -  Result := StrToCurr(Tmp);
    +  SetString(Tmp, C, Len);
    +  if Tmp='' then
    +    Exit(0);
    +  Result := StrToFloat(Tmp, Format);
     end;
     
    -function InternalStrToDate(const S: string): TDateTime;
    +function InternalStrToCurrency(C: pchar; Len: integer; const Format: TFormatSettings): Currency;
     
     var
    +  Tmp: string;
    +
    +begin
    +  SetString(Tmp, C, Len);
    +  if Tmp='' then
    +    Exit(0);
    +  Result := StrToCurr(Tmp, Format);
    +end;
    +
    +function InternalStrToBCD(C: pchar; Len: integer; const Format: TFormatSettings): tBCD;
    +
    +var
    +  Tmp: string;
    +
    +begin
    +  SetString(Tmp, C, Len);
    +  if Tmp='' then
    +    Exit(0);
    +  Result := StrToBCD(Tmp, Format);
    +end;
    +
    +function InternalStrToDate(C: pchar; Len: integer): TDateTime;
    +
    +var
       EY, EM, ED: Word;
     
     begin
    -  EY := StrToInt(Copy(S,1,4));
    -  EM := StrToInt(Copy(S,6,2));
    -  ED := StrToInt(Copy(S,9,2));
    +  if Len=0 then
    +    Exit(0);
    +  if Len<10 then
    +    raise EConvertError.Create('Invalid date string');
    +  EY := InternalStrToInt(C,4);
    +  EM := InternalStrToInt(C+5,2);
    +  ED := InternalStrToInt(C+8,2);
       if (EY = 0) or (EM = 0) or (ED = 0) then
         Result:=0
       else
    @@ -953,9 +991,11 @@
         Result:=EncodeDate(EY, EM, ED);
     end;
     
    -function StrToMSecs(const S: string): Word;
    +function StrToMSecs(C: pchar; Len: integer): Word;
    +{$IFDEF MYSQL56_UP}
     var C: char;
         d, MSecs: double;
    +{$ENDIF}
     begin
     {$IFDEF MYSQL56_UP}
       // datetime(n), where n is fractional seconds precision (between 0 and 6)
    @@ -972,7 +1012,7 @@
     {$ENDIF}
     end;
     
    -function InternalStrToDateTime(const S: string): TDateTime;
    +function InternalStrToDateTime(C: pchar; Len: integer): TDateTime;
     
     var
       EY, EM, ED: Word;
    @@ -979,13 +1019,20 @@
       EH, EN, ES, EMS: Word;
     
     begin
    -  EY := StrToInt(Copy(S, 1, 4));
    -  EM := StrToInt(Copy(S, 6, 2));
    -  ED := StrToInt(Copy(S, 9, 2));
    -  EH := StrToInt(Copy(S, 12, 2));
    -  EN := StrToInt(Copy(S, 15, 2));
    -  ES := StrToInt(Copy(S, 18, 2));
    -  EMS:= StrToMSecs(Copy(S, 21, 6));
    +  if Len=0 then
    +    Exit(0);
    +  if Len<19 then
    +    raise EConvertError.Create('Invalid datetime string');
    +  EY := InternalStrToInt(C,4);
    +  EM := InternalStrToInt(C+5,2);
    +  ED := InternalStrToInt(C+8,2);
    +  EH := InternalStrToInt(C+11, 2);
    +  EN := InternalStrToInt(C+14, 2);
    +  ES := InternalStrToInt(C+17, 2);
    +  if Len>20 then
    +    EMS := StrToMSecs(C+20, Len-20)
    +  else
    +    EMS := 0;
       if (EY = 0) or (EM = 0) or (ED = 0) then
         Result := 0
       else
    @@ -993,51 +1040,64 @@
       Result := ComposeDateTime(Result, EncodeTime(EH, EN, ES, EMS));
     end;
     
    -function InternalStrToTime(const S: string): TDateTime;
    +function InternalStrToTime(C: pchar; Len: integer): TDateTime;
     
     var
       EH, EM, ES, EMS: Word;
    -  p: integer;
    +  M: PChar;
    +  I: Integer;
     
     begin
    -  p := 1;
    -  EH := StrToInt(ExtractSubstr(S, p, [':'])); //hours can be 2 or 3 digits
    -  EM := StrToInt(ExtractSubstr(S, p, [':']));
    -  ES := StrToInt(ExtractSubstr(S, p, ['.']));
    -  EMS:= StrToMSecs(Copy(S, p, 6));
    +  if Len=0 then
    +    Exit(0);
    +  if Len<8 then
    +    raise EConvertError.Create('Invalid time string');
    +  //hours can be 2 or 3 digits
    +  M:=C;
    +  for I := 1 to Len do
    +  begin
    +    if M^=':' then
    +      break;
    +    Inc(M);
    +  end;
    +  if M^<>':' then
    +    raise EConvertError.Create('Invalid time string');
    +
    +  EH := InternalStrToInt(C, NativeInt(M-C));
    +  EM := InternalStrToInt(M+1, 2);
    +  ES := InternalStrToInt(M+4, 2);
    +  if Len>NativeInt(M-C)+7 then
    +    EMS := StrToMSecs(M+7, Len-(NativeInt(M-C)+7))
    +  else
    +    EMS := 0;
       Result := EncodeTimeInterval(EH, EM, ES, EMS);
     end;
     
    -function InternalStrToTimeStamp(const S: string): TDateTime;
    +{$IFDEF mysql40}
    +function InternalStrToTimeStamp(C: pchar; Len: integer): TDateTime;
     
     var
       EY, EM, ED: Word;
    -  EH, EN, ES, EMS: Word;
    +  EH, EN, ES: Word;
     
     begin
    -{$IFNDEF mysql40}
    -  EY := StrToInt(Copy(S, 1, 4));
    -  EM := StrToInt(Copy(S, 6, 2));
    -  ED := StrToInt(Copy(S, 9, 2));
    -  EH := StrToInt(Copy(S, 12, 2));
    -  EN := StrToInt(Copy(S, 15, 2));
    -  ES := StrToInt(Copy(S, 18, 2));
    -  EMS:= StrToMSecs(Copy(S, 21, 6));
    -{$ELSE}
    -  EY := StrToInt(Copy(S, 1, 4));
    -  EM := StrToInt(Copy(S, 5, 2));
    -  ED := StrToInt(Copy(S, 7, 2));
    -  EH := StrToInt(Copy(S, 9, 2));
    -  EN := StrToInt(Copy(S, 11, 2));
    -  ES := StrToInt(Copy(S, 13, 2));
    -  EMS:= 0;
    -{$ENDIF}
    +  if Len=0 then
    +    Exit(0);
    +  if Len<14 then
    +    raise EConvertError.Create('Invalid timestamp string');
    +  EY := InternalStrToInt(C, 4);
    +  EM := InternalStrToInt(C+4, 2));
    +  ED := InternalStrToInt(C+6, 2));
    +  EH := InternalStrToInt(C+8, 2));
    +  EN := InternalStrToInt(C+10, 2));
    +  ES := InternalStrToInt(C+12, 2));
       if (EY = 0) or (EM = 0) or (ED = 0) then
         Result := 0
       else
         Result := EncodeDate(EY, EM, ED);
    -  Result := Result + EncodeTime(EH, EN, ES, EMS);
    +  Result := ComposeDateTime(Result, EncodeTime(EH, EN, ES, 0));
     end;
    +{$ENDIF}
     
     function TConnectionName.MySQLWriteData(AField: PMYSQL_FIELD; FieldDef: TFieldDef; Source, Dest: PChar; Len: integer; out CreateBlob : boolean): Boolean;
     
    @@ -1050,7 +1110,6 @@
       VC: Currency;
       VD: TDateTime;
       VB: TBCD;
    -  Src : String;
     
     begin
       Result := False;
    @@ -1057,22 +1116,21 @@
       CreateBlob := False;
       if Source = Nil then // If the pointer is NULL, the field is NULL
         exit;
    -  SetString(Src, Source, Len);
     
       case FieldDef.DataType of
         ftSmallint:
           begin
    -      VS := InternalStrToInt(Src);
    +      VS := InternalStrToInt(Source, Len);
           Move(VS, Dest^, SizeOf(Smallint));
           end;
         ftWord:
           begin
    -      VW := InternalStrToInt(Src);
    +      VW := InternalStrToInt(Source, Len);
           Move(VW, Dest^, SizeOf(Word));
           end;
         ftInteger, ftAutoInc:
           begin
    -      VI := InternalStrToInt(Src);
    +      VI := InternalStrToInt(Source, Len);
           Move(VI, Dest^, SizeOf(Integer));
           end;
         ftLargeInt:
    @@ -1086,55 +1144,42 @@
             end
           else
           {$ENDIF}
    -        if Src <> '' then
    -          VL := StrToInt64(Src)
    -        else
    -          VL := 0;
    +      VL := InternalStrToInt64(Source, Len);
           Move(VL, Dest^, SizeOf(LargeInt));
           end;
         ftFloat:
           begin
    -      if Src <> '' then
    -        VF := InternalStrToFloat(Src)
    -      else
    -        VF := 0;
    +      VF := InternalStrToFloat(Source, Len, FSQLFormatSettings);
           Move(VF, Dest^, SizeOf(Double));
           end;
         ftBCD:
           begin
    -      VC := InternalStrToCurrency(Src);
    +      VC := InternalStrToCurrency(Source, Len, FSQLFormatSettings);
           Move(VC, Dest^, SizeOf(Currency));
           end;
         ftFmtBCD:
           begin
    -      VB := StrToBCD(Src, FSQLFormatSettings);
    +      VB := InternalStrToBCD(Source, Len, FSQLFormatSettings);
           Move(VB, Dest^, SizeOf(TBCD));
           end;
         ftDate:
           begin
    -      if Src <> '' then
    -        VD := InternalStrToDate(Src)
    -      else
    -        VD := 0;
    +      VD := InternalStrToDate(Source, Len);
           Move(VD, Dest^, SizeOf(TDateTime));
           end;
         ftTime:
           begin
    -      if Src <> '' then
    -        VD := InternalStrToTime(Src)
    -      else
    -        VD := 0;
    +      VD := InternalStrToTime(Source, Len);
           Move(VD, Dest^, SizeOf(TDateTime));
           end;
         ftDateTime:
           begin
    -      if Src <> '' then
    -        if AField^.ftype = FIELD_TYPE_TIMESTAMP then
    -          VD := InternalStrToTimeStamp(Src)
    -        else
    -          VD := InternalStrToDateTime(Src)
    +      {$IFDEF mysql40}
    +      if AField^.ftype = FIELD_TYPE_TIMESTAMP then
    +        VD := InternalStrToTimeStamp(Source, Len)
           else
    -        VD := 0;
    +      {$ENDIF}
    +        VD := InternalStrToDateTime(Source, Len);
           Move(VD, Dest^, SizeOf(TDateTime));
           end;
         ftString, ftFixedChar:
    
  • mysqlconn-performance-03.patch (9,908 bytes)
    Index: packages/fcl-db/src/sqldb/mysql/mysqlconn.inc
    ===================================================================
    --- packages/fcl-db/src/sqldb/mysql/mysqlconn.inc	(revision 39317)
    +++ packages/fcl-db/src/sqldb/mysql/mysqlconn.inc	(working copy)
    @@ -894,59 +894,97 @@
       ABlobBuf^.BlobBuffer^.Size := len;
     end;
     
    -function InternalStrToInt(const S: string): integer;
    +function InternalStrToInt(C: pchar; Len: integer): integer;
    +  procedure r;
    +  begin
    +    raise EConvertError.CreateFmt('"%s" is not a valid digit', [C^]);
    +  end;
    +var
    +  I: Integer;
     begin
    -  if S = '' then
    -    Result := 0
    -  else
    -    Result := StrToInt(S);
    +  Result := 0;
    +  for I := 1 to Len do
    +  begin
    +    case C^ of
    +      '0'..'9': Result := Result*10 + Ord(C^)-Ord('0');
    +      #0: break;
    +    else
    +      r;
    +    end;
    +    Inc(C);
    +  end;
     end;
     
    -function InternalStrToFloat(const S: string): Extended;
    -
    +function InternalStrToInt64(C: pchar; Len: integer): LargeInt;
    +  procedure r;
    +  begin
    +    raise EConvertError.CreateFmt('"%s" is not a valid digit', [C^]);
    +  end;
     var
       I: Integer;
    -  Tmp: string;
    -
     begin
    -  Tmp := '';
    -  for I := 1 to Length(S) do
    -    begin
    -    if not (S[I] in ['0'..'9', '+', '-', 'E', 'e']) then
    -      Tmp := Tmp + FormatSettings.DecimalSeparator
    +  Result := 0;
    +  for I := 1 to Len do
    +  begin
    +    case C^ of
    +      '0'..'9': Result := Result*10 + Ord(C^)-Ord('0');
    +      #0: break;
         else
    -      Tmp := Tmp + S[I];
    +      r;
         end;
    -  Result := StrToFloat(Tmp);
    +    Inc(C);
    +  end;
     end;
     
    -function InternalStrToCurrency(const S: string): Currency;
    +function InternalStrToFloat(C: pchar; Len: integer; const Format: TFormatSettings): Extended;
     
     var
    -  I: Integer;
       Tmp: string;
     
     begin
    -  Tmp := '';
    -  for I := 1 to Length(S) do
    -    begin
    -    if not (S[I] in ['0'..'9', '+', '-', 'E', 'e']) then
    -      Tmp := Tmp + FormatSettings.DecimalSeparator
    -    else
    -      Tmp := Tmp + S[I];
    -    end;
    -  Result := StrToCurr(Tmp);
    +  SetString(Tmp, C, Len);
    +  if Tmp='' then
    +    Exit(0);
    +  Result := StrToFloat(Tmp, Format);
     end;
     
    -function InternalStrToDate(const S: string): TDateTime;
    +function InternalStrToCurrency(C: pchar; Len: integer; const Format: TFormatSettings): Currency;
     
     var
    +  Tmp: string;
    +
    +begin
    +  SetString(Tmp, C, Len);
    +  if Tmp='' then
    +    Exit(0);
    +  Result := StrToCurr(Tmp, Format);
    +end;
    +
    +function InternalStrToBCD(C: pchar; Len: integer; const Format: TFormatSettings): tBCD;
    +
    +var
    +  Tmp: string;
    +
    +begin
    +  SetString(Tmp, C, Len);
    +  if Tmp='' then
    +    Exit(0);
    +  Result := StrToBCD(Tmp, Format);
    +end;
    +
    +function InternalStrToDate(C: pchar; Len: integer): TDateTime;
    +
    +var
       EY, EM, ED: Word;
     
     begin
    -  EY := StrToInt(Copy(S,1,4));
    -  EM := StrToInt(Copy(S,6,2));
    -  ED := StrToInt(Copy(S,9,2));
    +  if Len=0 then
    +    Exit(0);
    +  if Len<10 then
    +    raise EConvertError.Create('Invalid date string');
    +  EY := InternalStrToInt(C,4);
    +  EM := InternalStrToInt(C+5,2);
    +  ED := InternalStrToInt(C+8,2);
       if (EY = 0) or (EM = 0) or (ED = 0) then
         Result:=0
       else
    @@ -953,18 +991,24 @@
         Result:=EncodeDate(EY, EM, ED);
     end;
     
    -function StrToMSecs(const S: string): Word;
    -var C: char;
    +function StrToMSecs(C: pchar; Len: integer): Word;
    +{$IFDEF MYSQL56_UP}
    +var I: Integer;
         d, MSecs: double;
    +{$ENDIF}
     begin
     {$IFDEF MYSQL56_UP}
       // datetime(n), where n is fractional seconds precision (between 0 and 6)
       MSecs := 0;
       d := 100;
    -  for C in S do
    +  for I := 1 to Len do
         begin
    -    MSecs := MSecs + (ord(C)-ord('0'))*d;
    +    case C^ of
    +      '0'..'9': MSecs := MSecs + (ord(C^)-ord('0'))*d;
    +      #0: break;
    +    end;
         d := d / 10;
    +    Inc(C);
         end;
       Result := Round(MSecs);
     {$ELSE}
    @@ -972,7 +1016,7 @@
     {$ENDIF}
     end;
     
    -function InternalStrToDateTime(const S: string): TDateTime;
    +function InternalStrToDateTime(C: pchar; Len: integer): TDateTime;
     
     var
       EY, EM, ED: Word;
    @@ -979,13 +1023,20 @@
       EH, EN, ES, EMS: Word;
     
     begin
    -  EY := StrToInt(Copy(S, 1, 4));
    -  EM := StrToInt(Copy(S, 6, 2));
    -  ED := StrToInt(Copy(S, 9, 2));
    -  EH := StrToInt(Copy(S, 12, 2));
    -  EN := StrToInt(Copy(S, 15, 2));
    -  ES := StrToInt(Copy(S, 18, 2));
    -  EMS:= StrToMSecs(Copy(S, 21, 6));
    +  if Len=0 then
    +    Exit(0);
    +  if Len<19 then
    +    raise EConvertError.Create('Invalid datetime string');
    +  EY := InternalStrToInt(C,4);
    +  EM := InternalStrToInt(C+5,2);
    +  ED := InternalStrToInt(C+8,2);
    +  EH := InternalStrToInt(C+11, 2);
    +  EN := InternalStrToInt(C+14, 2);
    +  ES := InternalStrToInt(C+17, 2);
    +  if Len>20 then
    +    EMS := StrToMSecs(C+20, Len-20)
    +  else
    +    EMS := 0;
       if (EY = 0) or (EM = 0) or (ED = 0) then
         Result := 0
       else
    @@ -993,51 +1044,64 @@
       Result := ComposeDateTime(Result, EncodeTime(EH, EN, ES, EMS));
     end;
     
    -function InternalStrToTime(const S: string): TDateTime;
    +function InternalStrToTime(C: pchar; Len: integer): TDateTime;
     
     var
       EH, EM, ES, EMS: Word;
    -  p: integer;
    +  M: PChar;
    +  I: Integer;
     
     begin
    -  p := 1;
    -  EH := StrToInt(ExtractSubstr(S, p, [':'])); //hours can be 2 or 3 digits
    -  EM := StrToInt(ExtractSubstr(S, p, [':']));
    -  ES := StrToInt(ExtractSubstr(S, p, ['.']));
    -  EMS:= StrToMSecs(Copy(S, p, 6));
    +  if Len=0 then
    +    Exit(0);
    +  if Len<8 then
    +    raise EConvertError.Create('Invalid time string');
    +  //hours can be 2 or 3 digits
    +  M:=C;
    +  for I := 1 to Len do
    +  begin
    +    if M^=':' then
    +      break;
    +    Inc(M);
    +  end;
    +  if M^<>':' then
    +    raise EConvertError.Create('Invalid time string');
    +
    +  EH := InternalStrToInt(C, NativeInt(M-C));
    +  EM := InternalStrToInt(M+1, 2);
    +  ES := InternalStrToInt(M+4, 2);
    +  if Len>NativeInt(M-C)+7 then
    +    EMS := StrToMSecs(M+7, Len-(NativeInt(M-C)+7))
    +  else
    +    EMS := 0;
       Result := EncodeTimeInterval(EH, EM, ES, EMS);
     end;
     
    -function InternalStrToTimeStamp(const S: string): TDateTime;
    +{$IFDEF mysql40}
    +function InternalStrToTimeStamp(C: pchar; Len: integer): TDateTime;
     
     var
       EY, EM, ED: Word;
    -  EH, EN, ES, EMS: Word;
    +  EH, EN, ES: Word;
     
     begin
    -{$IFNDEF mysql40}
    -  EY := StrToInt(Copy(S, 1, 4));
    -  EM := StrToInt(Copy(S, 6, 2));
    -  ED := StrToInt(Copy(S, 9, 2));
    -  EH := StrToInt(Copy(S, 12, 2));
    -  EN := StrToInt(Copy(S, 15, 2));
    -  ES := StrToInt(Copy(S, 18, 2));
    -  EMS:= StrToMSecs(Copy(S, 21, 6));
    -{$ELSE}
    -  EY := StrToInt(Copy(S, 1, 4));
    -  EM := StrToInt(Copy(S, 5, 2));
    -  ED := StrToInt(Copy(S, 7, 2));
    -  EH := StrToInt(Copy(S, 9, 2));
    -  EN := StrToInt(Copy(S, 11, 2));
    -  ES := StrToInt(Copy(S, 13, 2));
    -  EMS:= 0;
    -{$ENDIF}
    +  if Len=0 then
    +    Exit(0);
    +  if Len<14 then
    +    raise EConvertError.Create('Invalid timestamp string');
    +  EY := InternalStrToInt(C, 4);
    +  EM := InternalStrToInt(C+4, 2));
    +  ED := InternalStrToInt(C+6, 2));
    +  EH := InternalStrToInt(C+8, 2));
    +  EN := InternalStrToInt(C+10, 2));
    +  ES := InternalStrToInt(C+12, 2));
       if (EY = 0) or (EM = 0) or (ED = 0) then
         Result := 0
       else
         Result := EncodeDate(EY, EM, ED);
    -  Result := Result + EncodeTime(EH, EN, ES, EMS);
    +  Result := ComposeDateTime(Result, EncodeTime(EH, EN, ES, 0));
     end;
    +{$ENDIF}
     
     function TConnectionName.MySQLWriteData(AField: PMYSQL_FIELD; FieldDef: TFieldDef; Source, Dest: PChar; Len: integer; out CreateBlob : boolean): Boolean;
     
    @@ -1050,7 +1114,6 @@
       VC: Currency;
       VD: TDateTime;
       VB: TBCD;
    -  Src : String;
     
     begin
       Result := False;
    @@ -1057,22 +1120,21 @@
       CreateBlob := False;
       if Source = Nil then // If the pointer is NULL, the field is NULL
         exit;
    -  SetString(Src, Source, Len);
     
       case FieldDef.DataType of
         ftSmallint:
           begin
    -      VS := InternalStrToInt(Src);
    +      VS := InternalStrToInt(Source, Len);
           Move(VS, Dest^, SizeOf(Smallint));
           end;
         ftWord:
           begin
    -      VW := InternalStrToInt(Src);
    +      VW := InternalStrToInt(Source, Len);
           Move(VW, Dest^, SizeOf(Word));
           end;
         ftInteger, ftAutoInc:
           begin
    -      VI := InternalStrToInt(Src);
    +      VI := InternalStrToInt(Source, Len);
           Move(VI, Dest^, SizeOf(Integer));
           end;
         ftLargeInt:
    @@ -1086,55 +1148,42 @@
             end
           else
           {$ENDIF}
    -        if Src <> '' then
    -          VL := StrToInt64(Src)
    -        else
    -          VL := 0;
    +      VL := InternalStrToInt64(Source, Len);
           Move(VL, Dest^, SizeOf(LargeInt));
           end;
         ftFloat:
           begin
    -      if Src <> '' then
    -        VF := InternalStrToFloat(Src)
    -      else
    -        VF := 0;
    +      VF := InternalStrToFloat(Source, Len, FSQLFormatSettings);
           Move(VF, Dest^, SizeOf(Double));
           end;
         ftBCD:
           begin
    -      VC := InternalStrToCurrency(Src);
    +      VC := InternalStrToCurrency(Source, Len, FSQLFormatSettings);
           Move(VC, Dest^, SizeOf(Currency));
           end;
         ftFmtBCD:
           begin
    -      VB := StrToBCD(Src, FSQLFormatSettings);
    +      VB := InternalStrToBCD(Source, Len, FSQLFormatSettings);
           Move(VB, Dest^, SizeOf(TBCD));
           end;
         ftDate:
           begin
    -      if Src <> '' then
    -        VD := InternalStrToDate(Src)
    -      else
    -        VD := 0;
    +      VD := InternalStrToDate(Source, Len);
           Move(VD, Dest^, SizeOf(TDateTime));
           end;
         ftTime:
           begin
    -      if Src <> '' then
    -        VD := InternalStrToTime(Src)
    -      else
    -        VD := 0;
    +      VD := InternalStrToTime(Source, Len);
           Move(VD, Dest^, SizeOf(TDateTime));
           end;
         ftDateTime:
           begin
    -      if Src <> '' then
    -        if AField^.ftype = FIELD_TYPE_TIMESTAMP then
    -          VD := InternalStrToTimeStamp(Src)
    -        else
    -          VD := InternalStrToDateTime(Src)
    +      {$IFDEF mysql40}
    +      if AField^.ftype = FIELD_TYPE_TIMESTAMP then
    +        VD := InternalStrToTimeStamp(Source, Len)
           else
    -        VD := 0;
    +      {$ENDIF}
    +        VD := InternalStrToDateTime(Source, Len);
           Move(VD, Dest^, SizeOf(TDateTime));
           end;
         ftString, ftFixedChar:
    
  • mysql_negative_33919.patch (1,107 bytes)
    Index: src/sqldb/mysql/mysqlconn.inc
    ===================================================================
    --- src/sqldb/mysql/mysqlconn.inc	(revision 39336)
    +++ src/sqldb/mysql/mysqlconn.inc	(working copy)
    @@ -903,8 +903,14 @@
       end;
     var
       I: Integer;
    +  negative: boolean;
     begin
       Result := 0;
    +  negative := C^ = '-';
    +  if negative then begin
    +    Inc(C);
    +    Dec(Len);
    +  end;
       for I := 1 to Len do
       begin
         case C^ of
    @@ -915,6 +921,8 @@
         end;
         Inc(C);
       end;
    +  if negative then
    +    Result := -Result;
     end;
     
     function InternalStrToInt64(C: pchar; Len: integer): LargeInt;
    @@ -923,9 +931,16 @@
         raise EConvertError.CreateFmt('"%s" is not a valid digit', [C^]);
       end;
     var
    -  I: Integer;
    +  I, sign: Integer;
     begin
       Result := 0;
    +  if C^ = '-' then begin
    +    sign := -1;
    +    Inc(C);
    +    Dec(Len);
    +  end
    +  else
    +    sign := 1;
       for I := 1 to Len do
       begin
         case C^ of
    @@ -936,6 +951,7 @@
         end;
         Inc(C);
       end;
    +  Result := Result * sign;
     end;
     
     function InternalStrToFloat(C: pchar; Len: integer; const Format: TFormatSettings): Extended;
    

Activities

Ondrej Pokorny

2018-06-27 21:31

developer  

mysqlconn-performance-01.patch (6,828 bytes)
Index: packages/fcl-db/src/sqldb/mysql/mysqlconn.inc
===================================================================
--- packages/fcl-db/src/sqldb/mysql/mysqlconn.inc	(revision 39317)
+++ packages/fcl-db/src/sqldb/mysql/mysqlconn.inc	(working copy)
@@ -894,56 +894,96 @@
   ABlobBuf^.BlobBuffer^.Size := len;
 end;
 
-function InternalStrToInt(const S: string): integer;
+function InternalStrToInt(C: pchar; Len: integer): integer;
+  procedure r;
+  begin
+    raise EConvertError.CreateFmt('"%s" is not a valid digit', [C^]);
+  end;
+var
+  I: Integer;
 begin
-  if S = '' then
-    Result := 0
-  else
-    Result := StrToInt(S);
+  Result := 0;
+  for I := 0 to Len-1 do
+  begin
+    if C^=#0 then
+      break;
+    case C^ of
+      '0'..'9': Result := Result*10 + Ord(C^)-Ord('0');
+    else
+      r;
+    end;
+    Inc(C);
+  end;
 end;
 
-function InternalStrToFloat(const S: string): Extended;
-
+function InternalStrToInt64(C: pchar; Len: integer): LargeInt;
+  procedure r;
+  begin
+    raise EConvertError.CreateFmt('"%s" is not a valid digit', [C^]);
+  end;
 var
   I: Integer;
-  Tmp: string;
-
 begin
-  Tmp := '';
-  for I := 1 to Length(S) do
-    begin
-    if not (S[I] in ['0'..'9', '+', '-', 'E', 'e']) then
-      Tmp := Tmp + FormatSettings.DecimalSeparator
+  Result := 0;
+  for I := 0 to Len-1 do
+  begin
+    if C^=#0 then
+      break;
+    case C^ of
+      '0'..'9': Result := Result*10 + Ord(C^)-Ord('0');
     else
-      Tmp := Tmp + S[I];
+      r;
     end;
-  Result := StrToFloat(Tmp);
+    Inc(C);
+  end;
 end;
 
-function InternalStrToCurrency(const S: string): Currency;
+function InternalStrToFloat(C: pchar; Len: integer; const Format: TFormatSettings): Extended;
 
 var
-  I: Integer;
   Tmp: string;
 
 begin
-  Tmp := '';
-  for I := 1 to Length(S) do
-    begin
-    if not (S[I] in ['0'..'9', '+', '-', 'E', 'e']) then
-      Tmp := Tmp + FormatSettings.DecimalSeparator
-    else
-      Tmp := Tmp + S[I];
-    end;
-  Result := StrToCurr(Tmp);
+  SetString(Tmp, C, Len);
+  if Tmp='' then
+    Exit(0);
+  Result := StrToFloat(Tmp, Format);
 end;
 
-function InternalStrToDate(const S: string): TDateTime;
+function InternalStrToCurrency(C: pchar; Len: integer; const Format: TFormatSettings): Currency;
 
 var
+  Tmp: string;
+
+begin
+  SetString(Tmp, C, Len);
+  if Tmp='' then
+    Exit(0);
+  Result := StrToCurr(Tmp, Format);
+end;
+
+function InternalStrToBCD(C: pchar; Len: integer; const Format: TFormatSettings): tBCD;
+
+var
+  Tmp: string;
+
+begin
+  SetString(Tmp, C, Len);
+  if Tmp='' then
+    Exit(0);
+  Result := StrToBCD(Tmp, Format);
+end;
+
+function InternalStrToDate(C: pchar; Len: integer): TDateTime;
+
+var
   EY, EM, ED: Word;
+  S: string;
 
 begin
+  SetString(S, C, Len);
+  if S='' then
+    Exit(0);
   EY := StrToInt(Copy(S,1,4));
   EM := StrToInt(Copy(S,6,2));
   ED := StrToInt(Copy(S,9,2));
@@ -972,13 +1012,17 @@
 {$ENDIF}
 end;
 
-function InternalStrToDateTime(const S: string): TDateTime;
+function InternalStrToDateTime(C: pchar; Len: integer): TDateTime;
 
 var
   EY, EM, ED: Word;
   EH, EN, ES, EMS: Word;
+  S: string;
 
 begin
+  SetString(S, C, Len);
+  if S='' then
+    Exit(0);
   EY := StrToInt(Copy(S, 1, 4));
   EM := StrToInt(Copy(S, 6, 2));
   ED := StrToInt(Copy(S, 9, 2));
@@ -993,13 +1037,17 @@
   Result := ComposeDateTime(Result, EncodeTime(EH, EN, ES, EMS));
 end;
 
-function InternalStrToTime(const S: string): TDateTime;
+function InternalStrToTime(C: pchar; Len: integer): TDateTime;
 
 var
   EH, EM, ES, EMS: Word;
   p: integer;
+  S: string;
 
 begin
+  SetString(S, C, Len);
+  if S='' then
+    Exit(0);
   p := 1;
   EH := StrToInt(ExtractSubstr(S, p, [':'])); //hours can be 2 or 3 digits
   EM := StrToInt(ExtractSubstr(S, p, [':']));
@@ -1008,13 +1056,17 @@
   Result := EncodeTimeInterval(EH, EM, ES, EMS);
 end;
 
-function InternalStrToTimeStamp(const S: string): TDateTime;
+function InternalStrToTimeStamp(C: pchar; Len: integer): TDateTime;
 
 var
   EY, EM, ED: Word;
   EH, EN, ES, EMS: Word;
+  S: string;
 
 begin
+  SetString(S, C, Len);
+  if S='' then
+    Exit(0);
 {$IFNDEF mysql40}
   EY := StrToInt(Copy(S, 1, 4));
   EM := StrToInt(Copy(S, 6, 2));
@@ -1050,7 +1102,6 @@
   VC: Currency;
   VD: TDateTime;
   VB: TBCD;
-  Src : String;
 
 begin
   Result := False;
@@ -1057,22 +1108,21 @@
   CreateBlob := False;
   if Source = Nil then // If the pointer is NULL, the field is NULL
     exit;
-  SetString(Src, Source, Len);
 
   case FieldDef.DataType of
     ftSmallint:
       begin
-      VS := InternalStrToInt(Src);
+      VS := InternalStrToInt(Source, Len);
       Move(VS, Dest^, SizeOf(Smallint));
       end;
     ftWord:
       begin
-      VW := InternalStrToInt(Src);
+      VW := InternalStrToInt(Source, Len);
       Move(VW, Dest^, SizeOf(Word));
       end;
     ftInteger, ftAutoInc:
       begin
-      VI := InternalStrToInt(Src);
+      VI := InternalStrToInt(Source, Len);
       Move(VI, Dest^, SizeOf(Integer));
       end;
     ftLargeInt:
@@ -1086,55 +1136,40 @@
         end
       else
       {$ENDIF}
-        if Src <> '' then
-          VL := StrToInt64(Src)
-        else
-          VL := 0;
+      VL := InternalStrToInt64(Source, Len);
       Move(VL, Dest^, SizeOf(LargeInt));
       end;
     ftFloat:
       begin
-      if Src <> '' then
-        VF := InternalStrToFloat(Src)
-      else
-        VF := 0;
+      VF := InternalStrToFloat(Source, Len, FSQLFormatSettings);
       Move(VF, Dest^, SizeOf(Double));
       end;
     ftBCD:
       begin
-      VC := InternalStrToCurrency(Src);
+      VC := InternalStrToCurrency(Source, Len, FSQLFormatSettings);
       Move(VC, Dest^, SizeOf(Currency));
       end;
     ftFmtBCD:
       begin
-      VB := StrToBCD(Src, FSQLFormatSettings);
+      VB := InternalStrToBCD(Source, Len, FSQLFormatSettings);
       Move(VB, Dest^, SizeOf(TBCD));
       end;
     ftDate:
       begin
-      if Src <> '' then
-        VD := InternalStrToDate(Src)
-      else
-        VD := 0;
+      VD := InternalStrToDate(Source, Len);
       Move(VD, Dest^, SizeOf(TDateTime));
       end;
     ftTime:
       begin
-      if Src <> '' then
-        VD := InternalStrToTime(Src)
-      else
-        VD := 0;
+      VD := InternalStrToTime(Source, Len);
       Move(VD, Dest^, SizeOf(TDateTime));
       end;
     ftDateTime:
       begin
-      if Src <> '' then
-        if AField^.ftype = FIELD_TYPE_TIMESTAMP then
-          VD := InternalStrToTimeStamp(Src)
-        else
-          VD := InternalStrToDateTime(Src)
+      if AField^.ftype = FIELD_TYPE_TIMESTAMP then
+        VD := InternalStrToTimeStamp(Source, Len)
       else
-        VD := 0;
+        VD := InternalStrToDateTime(Source, Len);
       Move(VD, Dest^, SizeOf(TDateTime));
       end;
     ftString, ftFixedChar:

Ondrej Pokorny

2018-06-28 07:30

developer   ~0109088

I'll send a new patch with more optimizations soon.

Ondrej Pokorny

2018-06-28 08:40

developer  

mysqlconn-performance-02.patch (9,590 bytes)
Index: packages/fcl-db/src/sqldb/mysql/mysqlconn.inc
===================================================================
--- packages/fcl-db/src/sqldb/mysql/mysqlconn.inc	(revision 39317)
+++ packages/fcl-db/src/sqldb/mysql/mysqlconn.inc	(working copy)
@@ -894,59 +894,97 @@
   ABlobBuf^.BlobBuffer^.Size := len;
 end;
 
-function InternalStrToInt(const S: string): integer;
+function InternalStrToInt(C: pchar; Len: integer): integer;
+  procedure r;
+  begin
+    raise EConvertError.CreateFmt('"%s" is not a valid digit', [C^]);
+  end;
+var
+  I: Integer;
 begin
-  if S = '' then
-    Result := 0
-  else
-    Result := StrToInt(S);
+  Result := 0;
+  for I := 1 to Len do
+  begin
+    case C^ of
+      '0'..'9': Result := Result*10 + Ord(C^)-Ord('0');
+      #0: break;
+    else
+      r;
+    end;
+    Inc(C);
+  end;
 end;
 
-function InternalStrToFloat(const S: string): Extended;
-
+function InternalStrToInt64(C: pchar; Len: integer): LargeInt;
+  procedure r;
+  begin
+    raise EConvertError.CreateFmt('"%s" is not a valid digit', [C^]);
+  end;
 var
   I: Integer;
-  Tmp: string;
-
 begin
-  Tmp := '';
-  for I := 1 to Length(S) do
-    begin
-    if not (S[I] in ['0'..'9', '+', '-', 'E', 'e']) then
-      Tmp := Tmp + FormatSettings.DecimalSeparator
+  Result := 0;
+  for I := 1 to Len do
+  begin
+    case C^ of
+      '0'..'9': Result := Result*10 + Ord(C^)-Ord('0');
+      #0: break;
     else
-      Tmp := Tmp + S[I];
+      r;
     end;
-  Result := StrToFloat(Tmp);
+    Inc(C);
+  end;
 end;
 
-function InternalStrToCurrency(const S: string): Currency;
+function InternalStrToFloat(C: pchar; Len: integer; const Format: TFormatSettings): Extended;
 
 var
-  I: Integer;
   Tmp: string;
 
 begin
-  Tmp := '';
-  for I := 1 to Length(S) do
-    begin
-    if not (S[I] in ['0'..'9', '+', '-', 'E', 'e']) then
-      Tmp := Tmp + FormatSettings.DecimalSeparator
-    else
-      Tmp := Tmp + S[I];
-    end;
-  Result := StrToCurr(Tmp);
+  SetString(Tmp, C, Len);
+  if Tmp='' then
+    Exit(0);
+  Result := StrToFloat(Tmp, Format);
 end;
 
-function InternalStrToDate(const S: string): TDateTime;
+function InternalStrToCurrency(C: pchar; Len: integer; const Format: TFormatSettings): Currency;
 
 var
+  Tmp: string;
+
+begin
+  SetString(Tmp, C, Len);
+  if Tmp='' then
+    Exit(0);
+  Result := StrToCurr(Tmp, Format);
+end;
+
+function InternalStrToBCD(C: pchar; Len: integer; const Format: TFormatSettings): tBCD;
+
+var
+  Tmp: string;
+
+begin
+  SetString(Tmp, C, Len);
+  if Tmp='' then
+    Exit(0);
+  Result := StrToBCD(Tmp, Format);
+end;
+
+function InternalStrToDate(C: pchar; Len: integer): TDateTime;
+
+var
   EY, EM, ED: Word;
 
 begin
-  EY := StrToInt(Copy(S,1,4));
-  EM := StrToInt(Copy(S,6,2));
-  ED := StrToInt(Copy(S,9,2));
+  if Len=0 then
+    Exit(0);
+  if Len<10 then
+    raise EConvertError.Create('Invalid date string');
+  EY := InternalStrToInt(C,4);
+  EM := InternalStrToInt(C+5,2);
+  ED := InternalStrToInt(C+8,2);
   if (EY = 0) or (EM = 0) or (ED = 0) then
     Result:=0
   else
@@ -953,9 +991,11 @@
     Result:=EncodeDate(EY, EM, ED);
 end;
 
-function StrToMSecs(const S: string): Word;
+function StrToMSecs(C: pchar; Len: integer): Word;
+{$IFDEF MYSQL56_UP}
 var C: char;
     d, MSecs: double;
+{$ENDIF}
 begin
 {$IFDEF MYSQL56_UP}
   // datetime(n), where n is fractional seconds precision (between 0 and 6)
@@ -972,7 +1012,7 @@
 {$ENDIF}
 end;
 
-function InternalStrToDateTime(const S: string): TDateTime;
+function InternalStrToDateTime(C: pchar; Len: integer): TDateTime;
 
 var
   EY, EM, ED: Word;
@@ -979,13 +1019,20 @@
   EH, EN, ES, EMS: Word;
 
 begin
-  EY := StrToInt(Copy(S, 1, 4));
-  EM := StrToInt(Copy(S, 6, 2));
-  ED := StrToInt(Copy(S, 9, 2));
-  EH := StrToInt(Copy(S, 12, 2));
-  EN := StrToInt(Copy(S, 15, 2));
-  ES := StrToInt(Copy(S, 18, 2));
-  EMS:= StrToMSecs(Copy(S, 21, 6));
+  if Len=0 then
+    Exit(0);
+  if Len<19 then
+    raise EConvertError.Create('Invalid datetime string');
+  EY := InternalStrToInt(C,4);
+  EM := InternalStrToInt(C+5,2);
+  ED := InternalStrToInt(C+8,2);
+  EH := InternalStrToInt(C+11, 2);
+  EN := InternalStrToInt(C+14, 2);
+  ES := InternalStrToInt(C+17, 2);
+  if Len>20 then
+    EMS := StrToMSecs(C+20, Len-20)
+  else
+    EMS := 0;
   if (EY = 0) or (EM = 0) or (ED = 0) then
     Result := 0
   else
@@ -993,51 +1040,64 @@
   Result := ComposeDateTime(Result, EncodeTime(EH, EN, ES, EMS));
 end;
 
-function InternalStrToTime(const S: string): TDateTime;
+function InternalStrToTime(C: pchar; Len: integer): TDateTime;
 
 var
   EH, EM, ES, EMS: Word;
-  p: integer;
+  M: PChar;
+  I: Integer;
 
 begin
-  p := 1;
-  EH := StrToInt(ExtractSubstr(S, p, [':'])); //hours can be 2 or 3 digits
-  EM := StrToInt(ExtractSubstr(S, p, [':']));
-  ES := StrToInt(ExtractSubstr(S, p, ['.']));
-  EMS:= StrToMSecs(Copy(S, p, 6));
+  if Len=0 then
+    Exit(0);
+  if Len<8 then
+    raise EConvertError.Create('Invalid time string');
+  //hours can be 2 or 3 digits
+  M:=C;
+  for I := 1 to Len do
+  begin
+    if M^=':' then
+      break;
+    Inc(M);
+  end;
+  if M^<>':' then
+    raise EConvertError.Create('Invalid time string');
+
+  EH := InternalStrToInt(C, NativeInt(M-C));
+  EM := InternalStrToInt(M+1, 2);
+  ES := InternalStrToInt(M+4, 2);
+  if Len>NativeInt(M-C)+7 then
+    EMS := StrToMSecs(M+7, Len-(NativeInt(M-C)+7))
+  else
+    EMS := 0;
   Result := EncodeTimeInterval(EH, EM, ES, EMS);
 end;
 
-function InternalStrToTimeStamp(const S: string): TDateTime;
+{$IFDEF mysql40}
+function InternalStrToTimeStamp(C: pchar; Len: integer): TDateTime;
 
 var
   EY, EM, ED: Word;
-  EH, EN, ES, EMS: Word;
+  EH, EN, ES: Word;
 
 begin
-{$IFNDEF mysql40}
-  EY := StrToInt(Copy(S, 1, 4));
-  EM := StrToInt(Copy(S, 6, 2));
-  ED := StrToInt(Copy(S, 9, 2));
-  EH := StrToInt(Copy(S, 12, 2));
-  EN := StrToInt(Copy(S, 15, 2));
-  ES := StrToInt(Copy(S, 18, 2));
-  EMS:= StrToMSecs(Copy(S, 21, 6));
-{$ELSE}
-  EY := StrToInt(Copy(S, 1, 4));
-  EM := StrToInt(Copy(S, 5, 2));
-  ED := StrToInt(Copy(S, 7, 2));
-  EH := StrToInt(Copy(S, 9, 2));
-  EN := StrToInt(Copy(S, 11, 2));
-  ES := StrToInt(Copy(S, 13, 2));
-  EMS:= 0;
-{$ENDIF}
+  if Len=0 then
+    Exit(0);
+  if Len<14 then
+    raise EConvertError.Create('Invalid timestamp string');
+  EY := InternalStrToInt(C, 4);
+  EM := InternalStrToInt(C+4, 2));
+  ED := InternalStrToInt(C+6, 2));
+  EH := InternalStrToInt(C+8, 2));
+  EN := InternalStrToInt(C+10, 2));
+  ES := InternalStrToInt(C+12, 2));
   if (EY = 0) or (EM = 0) or (ED = 0) then
     Result := 0
   else
     Result := EncodeDate(EY, EM, ED);
-  Result := Result + EncodeTime(EH, EN, ES, EMS);
+  Result := ComposeDateTime(Result, EncodeTime(EH, EN, ES, 0));
 end;
+{$ENDIF}
 
 function TConnectionName.MySQLWriteData(AField: PMYSQL_FIELD; FieldDef: TFieldDef; Source, Dest: PChar; Len: integer; out CreateBlob : boolean): Boolean;
 
@@ -1050,7 +1110,6 @@
   VC: Currency;
   VD: TDateTime;
   VB: TBCD;
-  Src : String;
 
 begin
   Result := False;
@@ -1057,22 +1116,21 @@
   CreateBlob := False;
   if Source = Nil then // If the pointer is NULL, the field is NULL
     exit;
-  SetString(Src, Source, Len);
 
   case FieldDef.DataType of
     ftSmallint:
       begin
-      VS := InternalStrToInt(Src);
+      VS := InternalStrToInt(Source, Len);
       Move(VS, Dest^, SizeOf(Smallint));
       end;
     ftWord:
       begin
-      VW := InternalStrToInt(Src);
+      VW := InternalStrToInt(Source, Len);
       Move(VW, Dest^, SizeOf(Word));
       end;
     ftInteger, ftAutoInc:
       begin
-      VI := InternalStrToInt(Src);
+      VI := InternalStrToInt(Source, Len);
       Move(VI, Dest^, SizeOf(Integer));
       end;
     ftLargeInt:
@@ -1086,55 +1144,42 @@
         end
       else
       {$ENDIF}
-        if Src <> '' then
-          VL := StrToInt64(Src)
-        else
-          VL := 0;
+      VL := InternalStrToInt64(Source, Len);
       Move(VL, Dest^, SizeOf(LargeInt));
       end;
     ftFloat:
       begin
-      if Src <> '' then
-        VF := InternalStrToFloat(Src)
-      else
-        VF := 0;
+      VF := InternalStrToFloat(Source, Len, FSQLFormatSettings);
       Move(VF, Dest^, SizeOf(Double));
       end;
     ftBCD:
       begin
-      VC := InternalStrToCurrency(Src);
+      VC := InternalStrToCurrency(Source, Len, FSQLFormatSettings);
       Move(VC, Dest^, SizeOf(Currency));
       end;
     ftFmtBCD:
       begin
-      VB := StrToBCD(Src, FSQLFormatSettings);
+      VB := InternalStrToBCD(Source, Len, FSQLFormatSettings);
       Move(VB, Dest^, SizeOf(TBCD));
       end;
     ftDate:
       begin
-      if Src <> '' then
-        VD := InternalStrToDate(Src)
-      else
-        VD := 0;
+      VD := InternalStrToDate(Source, Len);
       Move(VD, Dest^, SizeOf(TDateTime));
       end;
     ftTime:
       begin
-      if Src <> '' then
-        VD := InternalStrToTime(Src)
-      else
-        VD := 0;
+      VD := InternalStrToTime(Source, Len);
       Move(VD, Dest^, SizeOf(TDateTime));
       end;
     ftDateTime:
       begin
-      if Src <> '' then
-        if AField^.ftype = FIELD_TYPE_TIMESTAMP then
-          VD := InternalStrToTimeStamp(Src)
-        else
-          VD := InternalStrToDateTime(Src)
+      {$IFDEF mysql40}
+      if AField^.ftype = FIELD_TYPE_TIMESTAMP then
+        VD := InternalStrToTimeStamp(Source, Len)
       else
-        VD := 0;
+      {$ENDIF}
+        VD := InternalStrToDateTime(Source, Len);
       Move(VD, Dest^, SizeOf(TDateTime));
       end;
     ftString, ftFixedChar:

Ondrej Pokorny

2018-06-28 08:58

developer  

mysqlconn-performance-03.patch (9,908 bytes)
Index: packages/fcl-db/src/sqldb/mysql/mysqlconn.inc
===================================================================
--- packages/fcl-db/src/sqldb/mysql/mysqlconn.inc	(revision 39317)
+++ packages/fcl-db/src/sqldb/mysql/mysqlconn.inc	(working copy)
@@ -894,59 +894,97 @@
   ABlobBuf^.BlobBuffer^.Size := len;
 end;
 
-function InternalStrToInt(const S: string): integer;
+function InternalStrToInt(C: pchar; Len: integer): integer;
+  procedure r;
+  begin
+    raise EConvertError.CreateFmt('"%s" is not a valid digit', [C^]);
+  end;
+var
+  I: Integer;
 begin
-  if S = '' then
-    Result := 0
-  else
-    Result := StrToInt(S);
+  Result := 0;
+  for I := 1 to Len do
+  begin
+    case C^ of
+      '0'..'9': Result := Result*10 + Ord(C^)-Ord('0');
+      #0: break;
+    else
+      r;
+    end;
+    Inc(C);
+  end;
 end;
 
-function InternalStrToFloat(const S: string): Extended;
-
+function InternalStrToInt64(C: pchar; Len: integer): LargeInt;
+  procedure r;
+  begin
+    raise EConvertError.CreateFmt('"%s" is not a valid digit', [C^]);
+  end;
 var
   I: Integer;
-  Tmp: string;
-
 begin
-  Tmp := '';
-  for I := 1 to Length(S) do
-    begin
-    if not (S[I] in ['0'..'9', '+', '-', 'E', 'e']) then
-      Tmp := Tmp + FormatSettings.DecimalSeparator
+  Result := 0;
+  for I := 1 to Len do
+  begin
+    case C^ of
+      '0'..'9': Result := Result*10 + Ord(C^)-Ord('0');
+      #0: break;
     else
-      Tmp := Tmp + S[I];
+      r;
     end;
-  Result := StrToFloat(Tmp);
+    Inc(C);
+  end;
 end;
 
-function InternalStrToCurrency(const S: string): Currency;
+function InternalStrToFloat(C: pchar; Len: integer; const Format: TFormatSettings): Extended;
 
 var
-  I: Integer;
   Tmp: string;
 
 begin
-  Tmp := '';
-  for I := 1 to Length(S) do
-    begin
-    if not (S[I] in ['0'..'9', '+', '-', 'E', 'e']) then
-      Tmp := Tmp + FormatSettings.DecimalSeparator
-    else
-      Tmp := Tmp + S[I];
-    end;
-  Result := StrToCurr(Tmp);
+  SetString(Tmp, C, Len);
+  if Tmp='' then
+    Exit(0);
+  Result := StrToFloat(Tmp, Format);
 end;
 
-function InternalStrToDate(const S: string): TDateTime;
+function InternalStrToCurrency(C: pchar; Len: integer; const Format: TFormatSettings): Currency;
 
 var
+  Tmp: string;
+
+begin
+  SetString(Tmp, C, Len);
+  if Tmp='' then
+    Exit(0);
+  Result := StrToCurr(Tmp, Format);
+end;
+
+function InternalStrToBCD(C: pchar; Len: integer; const Format: TFormatSettings): tBCD;
+
+var
+  Tmp: string;
+
+begin
+  SetString(Tmp, C, Len);
+  if Tmp='' then
+    Exit(0);
+  Result := StrToBCD(Tmp, Format);
+end;
+
+function InternalStrToDate(C: pchar; Len: integer): TDateTime;
+
+var
   EY, EM, ED: Word;
 
 begin
-  EY := StrToInt(Copy(S,1,4));
-  EM := StrToInt(Copy(S,6,2));
-  ED := StrToInt(Copy(S,9,2));
+  if Len=0 then
+    Exit(0);
+  if Len<10 then
+    raise EConvertError.Create('Invalid date string');
+  EY := InternalStrToInt(C,4);
+  EM := InternalStrToInt(C+5,2);
+  ED := InternalStrToInt(C+8,2);
   if (EY = 0) or (EM = 0) or (ED = 0) then
     Result:=0
   else
@@ -953,18 +991,24 @@
     Result:=EncodeDate(EY, EM, ED);
 end;
 
-function StrToMSecs(const S: string): Word;
-var C: char;
+function StrToMSecs(C: pchar; Len: integer): Word;
+{$IFDEF MYSQL56_UP}
+var I: Integer;
     d, MSecs: double;
+{$ENDIF}
 begin
 {$IFDEF MYSQL56_UP}
   // datetime(n), where n is fractional seconds precision (between 0 and 6)
   MSecs := 0;
   d := 100;
-  for C in S do
+  for I := 1 to Len do
     begin
-    MSecs := MSecs + (ord(C)-ord('0'))*d;
+    case C^ of
+      '0'..'9': MSecs := MSecs + (ord(C^)-ord('0'))*d;
+      #0: break;
+    end;
     d := d / 10;
+    Inc(C);
     end;
   Result := Round(MSecs);
 {$ELSE}
@@ -972,7 +1016,7 @@
 {$ENDIF}
 end;
 
-function InternalStrToDateTime(const S: string): TDateTime;
+function InternalStrToDateTime(C: pchar; Len: integer): TDateTime;
 
 var
   EY, EM, ED: Word;
@@ -979,13 +1023,20 @@
   EH, EN, ES, EMS: Word;
 
 begin
-  EY := StrToInt(Copy(S, 1, 4));
-  EM := StrToInt(Copy(S, 6, 2));
-  ED := StrToInt(Copy(S, 9, 2));
-  EH := StrToInt(Copy(S, 12, 2));
-  EN := StrToInt(Copy(S, 15, 2));
-  ES := StrToInt(Copy(S, 18, 2));
-  EMS:= StrToMSecs(Copy(S, 21, 6));
+  if Len=0 then
+    Exit(0);
+  if Len<19 then
+    raise EConvertError.Create('Invalid datetime string');
+  EY := InternalStrToInt(C,4);
+  EM := InternalStrToInt(C+5,2);
+  ED := InternalStrToInt(C+8,2);
+  EH := InternalStrToInt(C+11, 2);
+  EN := InternalStrToInt(C+14, 2);
+  ES := InternalStrToInt(C+17, 2);
+  if Len>20 then
+    EMS := StrToMSecs(C+20, Len-20)
+  else
+    EMS := 0;
   if (EY = 0) or (EM = 0) or (ED = 0) then
     Result := 0
   else
@@ -993,51 +1044,64 @@
   Result := ComposeDateTime(Result, EncodeTime(EH, EN, ES, EMS));
 end;
 
-function InternalStrToTime(const S: string): TDateTime;
+function InternalStrToTime(C: pchar; Len: integer): TDateTime;
 
 var
   EH, EM, ES, EMS: Word;
-  p: integer;
+  M: PChar;
+  I: Integer;
 
 begin
-  p := 1;
-  EH := StrToInt(ExtractSubstr(S, p, [':'])); //hours can be 2 or 3 digits
-  EM := StrToInt(ExtractSubstr(S, p, [':']));
-  ES := StrToInt(ExtractSubstr(S, p, ['.']));
-  EMS:= StrToMSecs(Copy(S, p, 6));
+  if Len=0 then
+    Exit(0);
+  if Len<8 then
+    raise EConvertError.Create('Invalid time string');
+  //hours can be 2 or 3 digits
+  M:=C;
+  for I := 1 to Len do
+  begin
+    if M^=':' then
+      break;
+    Inc(M);
+  end;
+  if M^<>':' then
+    raise EConvertError.Create('Invalid time string');
+
+  EH := InternalStrToInt(C, NativeInt(M-C));
+  EM := InternalStrToInt(M+1, 2);
+  ES := InternalStrToInt(M+4, 2);
+  if Len>NativeInt(M-C)+7 then
+    EMS := StrToMSecs(M+7, Len-(NativeInt(M-C)+7))
+  else
+    EMS := 0;
   Result := EncodeTimeInterval(EH, EM, ES, EMS);
 end;
 
-function InternalStrToTimeStamp(const S: string): TDateTime;
+{$IFDEF mysql40}
+function InternalStrToTimeStamp(C: pchar; Len: integer): TDateTime;
 
 var
   EY, EM, ED: Word;
-  EH, EN, ES, EMS: Word;
+  EH, EN, ES: Word;
 
 begin
-{$IFNDEF mysql40}
-  EY := StrToInt(Copy(S, 1, 4));
-  EM := StrToInt(Copy(S, 6, 2));
-  ED := StrToInt(Copy(S, 9, 2));
-  EH := StrToInt(Copy(S, 12, 2));
-  EN := StrToInt(Copy(S, 15, 2));
-  ES := StrToInt(Copy(S, 18, 2));
-  EMS:= StrToMSecs(Copy(S, 21, 6));
-{$ELSE}
-  EY := StrToInt(Copy(S, 1, 4));
-  EM := StrToInt(Copy(S, 5, 2));
-  ED := StrToInt(Copy(S, 7, 2));
-  EH := StrToInt(Copy(S, 9, 2));
-  EN := StrToInt(Copy(S, 11, 2));
-  ES := StrToInt(Copy(S, 13, 2));
-  EMS:= 0;
-{$ENDIF}
+  if Len=0 then
+    Exit(0);
+  if Len<14 then
+    raise EConvertError.Create('Invalid timestamp string');
+  EY := InternalStrToInt(C, 4);
+  EM := InternalStrToInt(C+4, 2));
+  ED := InternalStrToInt(C+6, 2));
+  EH := InternalStrToInt(C+8, 2));
+  EN := InternalStrToInt(C+10, 2));
+  ES := InternalStrToInt(C+12, 2));
   if (EY = 0) or (EM = 0) or (ED = 0) then
     Result := 0
   else
     Result := EncodeDate(EY, EM, ED);
-  Result := Result + EncodeTime(EH, EN, ES, EMS);
+  Result := ComposeDateTime(Result, EncodeTime(EH, EN, ES, 0));
 end;
+{$ENDIF}
 
 function TConnectionName.MySQLWriteData(AField: PMYSQL_FIELD; FieldDef: TFieldDef; Source, Dest: PChar; Len: integer; out CreateBlob : boolean): Boolean;
 
@@ -1050,7 +1114,6 @@
   VC: Currency;
   VD: TDateTime;
   VB: TBCD;
-  Src : String;
 
 begin
   Result := False;
@@ -1057,22 +1120,21 @@
   CreateBlob := False;
   if Source = Nil then // If the pointer is NULL, the field is NULL
     exit;
-  SetString(Src, Source, Len);
 
   case FieldDef.DataType of
     ftSmallint:
       begin
-      VS := InternalStrToInt(Src);
+      VS := InternalStrToInt(Source, Len);
       Move(VS, Dest^, SizeOf(Smallint));
       end;
     ftWord:
       begin
-      VW := InternalStrToInt(Src);
+      VW := InternalStrToInt(Source, Len);
       Move(VW, Dest^, SizeOf(Word));
       end;
     ftInteger, ftAutoInc:
       begin
-      VI := InternalStrToInt(Src);
+      VI := InternalStrToInt(Source, Len);
       Move(VI, Dest^, SizeOf(Integer));
       end;
     ftLargeInt:
@@ -1086,55 +1148,42 @@
         end
       else
       {$ENDIF}
-        if Src <> '' then
-          VL := StrToInt64(Src)
-        else
-          VL := 0;
+      VL := InternalStrToInt64(Source, Len);
       Move(VL, Dest^, SizeOf(LargeInt));
       end;
     ftFloat:
       begin
-      if Src <> '' then
-        VF := InternalStrToFloat(Src)
-      else
-        VF := 0;
+      VF := InternalStrToFloat(Source, Len, FSQLFormatSettings);
       Move(VF, Dest^, SizeOf(Double));
       end;
     ftBCD:
       begin
-      VC := InternalStrToCurrency(Src);
+      VC := InternalStrToCurrency(Source, Len, FSQLFormatSettings);
       Move(VC, Dest^, SizeOf(Currency));
       end;
     ftFmtBCD:
       begin
-      VB := StrToBCD(Src, FSQLFormatSettings);
+      VB := InternalStrToBCD(Source, Len, FSQLFormatSettings);
       Move(VB, Dest^, SizeOf(TBCD));
       end;
     ftDate:
       begin
-      if Src <> '' then
-        VD := InternalStrToDate(Src)
-      else
-        VD := 0;
+      VD := InternalStrToDate(Source, Len);
       Move(VD, Dest^, SizeOf(TDateTime));
       end;
     ftTime:
       begin
-      if Src <> '' then
-        VD := InternalStrToTime(Src)
-      else
-        VD := 0;
+      VD := InternalStrToTime(Source, Len);
       Move(VD, Dest^, SizeOf(TDateTime));
       end;
     ftDateTime:
       begin
-      if Src <> '' then
-        if AField^.ftype = FIELD_TYPE_TIMESTAMP then
-          VD := InternalStrToTimeStamp(Src)
-        else
-          VD := InternalStrToDateTime(Src)
+      {$IFDEF mysql40}
+      if AField^.ftype = FIELD_TYPE_TIMESTAMP then
+        VD := InternalStrToTimeStamp(Source, Len)
       else
-        VD := 0;
+      {$ENDIF}
+        VD := InternalStrToDateTime(Source, Len);
       Move(VD, Dest^, SizeOf(TDateTime));
       end;
     ftString, ftFixedChar:

Ondrej Pokorny

2018-06-28 08:58

developer   ~0109090

I uploaded a new patch (03):

it improves performance of InternalStrTo Date/Time/DateTime/Timestamp - it uses the PChar directly without converting it to string.

Michael Van Canneyt

2018-06-28 10:08

administrator   ~0109093

I'm not 100% convinced that the expected speed gain is justified by the use of custom code as opposed to using well-tested generic routines, but I decided to apply the full patch anyway.

Thanks for your efforts!

Ondrej Pokorny

2018-06-28 10:20

developer   ~0109097

> I'm not 100% convinced that the expected speed gain is justified by the use of custom code as opposed to using well-tested generic routines

Actually this applies only to one routine: InternalStrToInt<>IntToStr. Other routines I improved (like e.g. InternalStrToDate) were not generic anyway.

(Btw. I'd love to have overloads to StrToInt, StrToFloat and other StrTo* functions that accept PChar in FPC as well :) )

Thank you for applying!

LacaK

2018-06-29 09:55

developer   ~0109124

This patch breaks tests. Functions InternalStrToInt and InternalStrToInt64 does not handle negative values!
I have attached patch. There are two approaches I do not know what is best.

Another questions:
- why is there nested function "r", which raises exception. Why is not simly raise statement in place where r is called ?
- why are FSQLFormatSettings passed as extra parameter to functions, when nothing else as this one value is used ?

LacaK

2018-06-29 09:55

developer  

mysql_negative_33919.patch (1,107 bytes)
Index: src/sqldb/mysql/mysqlconn.inc
===================================================================
--- src/sqldb/mysql/mysqlconn.inc	(revision 39336)
+++ src/sqldb/mysql/mysqlconn.inc	(working copy)
@@ -903,8 +903,14 @@
   end;
 var
   I: Integer;
+  negative: boolean;
 begin
   Result := 0;
+  negative := C^ = '-';
+  if negative then begin
+    Inc(C);
+    Dec(Len);
+  end;
   for I := 1 to Len do
   begin
     case C^ of
@@ -915,6 +921,8 @@
     end;
     Inc(C);
   end;
+  if negative then
+    Result := -Result;
 end;
 
 function InternalStrToInt64(C: pchar; Len: integer): LargeInt;
@@ -923,9 +931,16 @@
     raise EConvertError.CreateFmt('"%s" is not a valid digit', [C^]);
   end;
 var
-  I: Integer;
+  I, sign: Integer;
 begin
   Result := 0;
+  if C^ = '-' then begin
+    sign := -1;
+    Inc(C);
+    Dec(Len);
+  end
+  else
+    sign := 1;
   for I := 1 to Len do
   begin
     case C^ of
@@ -936,6 +951,7 @@
     end;
     Inc(C);
   end;
+  Result := Result * sign;
 end;
 
 function InternalStrToFloat(C: pchar; Len: integer; const Format: TFormatSettings): Extended;

Michael Van Canneyt

2018-06-29 11:26

administrator   ~0109125

This kind of thing is why I don't want such custom solutions.
So I reverted to using strtoint(64)() from sysutils.

Ondrej Pokorny

2018-06-29 11:40

developer   ~0109126

> Functions InternalStrToInt and InternalStrToInt64 does not handle negative values!

Ouch, sorry, my bad.

Issue History

Date Modified Username Field Change
2018-06-27 21:31 Ondrej Pokorny New Issue
2018-06-27 21:31 Ondrej Pokorny File Added: mysqlconn-performance-01.patch
2018-06-27 21:38 Michael Van Canneyt Assigned To => Michael Van Canneyt
2018-06-27 21:38 Michael Van Canneyt Status new => assigned
2018-06-28 07:30 Ondrej Pokorny Note Added: 0109088
2018-06-28 08:40 Ondrej Pokorny File Added: mysqlconn-performance-02.patch
2018-06-28 08:58 Ondrej Pokorny File Added: mysqlconn-performance-03.patch
2018-06-28 08:58 Ondrej Pokorny Note Added: 0109090
2018-06-28 10:08 Michael Van Canneyt Fixed in Revision => 39319
2018-06-28 10:08 Michael Van Canneyt Note Added: 0109093
2018-06-28 10:08 Michael Van Canneyt Status assigned => resolved
2018-06-28 10:08 Michael Van Canneyt Fixed in Version => 3.1.1
2018-06-28 10:08 Michael Van Canneyt Resolution open => fixed
2018-06-28 10:08 Michael Van Canneyt Target Version => 3.2.0
2018-06-28 10:20 Ondrej Pokorny Note Added: 0109097
2018-06-28 10:20 Ondrej Pokorny Status resolved => closed
2018-06-29 09:55 LacaK Note Added: 0109124
2018-06-29 09:55 LacaK Status closed => feedback
2018-06-29 09:55 LacaK Resolution fixed => reopened
2018-06-29 09:55 LacaK File Added: mysql_negative_33919.patch
2018-06-29 11:26 Michael Van Canneyt Fixed in Revision 39319 => 39339.
2018-06-29 11:26 Michael Van Canneyt Note Added: 0109125
2018-06-29 11:26 Michael Van Canneyt Status feedback => resolved
2018-06-29 11:26 Michael Van Canneyt Resolution reopened => fixed
2018-06-29 11:40 Ondrej Pokorny Note Added: 0109126
2018-06-29 11:40 Ondrej Pokorny Status resolved => closed