zeoslib  UNKNOWN
 All Files
ZDbcMySqlUtils.pas
Go to the documentation of this file.
1 {*********************************************************}
2 { }
3 { Zeos Database Objects }
4 { MySQL Database Connectivity Classes }
5 { }
6 { Originally written by Sergey Seroukhov }
7 { and Sergey Merkuriev }
8 { }
9 {*********************************************************}
10 
11 {@********************************************************}
12 { Copyright (c) 1999-2012 Zeos Development Group }
13 { }
14 { License Agreement: }
15 { }
16 { This library is distributed in the hope that it will be }
17 { useful, but WITHOUT ANY WARRANTY; without even the }
18 { implied warranty of MERCHANTABILITY or FITNESS FOR }
19 { A PARTICULAR PURPOSE. See the GNU Lesser General }
20 { Public License for more details. }
21 { }
22 { The source code of the ZEOS Libraries and packages are }
23 { distributed under the Library GNU General Public }
24 { License (see the file COPYING / COPYING.ZEOS) }
25 { with the following modification: }
26 { As a special exception, the copyright holders of this }
27 { library give you permission to link this library with }
28 { independent modules to produce an executable, }
29 { regardless of the license terms of these independent }
30 { modules, and to copy and distribute the resulting }
31 { executable under terms of your choice, provided that }
32 { you also meet, for each linked independent module, }
33 { the terms and conditions of the license of that module. }
34 { An independent module is a module which is not derived }
35 { from or based on this library. If you modify this }
36 { library, you may extend this exception to your version }
37 { of the library, but you are not obligated to do so. }
38 { If you do not wish to do so, delete this exception }
39 { statement from your version. }
40 { }
41 { }
42 { The project web site is located on: }
43 { http://zeos.firmos.at (FORUM) }
44 { http://sourceforge.net/p/zeoslib/tickets/ (BUGTRACKER)}
45 { svn://svn.code.sf.net/p/zeoslib/code-0/trunk (SVN) }
46 { }
47 { http://www.sourceforge.net/projects/zeoslib. }
48 { }
49 { }
50 { Zeos Development Group. }
51 {********************************************************@}
52 
53 unit ZDbcMySqlUtils;
54 
55 interface
56 
57 {$I ZDbc.inc}
58 
59 uses
60  Classes, {$IFDEF MSEgui}mclasses,{$ENDIF} SysUtils, StrUtils,
61  ZSysUtils, ZDbcIntfs, ZPlainMySqlDriver, ZPlainMySqlConstants, ZDbcLogging,
62  ZCompatibility, ZDbcResultSetMetadata;
63 
64 const
65  MAXBUF = 65535;
66 
67 type
68  {** Silent exception }
69  EZMySQLSilentException = class(EAbort);
70 
71 {**
72  Converts a MySQL native types into ZDBC SQL types.
73  @param PlainDriver a native MySQL plain driver.
74  @param FieldHandle a handler to field description structure.
75  @param FieldFlags field flags.
76  @return a SQL undepended type.
77 }
78 function ConvertMySQLHandleToSQLType(FieldHandle: PZMySQLField;
79  CtrlsCPType: TZControlsCodePage): TZSQLType;
80 
81 {**
82  Convert string mysql field type to SQLType
83  @param string field type value
84  @result the SQLType field type value
85 }
86 function ConvertMySQLTypeToSQLType(TypeName, TypeNameFull: string;
87  const CtrlsCPType: TZControlsCodePage): TZSQLType;
88 
89 {**
90  Checks for possible sql errors.
91  @param PlainDriver a MySQL plain driver.
92  @param Handle a MySQL connection handle.
93  @param LogCategory a logging category.
94  @param LogMessage a logging message.
95 }
96 procedure CheckMySQLError(PlainDriver: IZMySQLPlainDriver;
97  Handle: PZMySQLConnect; LogCategory: TZLoggingCategory; const LogMessage: string);
98 procedure CheckMySQLPrepStmtError(PlainDriver: IZMySQLPlainDriver;
99  Handle: PZMySQLConnect; LogCategory: TZLoggingCategory; const LogMessage: string);
100 procedure EnterSilentMySQLError;
101 procedure LeaveSilentMySQLError;
102 
103 {**
104  Decodes a MySQL Version Value encoded with format:
105  (major_version * 10,000) + (minor_version * 100) + sub_version
106  into separated major, minor and subversion values
107  @param MySQLVersion an integer containing the MySQL Full Version to decode.
108  @param MajorVersion an integer containing the Major Version decoded.
109  @param MinorVersion an integer containing the Minor Version decoded.
110  @param SubVersion an integer contaning the Sub Version (revision) decoded.
111 }
112 procedure DecodeMySQLVersioning(const MySQLVersion: Integer;
113  out MajorVersion: Integer; out MinorVersion: Integer;
114  out SubVersion: Integer);
115 
116 {**
117  Encodes major, minor and subversion (revision) values in MySQL format:
118  (major_version * 10,000) + (minor_version * 100) + sub_version
119  For example, 4.1.12 is returned as 40112.
120  @param MajorVersion an integer containing the Major Version.
121  @param MinorVersion an integer containing the Minor Version.
122  @param SubVersion an integer containing the Sub Version (revision).
123  @return an integer containing the full version.
124 }
125 function EncodeMySQLVersioning(const MajorVersion: Integer;
126  const MinorVersion: Integer; const SubVersion: Integer): Integer;
127 
128 {**
129  Decodes a MySQL Version Value and Encodes it to a Zeos SQL Version format:
130  (major_version * 1,000,000) + (minor_version * 1,000) + sub_version
131  into separated major, minor and subversion values
132  @param MySQLVersion an integer containing the Full Version to decode.
133  @return Encoded Zeos SQL Version Value.
134 }
135 function ConvertMySQLVersionToSQLVersion( const MySQLVersion: Integer ): Integer;
136 
137 function getMySQLFieldSize (field_type: TMysqlFieldTypes; field_size: LongWord): LongWord;
138 
139 {**
140  Returns a valid TZColumnInfo from a FieldHandle
141  @param PlainDriver the MySQL PlainDriver interface
142  @param FieldHandle the handle of the fetched field
143  @returns a new TZColumnInfo
144 }
145 function GetMySQLColumnInfoFromFieldHandle(PlainDriver: IZMySQLPlainDriver;
146  FieldHandle: PZMySQLField; ConSettings: PZConSettings;
147  bUseResult:boolean): TZColumnInfo;
148 
149 procedure ConvertMySQLColumnInfoFromString(const TypeInfo: String;
150  ConSettings: PZConSettings; out TypeName, TypeInfoSecond: String;
151  out FieldType: TZSQLType; out ColumnSize: Integer; out Precision: Integer);
152 
153 function ReverseWordBytes(Src: Pointer): Word;
154 function ReverseLongWordBytes(Src: Pointer; Len: Byte): LongWord;
155 function ReverseQuadWordBytes(Src: Pointer; Len: Byte): UInt64;
156 
157 implementation
158 
159 uses ZMessages, Math, ZDbcUtils;
160 
161 threadvar
162  SilentMySQLError: Integer;
163 
164 procedure EnterSilentMySQLError;
165 begin
166  Inc(SilentMySQLError);
167 end;
168 
169 procedure LeaveSilentMySQLError;
170 begin
171  Dec(SilentMySQLError);
172 end;
173 
174 {**
175  Converts a MySQL native types into ZDBC SQL types.
176  @param PlainDriver a native MySQL plain driver.
177  @param FieldHandle a handler to field description structure.
178  @param FieldFlags a field flags.
179  @return a SQL undepended type.
180 }
181 function ConvertMySQLHandleToSQLType(FieldHandle: PZMySQLField;
182  CtrlsCPType: TZControlsCodePage): TZSQLType;
183 begin
184  case PMYSQL_FIELD(FieldHandle)^._type of
185  FIELD_TYPE_TINY:
186  if PMYSQL_FIELD(FieldHandle)^.flags and UNSIGNED_FLAG = 0 then
187  Result := stByte
188  else
189  Result := stShort;
190  FIELD_TYPE_YEAR: //word value
191  Result := stShort;
192  FIELD_TYPE_SHORT:
193  if PMYSQL_FIELD(FieldHandle)^.flags and UNSIGNED_FLAG = 0 then
194  Result := stInteger
195  else
196  Result := stShort;
197  FIELD_TYPE_INT24, FIELD_TYPE_LONG:
198  if PMYSQL_FIELD(FieldHandle)^.flags and UNSIGNED_FLAG = 0 then
199  Result := stInteger
200  else
201  Result := stLong;
202  FIELD_TYPE_LONGLONG:
203  if PMYSQL_FIELD(FieldHandle)^.flags and UNSIGNED_FLAG = 0 then
204  Result := stLong
205  else
206  Result := stBigDecimal;
207  FIELD_TYPE_FLOAT:
208  Result := stDouble;
209  FIELD_TYPE_DECIMAL, FIELD_TYPE_NEWDECIMAL: {ADDED FIELD_TYPE_NEWDECIMAL by fduenas 20-06-2006}
210  if PMYSQL_FIELD(FieldHandle)^.decimals = 0 then
211  if PMYSQL_FIELD(FieldHandle)^.length < 11 then
212  Result := stInteger
213  else
214  Result := stLong
215  else
216  Result := stDouble;
217  FIELD_TYPE_DOUBLE:
218  Result := stDouble;
219  FIELD_TYPE_DATE, FIELD_TYPE_NEWDATE:
220  Result := stDate;
221  FIELD_TYPE_TIME:
222  Result := stTime;
223  FIELD_TYPE_DATETIME, FIELD_TYPE_TIMESTAMP:
224  Result := stTimestamp;
225  FIELD_TYPE_TINY_BLOB, FIELD_TYPE_MEDIUM_BLOB,
226  FIELD_TYPE_LONG_BLOB, FIELD_TYPE_BLOB:
227  if (PMYSQL_FIELD(FieldHandle)^.flags and BINARY_FLAG) = 0 then
228  If ( CtrlsCPType = cCP_UTF16) then
229  Result := stUnicodeStream
230  else
231  Result := stAsciiStream
232  else
233  Result := stBinaryStream;
234  FIELD_TYPE_BIT: //http://dev.mysql.com/doc/refman/5.1/en/bit-type.html
235  case PMYSQL_FIELD(FieldHandle)^.length of
236  1..8: Result := stByte;
237  9..16: Result := stShort;
238  17..32: Result := stInteger;
239  else Result := stLong;
240  end;
241  FIELD_TYPE_VARCHAR,
242  FIELD_TYPE_VAR_STRING,
243  FIELD_TYPE_STRING:
244  if (PMYSQL_FIELD(FieldHandle)^.flags and BINARY_FLAG) = 0 then
245  if ( CtrlsCPType = cCP_UTF16) then
246  Result := stUnicodeString
247  else
248  Result := stString
249  else
250  Result := stBytes;
251  FIELD_TYPE_ENUM:
252  Result := stString;
253  FIELD_TYPE_SET:
254  Result := stString;
255  FIELD_TYPE_NULL:
256  // Example: SELECT NULL FROM DUAL
257  Result := stString;
258  FIELD_TYPE_GEOMETRY:
259  // Todo: Would be nice to show as WKT.
260  Result := stBinaryStream;
261  else
262  raise Exception.Create('Unknown MySQL data type!');
263  end;
264 end;
265 
266 {**
267  Convert string mysql field type to SQLType
268  @param string field type value
269  @result the SQLType field type value
270 }
271 function ConvertMySQLTypeToSQLType(TypeName, TypeNameFull: string;
272  const CtrlsCPType: TZControlsCodePage): TZSQLType;
273 const
274  GeoTypes: array[0..7] of string = (
275  'POINT','LINESTRING','POLYGON','GEOMETRY',
276  'MULTIPOINT','MULTILINESTRING','MULTIPOLYGON','GEOMETRYCOLLECTION'
277  );
278 var
279  IsUnsigned: Boolean;
280  Posi, Len, i: Integer;
281  Spec: string;
282 begin
283  TypeName := UpperCase(TypeName);
284  TypeNameFull := UpperCase(TypeNameFull);
285  Result := stUnknown;
286 
287  Posi := FirstDelimiter(' ', TypeName);
288  if Posi > 0 then
289  TypeName := Copy(TypeName, 1, Posi - 1);
290 
291  Spec := '';
292  Posi := FirstDelimiter(' ', TypeNameFull);
293  if Posi > 0 then
294  Spec := Copy(TypeNameFull, Posi + 1, Length(TypeNameFull)-Posi);
295 
296  IsUnsigned := Pos('UNSIGNED', Spec) > 0;
297 
298  if TypeName = 'TINYINT' then
299  begin
300  if not IsUnsigned then
301  Result := stShort
302  else
303  Result := stByte;
304  end
305  else if TypeName = 'YEAR' then
306  Result := stShort
307  else if TypeName = 'SMALLINT' then
308  begin
309  if IsUnsigned then
310  Result := stInteger
311  else
312  Result := stShort;
313  end
314  else if TypeName = 'MEDIUMINT' then
315  Result := stInteger
316  else if (TypeName = 'INT') or (TypeName = 'INTEGER') then
317  begin
318  if IsUnsigned then
319  Result := stLong
320  else
321  Result := stInteger
322  end
323  else if TypeName = 'BIGINT' then
324  Result := stLong
325  else if TypeName = 'INT24' then
326  Result := stLong
327  else if TypeName = 'REAL' then
328  begin
329  if IsUnsigned then
330  Result := stDouble
331  else
332  Result := stFloat;
333  end
334  else if TypeName = 'FLOAT' then
335  begin
336 // if IsUnsigned then
337  Result := stDouble
338 // else Result := stFloat;
339  end
340  else if TypeName = 'DECIMAL' then
341  begin
342  if EndsWith(TypeNameFull, ',0)') then
343  begin
344  Len := StrToInt(Copy(TypeNameFull, 9, Length(TypeNameFull) - 11));
345  if Len < 10 then
346  Result := stInteger
347  else
348  Result := stLong;
349  end
350  else
351  Result := stDouble;
352  end
353  else if TypeName = 'DOUBLE' then
354  Result := stDouble
355  else if TypeName = 'CHAR' then
356  Result := stString
357  else if TypeName = 'VARCHAR' then
358  Result := stString
359  else if TypeName = 'VARBINARY' then
360  Result := stBytes
361  else if TypeName = 'BINARY' then
362  Result := stBytes
363  else if TypeName = 'DATE' then
364  Result := stDate
365  else if TypeName = 'TIME' then
366  Result := stTime
367  else if TypeName = 'TIMESTAMP' then
368  Result := stTimestamp
369  else if TypeName = 'DATETIME' then
370  Result := stTimestamp
371  else if TypeName = 'TINYBLOB' then
372  Result := stBinaryStream
373  else if TypeName = 'BLOB' then
374  Result := stBinaryStream
375  else if TypeName = 'MEDIUMBLOB' then
376  Result := stBinaryStream
377  else if TypeName = 'LONGBLOB' then
378  Result := stBinaryStream
379  else if TypeName = 'TINYTEXT' then
380  Result := stAsciiStream
381  else if TypeName = 'TEXT' then
382  Result := stAsciiStream
383  else if TypeName = 'MEDIUMTEXT' then
384  Result := stAsciiStream
385  else if TypeName = 'LONGTEXT' then
386  Result := stAsciiStream
387  else if TypeName = 'ENUM' then
388  begin
389  if (TypeNameFull = 'ENUM(''Y'',''N'')')
390  or (TypeNameFull = 'ENUM(''N'',''Y'')') then
391  Result := stBoolean
392  else
393  Result := stString;
394  end
395  else if TypeName = 'SET' then
396  Result := stString
397  else if TypeName = 'BIT' then //see: http://dev.mysql.com/doc/refman/5.1/en/bit-type.html
398  begin
399  Posi := Pos('(', TypeNameFull);
400  if (Posi > 0) and EndsWith(TypeNameFull, ')') then
401  begin
402  Len := StrToIntDef(Copy(TypeNameFull, Posi+1, Length(TypeNameFull)-Posi-1), 1);
403  case Len of
404  1..8: Result := stByte;
405  9..16: Result := stShort;
406  17..32: Result := stInteger;
407  else Result := stLong;
408  end;
409  end
410  else
411  Result := stByte
412  end else
413  for i := 0 to Length(GeoTypes) - 1 do
414  if GeoTypes[i] = TypeName then
415  Result := stBinaryStream;
416 
417  if ( CtrlsCPType = cCP_UTF16) then
418  case result of
419  stString: Result := stUnicodeString;
420  stAsciiStream: Result := stUnicodeStream;
421  end;
422 
423  if Result = stUnknown then
424  raise Exception.Create('Unknown MySQL data type!');
425 end;
426 
427 {**
428  Checks for possible sql errors.
429  @param PlainDriver a MySQL plain driver.
430  @param Handle a MySQL connection handle.
431  @param LogCategory a logging category.
432  @param LogMessage a logging message.
433 }
434 procedure CheckMySQLError(PlainDriver: IZMySQLPlainDriver;
435  Handle: PZMySQLConnect; LogCategory: TZLoggingCategory; const LogMessage: string);
436 var
437  ErrorMessage: string;
438  ErrorCode: Integer;
439 begin
440  ErrorMessage := Trim(String(PlainDriver.GetLastError(Handle)));
441  ErrorCode := PlainDriver.GetLastErrorCode(Handle);
442  if (ErrorCode <> 0) and (ErrorMessage <> '') then
443  begin
444  if SilentMySQLError > 0 then
445  raise EZMySQLSilentException.CreateFmt(SSQLError1, [ErrorMessage]);
446 
447  DriverManager.LogError(LogCategory, PlainDriver.GetProtocol, LogMessage,
448  ErrorCode, ErrorMessage);
449  raise EZSQLException.CreateWithCode(ErrorCode,
450  Format(SSQLError1, [ErrorMessage]));
451  end;
452 end;
453 
454 procedure CheckMySQLPrepStmtError(PlainDriver: IZMySQLPlainDriver;
455  Handle: PZMySQLConnect; LogCategory: TZLoggingCategory; const LogMessage: string);
456 var
457  ErrorMessage: string;
458  ErrorCode: Integer;
459 begin
460  ErrorMessage := Trim(String(PlainDriver.GetLastPreparedError(Handle)));
461  ErrorCode := PlainDriver.GetLastPreparedErrorCode(Handle);
462  if (ErrorCode <> 0) and (ErrorMessage <> '') then
463  begin
464  if SilentMySQLError > 0 then
465  raise EZMySQLSilentException.CreateFmt(SSQLError1, [ErrorMessage]);
466 
467  DriverManager.LogError(LogCategory,PlainDriver.GetProtocol,LogMessage,ErrorCode, ErrorMessage);
468  raise EZSQLException.CreateWithCode(ErrorCode,
469  Format(SSQLError1, [ErrorMessage]));
470  end;
471 end;
472 
473 {**
474  Decodes a MySQL Version Value encoded with format:
475  (major_version * 10,000) + (minor_version * 100) + sub_version
476  into separated major, minor and subversion values
477  @param MySQLVersion an integer containing the MySQL Full Version to decode.
478  @param MajorVersion an integer containing the Major Version decoded.
479  @param MinorVersion an integer containing the Minor Version decoded.
480  @param SubVersion an integer contaning the Sub Version (revision) decoded.
481 }
482 procedure DecodeMySQLVersioning(const MySQLVersion: Integer;
483  out MajorVersion: Integer; out MinorVersion: Integer;
484  out SubVersion: Integer);
485 begin
486  MajorVersion := MySQLVersion div 10000;
487  MinorVersion := (MySQLVersion - (MajorVersion * 10000)) div 100;
488  SubVersion := MySQLVersion-(MajorVersion*10000)-(MinorVersion*100);
489 end;
490 
491 {**
492  Encodes major, minor and subversion (revision) values in MySQL format:
493  (major_version * 10,000) + (minor_version * 100) + sub_version
494  For example, 4.1.12 is returned as 40112.
495  @param MajorVersion an integer containing the Major Version.
496  @param MinorVersion an integer containing the Minor Version.
497  @param SubVersion an integer containing the Sub Version (revision).
498  @return an integer containing the full version.
499 }
500 function EncodeMySQLVersioning(const MajorVersion: Integer;
501  const MinorVersion: Integer; const SubVersion: Integer): Integer;
502 begin
503  Result := (MajorVersion * 10000) + (MinorVersion * 100) + SubVersion;
504 end;
505 
506 {**
507  Decodes a MySQL Version Value and Encodes it to a Zeos SQL Version format:
508  (major_version * 1,000,000) + (minor_version * 1,000) + sub_version
509  into separated major, minor and subversion values
510  So it transforms a version in format XYYZZ to XYYYZZZ where:
511  X = major_version
512  Y = minor_version
513  Z = sub version
514  @param MySQLVersion an integer containing the Full MySQL Version to decode.
515  @return Encoded Zeos SQL Version Value.
516 }
517 function ConvertMySQLVersionToSQLVersion( const MySQLVersion: Integer ): integer;
518 var
519  MajorVersion, MinorVersion, SubVersion: Integer;
520 begin
521  DecodeMySQLVersioning(MySQLVersion,MajorVersion,MinorVersion,SubVersion);
522  Result := EncodeSQLVersioning(MajorVersion,MinorVersion,SubVersion);
523 end;
524 
525 function getMySQLFieldSize (field_type: TMysqlFieldTypes; field_size: LongWord): LongWord;
526 var
527  FieldSize: LongWord;
528 Begin
529  FieldSize := field_size;
530 
531  case field_type of
532  FIELD_TYPE_TINY: Result := 1;
533  FIELD_TYPE_SHORT: Result := 2;
534  FIELD_TYPE_LONG: Result := 4;
535  FIELD_TYPE_LONGLONG: Result := 8;
536  FIELD_TYPE_FLOAT: Result := 4;
537  FIELD_TYPE_DOUBLE: Result := 8;
538  FIELD_TYPE_DATE: Result := sizeOf(MYSQL_TIME);
539  FIELD_TYPE_TIME: Result := sizeOf(MYSQL_TIME);
540  FIELD_TYPE_DATETIME: Result := sizeOf(MYSQL_TIME);
541  FIELD_TYPE_TINY_BLOB: Result := FieldSize; //stBytes
542  FIELD_TYPE_BLOB: Result := FieldSize;
543  FIELD_TYPE_STRING: Result := FieldSize;
544  else
545  Result := 255; {unknown ??}
546  end;
547 end;
548 
549 {**
550  Returns a valid TZColumnInfo from a FieldHandle
551  @param PlainDriver the MySQL PlainDriver interface
552  @param FieldHandle the handle of the fetched field
553  @returns a new TZColumnInfo
554 }
555 function GetMySQLColumnInfoFromFieldHandle(PlainDriver: IZMySQLPlainDriver;
556  FieldHandle: PZMySQLField;ConSettings: PZConSettings; bUseResult:boolean): TZColumnInfo;
557 var
558  FieldFlags: Integer;
559  FieldLength: ULong;
560 begin
561  if Assigned(FieldHandle) then
562  begin
563  Result := TZColumnInfo.Create;
564  FieldFlags := PlainDriver.GetFieldFlags(FieldHandle);
565 
566  Result.ColumnLabel := PlainDriver.ZDbcString(PlainDriver.GetFieldName(FieldHandle), ConSettings);
567  Result.ColumnName := PlainDriver.ZDbcString(PlainDriver.GetFieldOrigName(FieldHandle), ConSettings);
568  Result.TableName := PlainDriver.ZDbcString(PlainDriver.GetFieldTable(FieldHandle), ConSettings);
569  Result.ReadOnly := (PlainDriver.GetFieldTable(FieldHandle) = '');
570  Result.Writable := not Result.ReadOnly;
571  Result.ColumnType := ConvertMySQLHandleToSQLType(FieldHandle, ConSettings.CPType);
572  FieldLength := PMYSQL_FIELD(FieldHandle)^.length;
573  //EgonHugeist: arrange the MBCS field DisplayWidth to a proper count of Chars
574  if Result.ColumnType in [stString, stUnicodeString] then
575  case PMYSQL_FIELD(FieldHandle)^.charsetnr of
576  1, 84, {Big5}
577  95, 96, {cp932 japanese}
578  19, 85, {euckr}
579  24, 86, {gb2312}
580  38, 87, {gbk}
581  13, 88, {sjis}
582  35, 90, 128..151: {ucs2}
583  begin
584  Result.ColumnDisplaySize := (FieldLength div 4);
585  Result.Precision := GetFieldSize(Result.ColumnType, ConSettings,
586  Result.ColumnDisplaySize, 2, nil);
587  end;
588  33, 83, 192..215, { utf8 }
589  97, 98, { eucjpms}
590  12, 91: {ujis}
591  begin
592  Result.ColumnDisplaySize := (FieldLength div 3);
593  Result.Precision := GetFieldSize(Result.ColumnType,
594  ConSettings, Result.ColumnDisplaySize, 3, nil);
595  end;
596  54, 55, 101..124, {utf16}
597  56, 62, {utf16le}
598  60, 61, 160..183, {utf32}
599  45, 46, 224..247: {utf8mb4}
600  begin
601  Result.ColumnDisplaySize := (FieldLength div 4);
602  Result.Precision := GetFieldSize(Result.ColumnType,
603  ConSettings, Result.ColumnDisplaySize, 4, nil);
604  end;
605  else //1-Byte charsets
606  begin
607  Result.ColumnDisplaySize := FieldLength;
608  Result.Precision := GetFieldSize(Result.ColumnType,
609  ConSettings, Result.ColumnDisplaySize, 1, nil);
610  end;
611  end
612  else
613  Result.Precision := min(MaxBlobSize,FieldLength);
614 
615  if PMYSQL_FIELD(FieldHandle)^._type in [FIELD_TYPE_BLOB, FIELD_TYPE_MEDIUM_BLOB,
616  FIELD_TYPE_LONG_BLOB,FIELD_TYPE_STRING, FIELD_TYPE_VAR_STRING] then
617  begin
618  if bUseResult then //PMYSQL_FIELD(Field)^.max_length not valid
619  Result.MaxLenghtBytes:=Result.Precision
620  else
621  Result.MaxLenghtBytes:=PlainDriver.GetFieldMaxLength(FieldHandle);
622  end
623  else
624  Result.MaxLenghtBytes:=FieldLength;
625  Result.Scale := PlainDriver.GetFieldDecimals(FieldHandle);
626  if (AUTO_INCREMENT_FLAG and FieldFlags <> 0)
627  or (TIMESTAMP_FLAG and FieldFlags <> 0) then
628  Result.AutoIncrement := True;
629  if UNSIGNED_FLAG and FieldFlags <> 0 then
630  Result.Signed := False
631  else
632  Result.Signed := True;
633  if NOT_NULL_FLAG and FieldFlags <> 0 then
634  Result.Nullable := ntNoNulls
635  else
636  Result.Nullable := ntNullable;
637  // Properties not set via query results here will be fetched from table metadata.
638  end
639  else
640  Result := nil;
641 end;
642 
643 procedure ConvertMySQLColumnInfoFromString(const TypeInfo: String;
644  ConSettings: PZConSettings; out TypeName, TypeInfoSecond:
645  String; out FieldType: TZSQLType; out ColumnSize: Integer; out Precision: Integer);
646 var
647  TypeInfoList: TStrings;
648  TypeInfoFirst: String;
649  J, TempPos: Integer;
650 begin
651  TypeInfoList := TStringList.Create;
652  TypeInfoFirst := '';
653  TypeInfoSecond := '';
654  Precision := 0;
655  ColumnSize := 0;
656 
657  if StrPos(PChar(TypeInfo), '(') <> nil then
658  begin
659  PutSplitString(TypeInfoList, TypeInfo, '()');
660  TypeInfoFirst := TypeInfoList.Strings[0];
661  TypeInfoSecond := TypeInfoList.Strings[1];
662  end
663  else
664  TypeInfoFirst := TypeInfo;
665 
666  TypeInfoFirst := LowerCase(TypeInfoFirst);
667  TypeName := TypeInfoFirst;
668 
669  FieldType := ConvertMySQLTypeToSQLType(TypeInfoFirst, TypeInfo, Consettings.CPType);
670  { the column type is ENUM}
671  if TypeInfoFirst = 'enum' then
672  begin
673  PutSplitString(TypeInfoList, TypeInfoSecond, ',');
674  for J := 0 to TypeInfoList.Count-1 do
675  ColumnSize := Max(ColumnSize, Length(TypeInfoList.Strings[J]));
676  end
677  else
678  { the column type is decimal }
679  if ( Pos(',', TypeInfoSecond) > 0 ) and not ( TypeInfoFirst = 'set' ) then
680  begin
681  TempPos := FirstDelimiter(',', TypeInfoSecond);
682  ColumnSize := StrToIntDef(Copy(TypeInfoSecond, 1, TempPos - 1), 0);
683  Precision := StrToIntDef(Copy(TypeInfoSecond, TempPos + 1,
684  Length(TypeInfoSecond) - TempPos), 0);
685  end
686  else
687  begin
688  { the column type is other }
689  if (TypeInfoSecond <> '') and not (TypeInfoFirst = 'set') then
690  ColumnSize := StrToIntDef(TypeInfoSecond, 0)
691  else if TypeInfoFirst = 'tinyint' then
692  ColumnSize := 1
693  else if TypeInfoFirst = 'smallint' then
694  ColumnSize := 6
695  else if TypeInfoFirst = 'mediumint' then
696  ColumnSize := 6
697  else if TypeInfoFirst = 'int' then
698  ColumnSize := 11
699  else if TypeInfoFirst = 'integer' then
700  ColumnSize := 11
701  else if TypeInfoFirst = 'bigint' then
702  ColumnSize := 25
703  else if TypeInfoFirst = 'int24' then
704  ColumnSize := 25
705  else if TypeInfoFirst = 'real' then
706  ColumnSize := 12
707  else if TypeInfoFirst = 'float' then
708  ColumnSize := 12
709  else if TypeInfoFirst = 'decimal' then
710  ColumnSize := 12
711  else if TypeInfoFirst = 'numeric' then
712  ColumnSize := 12
713  else if TypeInfoFirst = 'double' then
714  ColumnSize := 22
715  else if TypeInfoFirst = 'char' then
716  ColumnSize := 1
717  else if TypeInfoFirst = 'varchar' then
718  ColumnSize := 255
719  else if TypeInfoFirst = 'date' then
720  ColumnSize := 10
721  else if TypeInfoFirst = 'time' then
722  ColumnSize := 8
723  else if TypeInfoFirst = 'timestamp' then
724  ColumnSize := 19
725  else if TypeInfoFirst = 'datetime' then
726  ColumnSize := 19
727  else if TypeInfoFirst = 'tinyblob' then
728  ColumnSize := 255
729  else if TypeInfoFirst = 'blob' then
730  ColumnSize := MAXBUF
731  else if TypeInfoFirst = 'mediumblob' then
732  ColumnSize := 16277215//may be 65535
733  else if TypeInfoFirst = 'longblob' then
734  ColumnSize := High(Integer)//2147483657//may be 65535
735  else if TypeInfoFirst = 'tinytext' then
736  ColumnSize := 255
737  else if TypeInfoFirst = 'text' then
738  ColumnSize := 65535
739  else if TypeInfoFirst = 'mediumtext' then
740  ColumnSize := 16277215 //may be 65535
741  else if TypeInfoFirst = 'enum' then
742  ColumnSize := 255
743  else if TypeInfoFirst = 'set' then
744  ColumnSize := 255;
745  end;
746  if FieldType in [stString, stUnicodeString] then
747  ColumnSize := GetFieldSize(FieldType, consettings, ColumnSize,
748  ConSettings.ClientCodePage.CharWidth, nil);
749 
750  FreeAndNil(TypeInfoList);
751 end;
752 
753 procedure ReverseBytes(const Src, Dest: Pointer; Len: Byte);
754 var b: Byte;
755  P: PAnsiChar;
756 begin
757  Len := Len -1;
758  P := PAnsiChar(Src)+Len;
759  for b := Len downto 0 do
760  (PAnsiChar(Dest)+B)^ := (P-B)^;
761 end;
762 
763 function ReverseWordBytes(Src: Pointer): Word;
764 begin
765  (PAnsiChar(@Result)+1)^ := PAnsiChar(Src)^;
766  PAnsiChar(@Result)^ := (PAnsiChar(Src)+1)^;
767 end;
768 
769 function ReverseLongWordBytes(Src: Pointer; Len: Byte): LongWord;
770 begin
771  Result := 0;
772  ReverseBytes(Src, @Result, Len);
773 end;
774 
775 function ReverseQuadWordBytes(Src: Pointer; Len: Byte): UInt64;
776 begin
777  Result := 0;
778  ReverseBytes(Src, @Result, Len);
779 end;
780 
781 end.