zeoslib  UNKNOWN
 All Files
ZDbcMySqlMetadata.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 ZDbcMySqlMetadata;
54 
55 interface
56 
57 {$I ZDbc.inc}
58 
59 uses
60  Types, Classes, {$IFDEF MSEgui}mclasses,{$ENDIF} SysUtils,
61  ZClasses, ZSysUtils, ZDbcIntfs, ZDbcMetadata, ZDbcResultSet, ZDbcConnection,
62  ZDbcCachedResultSet, ZDbcResultSetMetadata, ZURL, ZCompatibility;
63 
64 type
65 
66  // technobot 2008-06-26 - methods moved as is from TZMySQLDatabaseMetadata:
67  {** Implements MySQL Database Information. }
68  TZMySQLDatabaseInfo = class(TZAbstractDatabaseInfo)
69  protected
70  procedure GetVersion(var MajorVersion, MinorVersion: integer);
71  public
72  constructor Create(const Metadata: TZAbstractDatabaseMetadata);
73 
74  // database/driver/server info:
75  function GetDatabaseProductName: string; override;
76  function GetDatabaseProductVersion: string; override;
77  function GetDriverName: string; override;
78 // function GetDriverVersion: string; override; -> Same as parent
79  function GetDriverMajorVersion: Integer; override;
80  function GetDriverMinorVersion: Integer; override;
81 // function GetServerVersion: string; -> Not implemented
82 
83  // capabilities (what it can/cannot do):
84 // function AllProceduresAreCallable: Boolean; override; -> Not implemented
85 // function AllTablesAreSelectable: Boolean; override; -> Not implemented
86 // function SupportsMixedCaseIdentifiers: Boolean; override; -> Not implemented
87 // function SupportsMixedCaseQuotedIdentifiers: Boolean; override; -> Not implemented
88 // function SupportsAlterTableWithAddColumn: Boolean; override; -> Not implemented
89 // function SupportsAlterTableWithDropColumn: Boolean; override; -> Not implemented
90 // function SupportsColumnAliasing: Boolean; override; -> Not implemented
91 // function SupportsConvert: Boolean; override; -> Not implemented
92 // function SupportsConvertForTypes(FromType: TZSQLType; ToType: TZSQLType):
93 // Boolean; override; -> Not implemented
94 // function SupportsTableCorrelationNames: Boolean; override; -> Not implemented
95 // function SupportsDifferentTableCorrelationNames: Boolean; override; -> Not implemented
96 // function SupportsExpressionsInOrderBy: Boolean; override; -> Not implemented
97  function SupportsOrderByUnrelated: Boolean; override;
98 // function SupportsGroupBy: Boolean; override; -> Not implemented
99  function SupportsGroupByUnrelated: Boolean; override;
100  function SupportsGroupByBeyondSelect: Boolean; override;
101 // function SupportsLikeEscapeClause: Boolean; override; -> Not implemented
102 // function SupportsMultipleResultSets: Boolean; override; -> Not implemented
103 // function SupportsMultipleTransactions: Boolean; override; -> Not implemented
104 // function SupportsNonNullableColumns: Boolean; override; -> Not implemented
105 // function SupportsMinimumSQLGrammar: Boolean; override; -> Not implemented
106 // function SupportsCoreSQLGrammar: Boolean; override; -> Not implemented
107 // function SupportsExtendedSQLGrammar: Boolean; override; -> Not implemented
108 // function SupportsANSI92EntryLevelSQL: Boolean; override; -> Not implemented
109 // function SupportsANSI92IntermediateSQL: Boolean; override; -> Not implemented
110 // function SupportsANSI92FullSQL: Boolean; override; -> Not implemented
111  function SupportsIntegrityEnhancementFacility: Boolean; override;
112 // function SupportsOuterJoins: Boolean; override; -> Not implemented
113 // function SupportsFullOuterJoins: Boolean; override; -> Not implemented
114 // function SupportsLimitedOuterJoins: Boolean; override; -> Not implemented
115 // function SupportsSchemasInDataManipulation: Boolean; override; -> Not implemented
116 // function SupportsSchemasInProcedureCalls: Boolean; override; -> Not implemented
117 // function SupportsSchemasInTableDefinitions: Boolean; override; -> Not implemented
118 // function SupportsSchemasInIndexDefinitions: Boolean; override; -> Not implemented
119 // function SupportsSchemasInPrivilegeDefinitions: Boolean; override; -> Not implemented
120  function SupportsCatalogsInDataManipulation: Boolean; override;
121 // function SupportsCatalogsInProcedureCalls: Boolean; override; -> Not implemented
122  function SupportsCatalogsInTableDefinitions: Boolean; override;
123 // function SupportsCatalogsInIndexDefinitions: Boolean; override; -> Not implemented
124 // function SupportsCatalogsInPrivilegeDefinitions: Boolean; override; -> Not implemented
125 // function SupportsPositionedDelete: Boolean; override; -> Not implemented
126 // function SupportsPositionedUpdate: Boolean; override; -> Not implemented
127 // function SupportsSelectForUpdate: Boolean; override; -> Not implemented
128 // function SupportsStoredProcedures: Boolean; override; -> Not implemented
129  function SupportsSubqueriesInComparisons: Boolean; override;
130 // function SupportsSubqueriesInExists: Boolean; override; -> Not implemented
131 // function SupportsSubqueriesInIns: Boolean; override; -> Not implemented
132 // function SupportsSubqueriesInQuantifieds: Boolean; override; -> Not implemented
133 // function SupportsCorrelatedSubqueries: Boolean; override; -> Not implemented
134 // function SupportsUnion: Boolean; override; -> Not implemented
135  function SupportsUnionAll: Boolean; override;
136 // function SupportsOpenCursorsAcrossCommit: Boolean; override; -> Not implemented
137 // function SupportsOpenCursorsAcrossRollback: Boolean; override; -> Not implemented
138  function SupportsOpenStatementsAcrossCommit: Boolean; override;
139  function SupportsOpenStatementsAcrossRollback: Boolean; override;
140 // function SupportsTransactions: Boolean; override; -> Not implemented
141 // function SupportsTransactionIsolationLevel(Level: TZTransactIsolationLevel):
142 // Boolean; override; -> Not implemented
143  function SupportsDataDefinitionAndDataManipulationTransactions: Boolean; override;
144  function SupportsDataManipulationTransactionsOnly: Boolean; override;
145 // function SupportsResultSetType(_Type: TZResultSetType): Boolean; override; -> Not implemented
146 // function SupportsResultSetConcurrency(_Type: TZResultSetType;
147 // Concurrency: TZResultSetConcurrency): Boolean; override; -> Not implemented
148 // function SupportsBatchUpdates: Boolean; override; -> Not implemented
149 
150  // maxima:
151  function GetMaxBinaryLiteralLength: Integer; override;
152  function GetMaxCharLiteralLength: Integer; override;
153  function GetMaxColumnNameLength: Integer; override;
154  function GetMaxColumnsInGroupBy: Integer; override;
155  function GetMaxColumnsInIndex: Integer; override;
156  function GetMaxColumnsInOrderBy: Integer; override;
157  function GetMaxColumnsInSelect: Integer; override;
158  function GetMaxColumnsInTable: Integer; override;
159  function GetMaxConnections: Integer; override;
160  function GetMaxCursorNameLength: Integer; override;
161  function GetMaxIndexLength: Integer; override;
162 // function GetMaxSchemaNameLength: Integer; override; -> Not implemented
163 // function GetMaxProcedureNameLength: Integer; override; -> Not implemented
164  function GetMaxCatalogNameLength: Integer; override;
165  function GetMaxRowSize: Integer; override;
166  function GetMaxStatementLength: Integer; override;
167  function GetMaxStatements: Integer; override;
168  function GetMaxTableNameLength: Integer; override;
169  function GetMaxTablesInSelect: Integer; override;
170  function GetMaxUserNameLength: Integer; override;
171 
172  // policies (how are various data and operations handled):
173 // function IsReadOnly: Boolean; override; -> Not implemented
174 // function IsCatalogAtStart: Boolean; override; -> Not implemented
175  function DoesMaxRowSizeIncludeBlobs: Boolean; override;
176 // function NullsAreSortedHigh: Boolean; override; -> Not implemented
177 // function NullsAreSortedLow: Boolean; override; -> Not implemented
178 // function NullsAreSortedAtStart: Boolean; override; -> Not implemented
179 // function NullsAreSortedAtEnd: Boolean; override; -> Not implemented
180 // function NullPlusNonNullIsNull: Boolean; override; -> Not implemented
181 // function UsesLocalFiles: Boolean; override; -> Not implemented
182  function UsesLocalFilePerTable: Boolean; override;
183 // function StoresUpperCaseIdentifiers: Boolean; override; -> Not implemented
184 // function StoresLowerCaseIdentifiers: Boolean; override; -> Not implemented
185  function StoresMixedCaseIdentifiers: Boolean; override;
186 // function StoresUpperCaseQuotedIdentifiers: Boolean; override; -> Not implemented
187 // function StoresLowerCaseQuotedIdentifiers: Boolean; override; -> Not implemented
188 // function StoresMixedCaseQuotedIdentifiers: Boolean; override; -> Not implemented
189  function GetDefaultTransactionIsolation: TZTransactIsolationLevel; override;
190 // function DataDefinitionCausesTransactionCommit: Boolean; override; -> Not implemented
191 // function DataDefinitionIgnoredInTransactions: Boolean; override; -> Not implemented
192 
193  // interface details (terms, keywords, etc):
194  function GetSchemaTerm: string; override;
195  function GetProcedureTerm: string; override;
196  function GetCatalogTerm: string; override;
197 // function GetCatalogSeparator: string; override; -> Not implemented
198  function GetSQLKeywords: string; override;
199  function GetNumericFunctions: string; override;
200  function GetStringFunctions: string; override;
201  function GetSystemFunctions: string; override;
202  function GetTimeDateFunctions: string; override;
203  function GetSearchStringEscape: string; override;
204  function GetExtraNameCharacters: string; override;
205  end;
206 
207  {** Implements MySQL Database Metadata. }
208  TZMySQLDatabaseMetadata = class(TZAbstractDatabaseMetadata)
209  protected
210  function CreateDatabaseInfo: IZDatabaseInfo; override; // technobot 2008-06-26
211 
212  procedure GetCatalogAndNamePattern(const Catalog, SchemaPattern,
213  NamePattern: string; out OutCatalog, OutNamePattern: string);
214  function UncachedGetTables(const Catalog: string; const SchemaPattern: string;
215  const TableNamePattern: string; const Types: TStringDynArray): IZResultSet; override;
216 // function UncachedGetSchemas: IZResultSet; override; -> Not implemented
217  function UncachedGetCatalogs: IZResultSet; override;
218  function UncachedGetTableTypes: IZResultSet; override;
219  function UncachedGetColumns(const Catalog: string; const SchemaPattern: string;
220  const TableNamePattern: string; const ColumnNamePattern: string): IZResultSet; override;
221  function UncachedGetTablePrivileges(const Catalog: string; const SchemaPattern: string;
222  const TableNamePattern: string): IZResultSet; override;
223  function UncachedGetColumnPrivileges(const Catalog: string; const Schema: string;
224  const Table: string; const ColumnNamePattern: string): IZResultSet; override;
225  function UncachedGetPrimaryKeys(const Catalog: string; const Schema: string;
226  const Table: string): IZResultSet; override;
227  function UncachedGetImportedKeys(const Catalog: string; const Schema: string;
228  const Table: string): IZResultSet; override;
229  function UncachedGetExportedKeys(const Catalog: string; const Schema: string;
230  const Table: string): IZResultSet; override;
231  function UncachedGetCrossReference(const PrimaryCatalog: string; const PrimarySchema: string;
232  const PrimaryTable: string; const ForeignCatalog: string; const ForeignSchema: string;
233  const ForeignTable: string): IZResultSet; override;
234  function UncachedGetIndexInfo(const Catalog: string; const Schema: string; const Table: string;
235  Unique: Boolean; Approximate: Boolean): IZResultSet; override;
236 // function UncachedGetSequences(const Catalog: string; const SchemaPattern: string;
237 // const SequenceNamePattern: string): IZResultSet; override; -> Not implemented
238  function UncachedGetProcedures(const Catalog: string; const SchemaPattern: string;
239  const ProcedureNamePattern: string): IZResultSet; override;
240  function UncachedGetProcedureColumns(const Catalog: string; const SchemaPattern: string;
241  const ProcedureNamePattern: string; const ColumnNamePattern: string):
242  IZResultSet; override;
243  function UncachedGetVersionColumns(const Catalog: string; const Schema: string;
244  const Table: string): IZResultSet; override;
245  function UncachedGetTypeInfo: IZResultSet; override;
246 // function UncachedGetUDTs(const Catalog: string; const SchemaPattern: string;
247 // const TypeNamePattern: string; const Types: TIntegerDynArray): IZResultSet; override;
248  function UncachedGetCollationAndCharSet(const Catalog, SchemaPattern,
249  TableNamePattern, ColumnNamePattern: string): IZResultSet; override; //EgonHugeist
250  function UncachedGetCharacterSets: IZResultSet; override; //EgonHugeist
251  public
252  destructor Destroy; override;
253  end;
254 
255 implementation
256 
257 uses
258  Math, ZMessages, ZDbcUtils, ZCollections, ZDbcMySqlUtils;
259 
260 { TZMySQLDatabaseInfo }
261 
262 {**
263  Constructs this object.
264  @param Metadata the interface of the correpsonding database metadata object
265 }
266 constructor TZMySQLDatabaseInfo.Create(const Metadata: TZAbstractDatabaseMetadata);
267 begin
268  inherited Create(MetaData, '`');
269 end;
270 
271 //----------------------------------------------------------------------
272 // First, a variety of minor information about the target database.
273 
274 {**
275  What's the name of this database product?
276  @return database product name
277 }
278 function TZMySQLDatabaseInfo.GetDatabaseProductName: string;
279 begin
280  Result := 'MySQL';
281 end;
282 
283 {**
284  What's the version of this database product?
285  @return database version
286 }
287 function TZMySQLDatabaseInfo.GetDatabaseProductVersion: string;
288 begin
289  Result := '3+';
290 end;
291 
292 {**
293  What's the name of this JDBC driver?
294  @return JDBC driver name
295 }
296 function TZMySQLDatabaseInfo.GetDriverName: string;
297 begin
298  Result := 'Zeos Database Connectivity Driver for MySQL';
299 end;
300 
301 {**
302  What's this JDBC driver's major version number?
303  @return JDBC driver major version
304 }
305 function TZMySQLDatabaseInfo.GetDriverMajorVersion: Integer;
306 begin
307  Result := 1;
308 end;
309 
310 {**
311  What's this JDBC driver's minor version number?
312  @return JDBC driver minor version number
313 }
314 function TZMySQLDatabaseInfo.GetDriverMinorVersion: Integer;
315 begin
316  Result := 1;
317 end;
318 
319 {**
320  Does the database use a file for each table?
321  @return true if the database uses a local file for each table
322 }
323 function TZMySQLDatabaseInfo.UsesLocalFilePerTable: Boolean;
324 begin
325  Result := True;
326 end;
327 
328 {**
329  Does the database treat mixed case unquoted SQL identifiers as
330  case insensitive and store them in mixed case?
331  @return <code>true</code> if so; <code>false</code> otherwise
332 }
333 function TZMySQLDatabaseInfo.StoresMixedCaseIdentifiers: Boolean;
334 begin
335  Result := True;
336 end;
337 
338 {**
339  Gets a comma-separated list of all a database's SQL keywords
340  that are NOT also SQL92 keywords.
341  @return the list
342 }
343 function TZMySQLDatabaseInfo.GetSQLKeywords: string;
344 begin
345  Result := 'AUTO_INCREMENT,BINARY,BLOB,ENUM,INFILE,LOAD,MEDIUMINT,OPTION,'
346  + 'OUTFILE,REPLACE,SET,TEXT,UNSIGNED,ZEROFILL';
347  { mdaems : added all reserved words indicated by mysql documentation (up to mysql 5.1)}
348  Result := Result + 'ACCESSIBLE,ADD,ALL,ALTER,ANALYZE,AND,ASC,ASENSITIVE,'
349  + ' BEFORE,BETWEEN,BIGINT,BOTH,CALL,CASCADE,CASE,CHANGE,CHARACTER,CHECK,'
350  + 'COLLATE,COLUMN,CONDITION,CONSTRAINT,CONTINUE,CONVERT,CROSS,'
351  + 'CURRENT_DATE,CURRENT_TIME,CURRENT_TIMESTAMP,CURRENT_USER,CURSOR,'
352  + 'DATABASE,DATABASES,DAY_HOUR,DAY_MICROSECOND,DAY_MINUTE,DAY_SECOND,'
353  + 'DEC,DECIMAL,DECLARE,DEFAULT,DELAYED,DESC,DESCRIBE,DETERMINISTIC,'
354  + 'DISTINCT,DISTINCTROW,DIV,DOUBLE,DUAL,EACH,ELSE,ELSEIF,ENCLOSED,'
355  + 'ESCAPED,EXISTS,EXIT,EXPLAIN,FALSE,FETCH,FLOAT,FLOAT4,FLOAT8,FOR,'
356  + 'FORCE,FOREIGN,FULLTEXT,GENERAL,GRANT,HIGH_PRIORITY,HOUR_MICROSECOND,'
357  + 'HOUR_MINUTE,HOUR_SECOND,IF,IGNORE,IGNORE_SERVER_IDS,IN,INNER,INOUT,INSENSITIVE,INT,'
358  + 'INT1,INT2,INT3,INT4,INT8,INTERVAL,ITERATE,JOIN,KEYS,KILL,LEADING,'
359  + 'LEAVE,LEFT,LIKE,LIMIT,LINEAR,LINES,LOCALTIME,LOCALTIMESTAMP,LOCK,'
360  + 'LONG,LONGBLOB,LONGTEXT,LOOP,LOW_PRIORITY,MASTER_HEARTBEAT_PERIOD,MASTER_SSL_VERIFY_SERVER_CERT,'
361  + 'MATCH,MAXVALUE,MEDIUMBLOB,MEDIUMTEXT,MIDDLEINT,MINUTE_MICROSECOND,MINUTE_SECOND,'
362  + 'MOD,MODIFIES,NATURAL,NOT,NO_WRITE_TO_BINLOG,NUMERIC,OPTIMIZE,'
363  + 'OPTIONALLY,OR,OUT,OUTER,PRECISION,PROCEDURE,PURGE,RANGE,READ,READS,'
364  + 'READ_ONLY,READ_WRITE,REAL,REFERENCES,REGEXP,RELEASE,RENAME,REPEAT,'
365  + 'REQUIRE,RESIGNAL SIGNAL,RESTRICT,RETURN,REVOKE,RIGHT,RLIKE,SCHEMA,SCHEMAS,'
366  + 'SECOND_MICROSECOND,SENSITIVE,SEPARATOR,SHOW,SLOW,SMALLINT,SPATIAL,'
367  + 'SPECIFIC,SQL,SQLEXCEPTION,SQLSTATE,SQLWARNING,SQL_BIG_RESULT,'
368  + 'SQL_CALC_FOUND_ROWS,SQL_SMALL_RESULT,SSL,STARTING,STRAIGHT_JOIN,'
369  + 'TERMINATED,THEN,TINYBLOB,TINYINT,TINYTEXT,TO,TRAILING,TRIGGER,'
370  + 'TRUE,UNDO,UNION,UNIQUE,UNLOCK,USAGE,USE,USING,UTC_DATE,UTC_TIME,'
371  + 'UTC_TIMESTAMP,VARBINARY,VARCHARACTER,VARYING,WHEN,WHILE,WITH,'
372  + 'WRITE,X509,XOR,YEAR_MONTH,ACCESSIBLE,LINEAR,'
373  + 'MASTER_SSL_VERIFY_SERVER_CERT,RANGE,READ_ONLY,READ_WRITE';
374 end;
375 
376 {**
377  Gets a comma-separated list of math functions. These are the
378  X/Open CLI math function names used in the JDBC function escape
379  clause.
380  @return the list
381 }
382 function TZMySQLDatabaseInfo.GetNumericFunctions: string;
383 begin
384  Result := 'ABS,ACOS,ASIN,ATAN,ATAN2,BIT_COUNT,CEILING,COS,COT,DEGREES,EXP,'
385  + 'FLOOR,LOG,LOG10,MAX,MIN,MOD,PI,POW,POWER,RADIANS,RAND,ROUND,SIN,SQRT,'
386  + 'TAN,TRUNCATE';
387  { mdaems : added all numeric functions indicated by mysql documentation (up to mysql 5.1)}
388  Result := Result + 'BIT_COUNT,CEIL,CRC32,LN,LOG2,SIGN,UUID';
389 end;
390 
391 {**
392  Gets a comma-separated list of string functions. These are the
393  X/Open CLI string function names used in the JDBC function escape
394  clause.
395  @return the list
396 }
397 function TZMySQLDatabaseInfo.GetStringFunctions: string;
398 begin
399  Result := 'ASCII,CHAR,CHAR_LENGTH,CHARACTER_LENGTH,CONCAT,ELT,FIELD,'
400  + 'FIND_IN_SET,INSERT,INSTR,INTERVAL,LCASE,LEFT,LENGTH,LOCATE,LOWER,LTRIM,'
401  + 'MID,POSITION,OCTET_LENGTH,REPEAT,REPLACE,REVERSE,RIGHT,RTRIM,SPACE,'
402  + 'SOUNDEX,SUBSTRING,SUBSTRING_INDEX,TRIM,UCASE,UPPER';
403  { mdaems : added all string functions indicated by mysql documentation (up to mysql 5.1)}
404  Result := Result + 'AES_DECRYPT,AES_ENCRYPT,BIN,BIT_LENGTH,CHARSET,'
405  + 'COERCIBILITY,COLLATION,COMPRESS,CONCAT_WS,DECODE,DES_DECRYPT,DES_ENCRYPT,'
406  + 'ENCODE,ENCRYPT,EXPORT_SET,FORMAT,HEX,LOAD_FILE,LPAD,MAKE_SET,MD5,OCT,ORD,'
407  + 'QUOTE,RPAD,STRCMP,SHA,SHA1,SUBSTR,UNHEX,EXTRACTVALUE,UPDATEXML,'
408  + 'UNCOMPRESS,UNCOMPRESSED_LENGTH';
409 end;
410 
411 {**
412  Gets a comma-separated list of system functions. These are the
413  X/Open CLI system function names used in the JDBC function escape
414  clause.
415  @return the list
416 }
417 function TZMySQLDatabaseInfo.GetSystemFunctions: string;
418 begin
419  Result := 'DATABASE,USER,SYSTEM_USER,SESSION_USER,PASSWORD,ENCRYPT,'
420  + 'LAST_INSERT_ID,VERSION';
421  { mdaems : added all system functions indicated by mysql documentation (up to mysql 5.1)}
422  Result := Result + 'BENCHMARK,CONNECTION_ID,CURRENT_USER,DEFAULT,FOUND_ROWS,'
423  + 'GET_LOCK,INET_ATON,INET_NTOA,IS_FREE_LOCK,IS_USED_LOCK,MASTER_POS_WAIT,'
424  + 'NAME_CONST,OLD_PASSWORD,RELEASE_LOCK,ROW_COUNT,SCHEMA,SLEEP';
425 end;
426 
427 {**
428  Gets a comma-separated list of time and date functions.
429  @return the list
430 }
431 function TZMySQLDatabaseInfo.GetTimeDateFunctions: string;
432 begin
433  Result := 'DAYOFWEEK,WEEKDAY,DAYOFMONTH,DAYOFYEAR,MONTH,DAYNAME,MONTHNAME,'
434  + 'QUARTER,WEEK,YEAR,HOUR,MINUTE,SECOND,PERIOD_ADD,PERIOD_DIFF,TO_DAYS,'
435  + 'FROM_DAYS,DATE_FORMAT,TIME_FORMAT,CURDATE,CURRENT_DATE,CURTIME,'
436  + 'CURRENT_TIME,NOW,SYSDATE,CURRENT_TIMESTAMP,UNIX_TIMESTAMP,FROM_UNIXTIME,'
437  + 'SEC_TO_TIME,TIME_TO_SEC';
438  { mdaems : added all time and date functions indicated by mysql documentation (up to mysql 5.1)}
439  Result := Result + 'ADDDATE,ADDTIME,CONVERT_TZ,CURRENT_TIMESTAMP,DATE_ADD,'
440  + 'DATE_SUB,DATE,DATEDIFF,DAYOFWEEK,GET_FORMAT,LAST_DAY,LOCALTIME,'
441  + 'LOCALTIMESTAMP,MAKEDATE,MAKETIME,MICROSECOND,STR_TO_DATE,SUBDATE,SUBTIME,'
442  + 'TIMEDIFF,TIMESTAMP,TIMESTAMPADD,TIMESTAMPDIFF,UTC_DATE,UTC_TIME,'
443  + 'UTC_TIMESTAMP,WEEKOFYEAR,YEARWEEK';
444 end;
445 
446 {**
447  Gets the string that can be used to escape wildcard characters.
448  This is the string that can be used to escape '_' or '%' in
449  the string pattern style catalog search parameters.
450 
451  <P>The '_' character represents any single character.
452  <P>The '%' character represents any sequence of zero or
453  more characters.
454 
455  @return the string used to escape wildcard characters
456 }
457 function TZMySQLDatabaseInfo.GetSearchStringEscape: string;
458 begin
459  Result := '\';
460 end;
461 
462 {**
463  Gets all the "extra" characters that can be used in unquoted
464  identifier names (those beyond a-z, A-Z, 0-9 and _).
465  @return the string containing the extra characters
466 }
467 function TZMySQLDatabaseInfo.GetExtraNameCharacters: string;
468 begin
469  Result := '';
470 end;
471 
472 //--------------------------------------------------------------------
473 // Functions describing which features are supported.
474 
475 {**
476  Can an "ORDER BY" clause use columns not in the SELECT statement?
477  @return <code>true</code> if so; <code>false</code> otherwise
478 }
479 function TZMySQLDatabaseInfo.SupportsOrderByUnrelated: Boolean;
480 var
481  MajorVersion: Integer;
482  MinorVersion: Integer;
483 begin
484  GetVersion(MajorVersion, MinorVersion);
485  // changed from False by mdaems. After testing with lower versions, please correct.
486  Result := MajorVersion >= 5;
487 end;
488 
489 {**
490  Can a "GROUP BY" clause use columns not in the SELECT?
491  @return <code>true</code> if so; <code>false</code> otherwise
492 }
493 function TZMySQLDatabaseInfo.SupportsGroupByUnrelated: Boolean;
494 begin
495  Result := False;
496 end;
497 
498 {**
499  Can a "GROUP BY" clause add columns not in the SELECT
500  provided it specifies all the columns in the SELECT?
501  @return <code>true</code> if so; <code>false</code> otherwise
502 }
503 function TZMySQLDatabaseInfo.SupportsGroupByBeyondSelect: Boolean;
504 begin
505  Result := True;
506 end;
507 
508 {**
509  Is the SQL Integrity Enhancement Facility supported?
510  @return <code>true</code> if so; <code>false</code> otherwise
511 }
512 function TZMySQLDatabaseInfo.SupportsIntegrityEnhancementFacility: Boolean;
513 begin
514  Result := False;
515 end;
516 
517 {**
518  What's the database vendor's preferred term for "schema"?
519  @return the vendor term
520 }
521 function TZMySQLDatabaseInfo.GetSchemaTerm: string;
522 begin
523  Result := '';
524 end;
525 
526 {**
527  What's the database vendor's preferred term for "procedure"?
528  @return the vendor term
529 }
530 function TZMySQLDatabaseInfo.GetProcedureTerm: string;
531 begin
532  Result := '';
533 end;
534 
535 {**
536  What's the database vendor's preferred term for "catalog"?
537  @return the vendor term
538 }
539 function TZMySQLDatabaseInfo.GetCatalogTerm: string;
540 begin
541  Result := 'Database';
542 end;
543 
544 {**
545  Can a catalog name be used in a data manipulation statement?
546  @return <code>true</code> if so; <code>false</code> otherwise
547 }
548 function TZMySQLDatabaseInfo.SupportsCatalogsInDataManipulation: Boolean;
549 var
550  MajorVersion: Integer;
551  MinorVersion: Integer;
552 begin
553  GetVersion(MajorVersion, MinorVersion);
554  Result := ((MajorVersion = 3) and (MinorVersion >= 22)) or (MajorVersion > 3);
555 end;
556 
557 {**
558  Can a catalog name be used in a table definition statement?
559  @return <code>true</code> if so; <code>false</code> otherwise
560 }
561 function TZMySQLDatabaseInfo.SupportsCatalogsInTableDefinitions: Boolean;
562 begin
563  Result := False;
564 end;
565 
566 {**
567  Are subqueries in comparison expressions supported?
568  A JDBC Compliant<sup><font size=-2>TM</font></sup> driver always returns true.
569  @return <code>true</code> if so; <code>false</code> otherwise
570 }
571 function TZMySQLDatabaseInfo.SupportsSubqueriesInComparisons: Boolean;
572 begin
573  Result := True;
574 end;
575 
576 {**
577  Is SQL UNION ALL supported?
578  @return <code>true</code> if so; <code>false</code> otherwise
579 }
580 function TZMySQLDatabaseInfo.SupportsUnionAll: Boolean;
581 var
582  MajorVersion: Integer;
583  MinorVersion: Integer;
584 begin
585  GetVersion(MajorVersion, MinorVersion);
586  Result := MajorVersion >= 4;
587 end;
588 
589 {**
590  Can statements remain open across commits?
591  @return <code>true</code> if statements always remain open;
592  <code>false</code> if they might not remain open
593 }
594 function TZMySQLDatabaseInfo.SupportsOpenStatementsAcrossCommit: Boolean;
595 begin
596  Result := False;
597 end;
598 
599 {**
600  Can statements remain open across rollbacks?
601  @return <code>true</code> if statements always remain open;
602  <code>false</code> if they might not remain open
603 }
604 function TZMySQLDatabaseInfo.SupportsOpenStatementsAcrossRollback: Boolean;
605 begin
606  Result := False;
607 end;
608 
609 //----------------------------------------------------------------------
610 // The following group of methods exposes various limitations
611 // based on the target database with the current driver.
612 // Unless otherwise specified, a result of zero means there is no
613 // limit, or the limit is not known.
614 
615 {**
616  How many hex characters can you have in an inline binary literal?
617  @return max binary literal length in hex characters;
618  a result of zero means that there is no limit or the limit is not known
619 }
620 function TZMySQLDatabaseInfo.GetMaxBinaryLiteralLength: Integer;
621 begin
622  Result := 16777208;
623 end;
624 
625 {**
626  What's the max length for a character literal?
627  @return max literal length;
628  a result of zero means that there is no limit or the limit is not known
629 }
630 function TZMySQLDatabaseInfo.GetMaxCharLiteralLength: Integer;
631 begin
632  Result := 16777208;
633 end;
634 
635 {**
636  What's the limit on column name length?
637  @return max column name length;
638  a result of zero means that there is no limit or the limit is not known
639 }
640 function TZMySQLDatabaseInfo.GetMaxColumnNameLength: Integer;
641 begin
642  Result := 64;
643 end;
644 
645 {**
646  What's the maximum number of columns in a "GROUP BY" clause?
647  @return max number of columns;
648  a result of zero means that there is no limit or the limit is not known
649 }
650 function TZMySQLDatabaseInfo.GetMaxColumnsInGroupBy: Integer;
651 begin
652  Result := 16;
653 end;
654 
655 {**
656  What's the maximum number of columns allowed in an index?
657  @return max number of columns;
658  a result of zero means that there is no limit or the limit is not known
659 }
660 function TZMySQLDatabaseInfo.GetMaxColumnsInIndex: Integer;
661 begin
662  Result := 16;
663 end;
664 
665 {**
666  What's the maximum number of columns in an "ORDER BY" clause?
667  @return max number of columns;
668  a result of zero means that there is no limit or the limit is not known
669 }
670 function TZMySQLDatabaseInfo.GetMaxColumnsInOrderBy: Integer;
671 begin
672  Result := 16;
673 end;
674 
675 {**
676  What's the maximum number of columns in a "SELECT" list?
677  @return max number of columns;
678  a result of zero means that there is no limit or the limit is not known
679 }
680 function TZMySQLDatabaseInfo.GetMaxColumnsInSelect: Integer;
681 begin
682  Result := 256;
683 end;
684 
685 {**
686  What's the maximum number of columns in a table?
687  @return max number of columns;
688  a result of zero means that there is no limit or the limit is not known
689 }
690 function TZMySQLDatabaseInfo.GetMaxColumnsInTable: Integer;
691 begin
692  Result := 512;
693 end;
694 
695 {**
696  How many active connections can we have at a time to this database?
697  @return max number of active connections;
698  a result of zero means that there is no limit or the limit is not known
699 }
700 function TZMySQLDatabaseInfo.GetMaxConnections: Integer;
701 begin
702  Result := 0;
703 end;
704 
705 {**
706  What's the maximum cursor name length?
707  @return max cursor name length in bytes;
708  a result of zero means that there is no limit or the limit is not known
709 }
710 function TZMySQLDatabaseInfo.GetMaxCursorNameLength: Integer;
711 begin
712  Result := 64;
713 end;
714 
715 {**
716  Retrieves the maximum number of bytes for an index, including all
717  of the parts of the index.
718  @return max index length in bytes, which includes the composite of all
719  the constituent parts of the index;
720  a result of zero means that there is no limit or the limit is not known
721 }
722 function TZMySQLDatabaseInfo.GetMaxIndexLength: Integer;
723 begin
724  Result := 128;
725 end;
726 
727 {**
728  What's the maximum length of a catalog name?
729  @return max name length in bytes;
730  a result of zero means that there is no limit or the limit is not known
731 }
732 function TZMySQLDatabaseInfo.GetMaxCatalogNameLength: Integer;
733 begin
734  Result := 32;
735 end;
736 
737 {**
738  What's the maximum length of a single row?
739  @return max row size in bytes;
740  a result of zero means that there is no limit or the limit is not known
741 }
742 function TZMySQLDatabaseInfo.GetMaxRowSize: Integer;
743 begin
744  Result := 2147483639;
745 end;
746 
747 {**
748  Did getMaxRowSize() include LONGVARCHAR and LONGVARBINARY
749  blobs?
750  @return <code>true</code> if so; <code>false</code> otherwise
751 }
752 function TZMySQLDatabaseInfo.DoesMaxRowSizeIncludeBlobs: Boolean;
753 begin
754  Result := True;
755 end;
756 
757 {**
758  What's the maximum length of an SQL statement?
759  @return max length in bytes;
760  a result of zero means that there is no limit or the limit is not known
761 }
762 function TZMySQLDatabaseInfo.GetMaxStatementLength: Integer;
763 begin
764  Result := 65531;
765 end;
766 
767 {**
768  How many active statements can we have open at one time to this
769  database?
770  @return the maximum number of statements that can be open at one time;
771  a result of zero means that there is no limit or the limit is not known
772 }
773 function TZMySQLDatabaseInfo.GetMaxStatements: Integer;
774 begin
775  Result := 0;
776 end;
777 
778 {**
779  What's the maximum length of a table name?
780  @return max name length in bytes;
781  a result of zero means that there is no limit or the limit is not known
782 }
783 function TZMySQLDatabaseInfo.GetMaxTableNameLength: Integer;
784 begin
785  Result := 64;
786 end;
787 
788 {**
789  What's the maximum number of tables in a SELECT statement?
790  @return the maximum number of tables allowed in a SELECT statement;
791  a result of zero means that there is no limit or the limit is not known
792 }
793 function TZMySQLDatabaseInfo.GetMaxTablesInSelect: Integer;
794 begin
795  Result := 256;
796 end;
797 
798 {**
799  What's the maximum length of a user name?
800  @return max user name length in bytes;
801  a result of zero means that there is no limit or the limit is not known
802 }
803 function TZMySQLDatabaseInfo.GetMaxUserNameLength: Integer;
804 begin
805  Result := 16;
806 end;
807 
808 //----------------------------------------------------------------------
809 
810 {**
811  What's the database's default transaction isolation level? The
812  values are defined in <code>java.sql.Connection</code>.
813  @return the default isolation level
814  @see Connection
815 }
816 function TZMySQLDatabaseInfo.GetDefaultTransactionIsolation:
817  TZTransactIsolationLevel;
818 begin
819  Result := tiNone;
820 end;
821 
822 {**
823  Are both data definition and data manipulation statements
824  within a transaction supported?
825  @return <code>true</code> if so; <code>false</code> otherwise
826 }
827 function TZMySQLDatabaseInfo.
828  SupportsDataDefinitionAndDataManipulationTransactions: Boolean;
829 begin
830  Result := True;
831 end;
832 
833 {**
834  Are only data manipulation statements within a transaction
835  supported?
836  @return <code>true</code> if so; <code>false</code> otherwise
837 }
838 function TZMySQLDatabaseInfo.SupportsDataManipulationTransactionsOnly: Boolean;
839 begin
840  case Metadata.GetConnection.GetTransactionIsolation of
841  tiReadUncommitted: Result := True;
842  tiReadCommitted: Result := True;
843  tiRepeatableRead: Result := True;
844  tiSerializable: Result := True;
845  else
846  Result := False;
847  end;
848 end;
849 
850 {**
851  Gets the MySQL version info.
852  @param MajorVesion the major version of MySQL server.
853  @param MinorVersion the minor version of MySQL server.
854 }
855 procedure TZMySQLDatabaseInfo.GetVersion(var MajorVersion,
856  MinorVersion: Integer);
857 var
858  VersionList: TStrings;
859  Subversion : integer;
860 begin
861  DecodeSqlVersioning(Metadata.GetConnection.GetHostVersion,
862  MajorVersion,MinorVersion, Subversion);
863  if (Majorversion < 4) or ((majorversion=4) and (Minorversion = 0)) then
864  with Metadata.GetConnection.CreateStatement.ExecuteQuery('SELECT VERSION()') do
865  begin
866  VersionList := SplitString(String(GetString(1)), '.-');
867  try
868  if VersionList.Count >= 2 then
869  begin
870  MajorVersion := StrToIntDef(VersionList.Strings[0], 0);
871  MinorVersion := StrToIntDef(VersionList.Strings[1], 0);
872  end;
873  finally
874  VersionList.Free;
875  end;
876  Close;
877  end;
878 end;
879 
880 { TZMySQLDatabaseMetadata }
881 
882 {**
883  Destroys this object and cleanups the memory.
884 }
885 destructor TZMySQLDatabaseMetadata.Destroy;
886 begin
887  inherited Destroy;
888 end;
889 
890 {**
891  Constructs a database information object and returns the interface to it. Used
892  internally by the constructor.
893  @return the database information object interface
894 }
895 function TZMySQLDatabaseMetadata.CreateDatabaseInfo: IZDatabaseInfo;
896 begin
897  Result := TZMySQLDatabaseInfo.Create(Self);
898 end;
899 
900 procedure TZMySQLDatabaseMetadata.GetCatalogAndNamePattern(const Catalog,
901  SchemaPattern, NamePattern: string; out OutCatalog, OutNamePattern: string);
902 begin
903  if Catalog = '' then
904  begin
905  if SchemaPattern <> '' then
906  OutCatalog := NormalizePatternCase(SchemaPattern)
907  else
908  OutCatalog := NormalizePatternCase(FDatabase);
909  end
910  else
911  OutCatalog := NormalizePatternCase(Catalog);
912 
913  if NamePattern = '' then
914  OutNamePattern := '%'
915  else
916  OutNamePattern := NormalizePatternCase(NamePattern);
917 end;
918 
919 {**
920  Gets a description of tables available in a catalog.
921 
922  <P>Only table descriptions matching the catalog, schema, table
923  name and type criteria are returned. They are ordered by
924  TABLE_TYPE, TABLE_SCHEM and TABLE_NAME.
925 
926  <P>Each table description has the following columns:
927  <OL>
928  <LI><B>TABLE_CAT</B> String => table catalog (may be null)
929  <LI><B>TABLE_SCHEM</B> String => table schema (may be null)
930  <LI><B>TABLE_NAME</B> String => table name
931  <LI><B>TABLE_TYPE</B> String => table type. Typical types are "TABLE",
932  "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY",
933  "LOCAL TEMPORARY", "ALIAS", "SYNONYM".
934  <LI><B>REMARKS</B> String => explanatory comment on the table
935  </OL>
936 
937  <P><B>Note:</B> Some databases may not return information for
938  all tables.
939 
940  @param catalog a catalog name; "" retrieves those without a
941  catalog; null means drop catalog name from the selection criteria
942  @param schemaPattern a schema name pattern; "" retrieves those
943  without a schema
944  @param tableNamePattern a table name pattern
945  @param types a list of table types to include; null returns all types
946  @return <code>ResultSet</code> - each row is a table description
947  @see #getSearchStringEscape
948 }
949 function TZMySQLDatabaseMetadata.UncachedGetTables(const Catalog: string;
950  const SchemaPattern: string; const TableNamePattern: string;
951  const Types: TStringDynArray): IZResultSet;
952 var
953  LCatalog, LTableNamePattern: string;
954 begin
955  Result := inherited UncachedGetTables(Catalog, SchemaPattern, TableNamePattern, Types);
956 
957  GetCatalogAndNamePattern(Catalog, SchemaPattern, TableNamePattern,
958  LCatalog, LTableNamePattern);
959 
960  with GetConnection.CreateStatement.ExecuteQuery(
961  Format('SHOW TABLES FROM %s LIKE ''%s''',
962  [IC.Quote(LCatalog), LTableNamePattern])) do
963  begin
964  while Next do
965  begin
966  Result.MoveToInsertRow;
967  Result.UpdateString(1, LCatalog);
968  Result.UpdateString(3, GetString(1));
969  Result.UpdateString(4, 'TABLE');
970  Result.InsertRow;
971  end;
972  Close;
973  end;
974 
975  // If a table was specified but not found, check if it could be a temporary table
976  if not Result.First and (LTableNamePattern <> '%') then
977  begin
978  try
979  EnterSilentMySQLError;
980  try
981  if GetConnection.CreateStatement.ExecuteQuery(
982  Format('SHOW COLUMNS FROM %s.%s',
983  [IC.Quote(LCatalog),
984  IC.Quote(LTableNamePattern)])).Next then
985  begin
986  Result.MoveToInsertRow;
987  Result.UpdateString(1, LCatalog);
988  Result.UpdateString(3, LTableNamePattern);
989  Result.UpdateString(4, 'TABLE');
990  Result.InsertRow;
991  end;
992  finally
993  LeaveSilentMySQLError;
994  end;
995  except
996  on EZMySQLSilentException do ;
997  on EZSQLException do ;
998  end;
999  end;
1000 end;
1001 
1002 {**
1003  Gets the catalog names available in this database. The results
1004  are ordered by catalog name.
1005 
1006  <P>The catalog column is:
1007  <OL>
1008  <LI><B>TABLE_CAT</B> String => catalog name
1009  </OL>
1010 
1011  @return <code>ResultSet</code> - each row has a single String column that is a
1012  catalog name
1013 }
1014 function TZMySQLDatabaseMetadata.UncachedGetCatalogs: IZResultSet;
1015 begin
1016  Result:=inherited UncachedGetCatalogs;
1017 
1018  with GetConnection.CreateStatement.ExecuteQuery('SHOW DATABASES') do
1019  begin
1020  while Next do
1021  begin
1022  Result.MoveToInsertRow;
1023  Result.UpdateString(1, GetString(1));
1024  Result.InsertRow;
1025  end;
1026  Close;
1027  end;
1028 end;
1029 
1030 {**
1031  Gets the table types available in this database. The results
1032  are ordered by table type.
1033 
1034  <P>The table type is:
1035  <OL>
1036  <LI><B>TABLE_TYPE</B> String => table type. Typical types are "TABLE",
1037  "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY",
1038  "LOCAL TEMPORARY", "ALIAS", "SYNONYM".
1039  </OL>
1040 
1041  @return <code>ResultSet</code> - each row has a single String column that is a
1042  table type
1043 }
1044 function TZMySQLDatabaseMetadata.UncachedGetTableTypes: IZResultSet;
1045 begin
1046  Result:=inherited UncachedGetTableTypes;
1047 
1048  Result.MoveToInsertRow;
1049  Result.UpdateString(1, 'TABLE');
1050  Result.InsertRow;
1051 end;
1052 
1053 {**
1054  Gets a description of table columns available in
1055  the specified catalog.
1056 
1057  <P>Only column descriptions matching the catalog, schema, table
1058  and column name criteria are returned. They are ordered by
1059  TABLE_SCHEM, TABLE_NAME and ORDINAL_POSITION.
1060 
1061  <P>Each column description has the following columns:
1062  <OL>
1063  <LI><B>TABLE_CAT</B> String => table catalog (may be null)
1064  <LI><B>TABLE_SCHEM</B> String => table schema (may be null)
1065  <LI><B>TABLE_NAME</B> String => table name
1066  <LI><B>COLUMN_NAME</B> String => column name
1067  <LI><B>DATA_TYPE</B> short => SQL type from java.sql.Types
1068  <LI><B>TYPE_NAME</B> String => Data source dependent type name,
1069  for a UDT the type name is fully qualified
1070  <LI><B>COLUMN_SIZE</B> int => column size. For char or date
1071  types this is the maximum number of characters, for numeric or
1072  decimal types this is precision.
1073  <LI><B>BUFFER_LENGTH</B> is not used.
1074  <LI><B>DECIMAL_DIGITS</B> int => the number of fractional digits
1075  <LI><B>NUM_PREC_RADIX</B> int => Radix (typically either 10 or 2)
1076  <LI><B>NULLABLE</B> int => is NULL allowed?
1077  <UL>
1078  <LI> columnNoNulls - might not allow NULL values
1079  <LI> columnNullable - definitely allows NULL values
1080  <LI> columnNullableUnknown - nullability unknown
1081  </UL>
1082  <LI><B>REMARKS</B> String => comment describing column (may be null)
1083  <LI><B>COLUMN_DEF</B> String => default value (may be null)
1084  <LI><B>SQL_DATA_TYPE</B> int => unused
1085  <LI><B>SQL_DATETIME_SUB</B> int => unused
1086  <LI><B>CHAR_OCTET_LENGTH</B> int => for char types the
1087  maximum number of bytes in the column
1088  <LI><B>ORDINAL_POSITION</B> int => index of column in table
1089  (starting at 1)
1090  <LI><B>IS_NULLABLE</B> String => "NO" means column definitely
1091  does not allow NULL values; "YES" means the column might
1092  allow NULL values. An empty string means nobody knows.
1093  </OL>
1094 
1095  @param catalog a catalog name; "" retrieves those without a
1096  catalog; null means drop catalog name from the selection criteria
1097  @param schemaPattern a schema name pattern; "" retrieves those
1098  without a schema
1099  @param tableNamePattern a table name pattern
1100  @param columnNamePattern a column name pattern
1101  @return <code>ResultSet</code> - each row is a column description
1102  @see #getSearchStringEscape
1103 }
1104 function TZMySQLDatabaseMetadata.UncachedGetColumns(const Catalog: string;
1105  const SchemaPattern: string; const TableNamePattern: string;
1106  const ColumnNamePattern: string): IZResultSet;
1107 var
1108  I: Integer;
1109  MySQLType: TZSQLType;
1110  TempCatalog, TempColumnNamePattern, TempTableNamePattern: string;
1111 
1112  TypeName, TypeInfoSecond: String;
1113  Nullable, DefaultValue: String;
1114  HasDefaultValue: Boolean;
1115  ColumnSize, ColumnDecimals: Integer;
1116  OrdPosition: Integer;
1117 
1118  TableNameList: TStrings;
1119  TableNameLength: Integer;
1120  ColumnIndexes : Array[1..6] of integer;
1121  Res : IZResultset;
1122 
1123 begin
1124  Res:=inherited UncachedGetColumns(Catalog, SchemaPattern, TableNamePattern, ColumnNamePattern);
1125 
1126  GetCatalogAndNamePattern(Catalog, SchemaPattern, ColumnNamePattern,
1127  TempCatalog, TempColumnNamePattern);
1128 
1129  TableNameLength := 0;
1130  TableNameList := TStringList.Create;
1131  try
1132  with GetTables(Catalog, SchemaPattern, TableNamePattern, nil) do
1133  begin
1134  while Next do
1135  begin
1136  TableNameList.Add(GetString(3)); //TABLE_NAME
1137  TableNameLength := Max(TableNameLength, Length(TableNameList[TableNameList.Count - 1]));
1138  end;
1139  Close;
1140  end;
1141 
1142  for I := 0 to TableNameList.Count - 1 do
1143  begin
1144  OrdPosition := 1;
1145  TempTableNamePattern := TableNameList.Strings[I];
1146 
1147  with GetConnection.CreateStatement.ExecuteQuery(
1148  Format('SHOW FULL COLUMNS FROM %s.%s LIKE ''%s''',
1149  [IC.Quote(TempCatalog),
1150  IC.Quote(TempTableNamePattern),
1151  TempColumnNamePattern])) do
1152  begin
1153  ColumnIndexes[1] := FindColumn('Field');
1154  ColumnIndexes[2] := FindColumn('Type');
1155  ColumnIndexes[3] := FindColumn('Null');
1156  ColumnIndexes[4] := FindColumn('Extra');
1157  ColumnIndexes[5] := FindColumn('Default');
1158  ColumnIndexes[6] := FindColumn('Collation');
1159  while Next do
1160  begin
1161  {initialise some variables}
1162  Res.MoveToInsertRow;
1163  Res.UpdateString(1, TempCatalog);
1164  Res.UpdateString(2, '');
1165  Res.UpdateString(3, TempTableNamePattern) ;
1166  Res.UpdateString(4, GetString(ColumnIndexes[1]));
1167 
1168  ConvertMySQLColumnInfoFromString(GetString(ColumnIndexes[2]),
1169  ConSettings, TypeName,
1170  TypeInfoSecond, MySQLType, ColumnSize, ColumnDecimals);
1171  Res.UpdateInt(5, Ord(MySQLType));
1172  Res.UpdateString(6, TypeName);
1173  Res.UpdateInt(7, ColumnSize);
1174  Res.UpdateInt(8, MAXBUF);
1175  Res.UpdateInt(9, ColumnDecimals);
1176  Res.UpdateNull(10);
1177 
1178  { Sets nullable fields. }
1179  Nullable := GetString(ColumnIndexes[3]);
1180  if Nullable <> '' then
1181  if Nullable = 'YES' then
1182  begin
1183  Res.UpdateInt(11, Ord(ntNullable));
1184  Res.UpdateString(18, 'YES');
1185  end
1186  else
1187  begin
1188  Res.UpdateInt(11, Ord(ntNoNulls));
1189  Res.UpdateString(18, 'NO');
1190  end
1191  else
1192  begin
1193  Res.UpdateInt(11, 0);
1194  Res.UpdateString(18, 'NO');
1195  end;
1196  Res.UpdateString(12, GetString(ColumnIndexes[4]));
1197  // MySQL is a bit bizarre.
1198  if IsNull(ColumnIndexes[5]) then
1199  begin
1200  // MySQL bizarity 1:
1201  // NULL actually means that the default is NULL.
1202  // Superfluous, since there's a NULL / NOT NULL flag to control whether the field may have no value.
1203  // So we just ignore this, the field gets set to NULL if nothing was specified...
1204  HasDefaultValue := false;
1205  DefaultValue := '';
1206  end
1207  else
1208  begin
1209  DefaultValue := GetString(ColumnIndexes[5]);
1210  if not (DefaultValue = '') then
1211  HasDefaultValue := true
1212  else
1213  begin
1214  // MySQL bizarity 2:
1215  // For CHAR, BLOB, TEXT and SET types, '' either means: default value is '' or: no default value
1216  // There's absolutely no way of telling when using SHOW COLUMNS FROM,
1217  // the correct information can /only/ be discerned by using information_schema.
1218  // TODO: For now, just use '' as default value for these types, but this should really be fixed to use information_schema.
1219  // For ENUM types, '' means: default value is first value in enum set
1220  // For other types, '' means: no default value
1221  HasDefaultValue := false;
1222  if Pos('blob', TypeName) > 0 then HasDefaultValue := true;
1223  if Pos('text', TypeName) > 0 then HasDefaultValue := true;
1224  if Pos('char', TypeName) > 0 then HasDefaultValue := true;
1225  if 'set' = TypeName then HasDefaultValue := true;
1226  if 'enum' = TypeName then
1227  begin
1228  HasDefaultValue := true;
1229  DefaultValue := Copy(TypeInfoSecond, 2,length(TypeInfoSecond)-1);
1230  DefaultValue := Copy(DefaultValue, 1, Pos('''', DefaultValue) - 1);
1231  end;
1232  end;
1233  end;
1234  if HasDefaultValue then
1235  begin
1236  // String values in the 'Default value' field are not escaped with apostrophes.
1237  // Guess this makes it impossible to specify a function call or similar via default values.
1238  if (MySQLType in [stString, stUnicodeString, stBinaryStream, stAsciiStream]) then
1239  begin
1240  // Since we changed date/time-related columntypes to be presented
1241  // as strings, we need to move the CURRENT_TIMESTAMP-check to here.
1242  // Also left the other line in order to minimize the changes in ZeosLib
1243  if DefaultValue <> 'CURRENT_TIMESTAMP' then
1244  DefaultValue := '''' + DefaultValue + ''''
1245  end
1246  else if (MySQLType in [stDate, stTime, stTimestamp]) then
1247  begin
1248  if DefaultValue <> 'CURRENT_TIMESTAMP' then
1249  DefaultValue := '''' + DefaultValue + ''''
1250  end
1251  else if (MySQLType = stBoolean) and (TypeName = 'enum') then
1252  begin
1253  if (DefaultValue = 'y') or (DefaultValue = 'Y') then
1254  DefaultValue := '1'
1255  else
1256  DefaultValue := '0';
1257  end;
1258  end;
1259  Res.UpdateString(13, DefaultValue);
1260  Res.UpdateNull(14);
1261  Res.UpdateNull(15);
1262  Res.UpdateInt(17, OrdPosition);
1263 
1264  Res.UpdateBoolean(19, //AUTO_INCREMENT
1265  Trim(LowerCase(GetString(ColumnIndexes[4]))) = 'auto_increment'); //Extra
1266  Res.UpdateBoolean(20, //CASE_SENSITIVE
1267  IC.IsCaseSensitive(GetString(ColumnIndexes[1]))); //Field
1268  Res.UpdateBoolean(21, True); //SEARCHABLE
1269  Res.UpdateBoolean(22, True); //WRITABLE
1270  Res.UpdateBoolean(23, True); //DEFINITELYWRITABLE
1271  Res.UpdateBoolean(24, False); //READONLY
1272 
1273  Inc(OrdPosition);
1274  Res.InsertRow;
1275  end;
1276  Close;
1277  end;
1278  end;
1279  finally
1280  TableNameList.Free;
1281  end;
1282  Result := Res;
1283 end;
1284 
1285 {**
1286  Gets a description of the access rights for a table's columns.
1287 
1288  <P>Only privileges matching the column name criteria are
1289  returned. They are ordered by COLUMN_NAME and PRIVILEGE.
1290 
1291  <P>Each privilige description has the following columns:
1292  <OL>
1293  <LI><B>TABLE_CAT</B> String => table catalog (may be null)
1294  <LI><B>TABLE_SCHEM</B> String => table schema (may be null)
1295  <LI><B>TABLE_NAME</B> String => table name
1296  <LI><B>COLUMN_NAME</B> String => column name
1297  <LI><B>GRANTOR</B> => grantor of access (may be null)
1298  <LI><B>GRANTEE</B> String => grantee of access
1299  <LI><B>PRIVILEGE</B> String => name of access (SELECT,
1300  INSERT, UPDATE, REFRENCES, ...)
1301  <LI><B>IS_GRANTABLE</B> String => "YES" if grantee is permitted
1302  to grant to others; "NO" if not; null if unknown
1303  </OL>
1304 
1305  @param catalog a catalog name; "" retrieves those without a
1306  catalog; null means drop catalog name from the selection criteria
1307  @param schema a schema name; "" retrieves those without a schema
1308  @param table a table name
1309  @param columnNamePattern a column name pattern
1310  @return <code>ResultSet</code> - each row is a column privilege description
1311  @see #getSearchStringEscape
1312 }
1313 function TZMySQLDatabaseMetadata.UncachedGetColumnPrivileges(const Catalog: string;
1314  const Schema: string; const Table: string; const ColumnNamePattern: string): IZResultSet;
1315 var
1316  I: Integer;
1317  Host, Database, Grantor, User, FullUser: String;
1318  AllPrivileges, ColumnName, Privilege: String;
1319  PrivilegesList: TStrings;
1320  ColumnNameCondition, TableNameCondition, SchemaCondition: string;
1321 begin
1322  Result:=inherited UncachedGetColumnPrivileges(Catalog, Schema, Table, ColumnNamePattern);
1323 
1324  If Catalog = '' then
1325  If Schema <> '' then
1326  SchemaCondition := ConstructNameCondition(Schema,'c.db')
1327  else
1328  SchemaCondition := ConstructNameCondition(FDatabase,'c.db')
1329  else
1330  SchemaCondition := ConstructNameCondition(Catalog,'c.db');
1331  TableNameCondition := ConstructNameCondition(Table,'c.table_name');
1332  ColumnNameCondition := ConstructNameCondition(ColumnNamePattern,'c.column_name');
1333  If SchemaCondition <> '' then
1334  SchemaCondition := ' and ' + SchemaCondition;
1335  If TableNameCondition <> '' then
1336  TableNameCondition := ' and ' + TableNameCondition;
1337  If ColumnNameCondition <> '' then
1338  ColumnNameCondition := ' and ' + ColumnNameCondition;
1339 
1340  PrivilegesList := TStringList.Create;
1341  try
1342  with GetConnection.CreateStatement.ExecuteQuery(
1343  'SELECT c.host, c.db, t.grantor, c.user, c.table_name,'
1344  + ' c.column_name, c.column_priv FROM mysql.columns_priv c,'
1345  + ' mysql.tables_priv t WHERE c.host=t.host AND c.db=t.db'
1346  + ' AND c.table_name=t.table_name'
1347  + SchemaCondition + TableNameCondition + ColumnNameCondition
1348  ) do
1349  begin
1350  while Next do
1351  begin
1352  Host := GetString(1);
1353  Database := GetString(2);
1354  Grantor := GetString(4);
1355  User := GetString(5);
1356  if User = '' then
1357  User := '%';
1358  if Host <> '' then
1359  FullUser := User + '@' + Host;
1360  ColumnName := GetString(6);
1361 
1362  AllPrivileges := GetString(7);
1363  PutSplitString(PrivilegesList, AllPrivileges, ',');
1364 
1365  for I := 0 to PrivilegesList.Count - 1 do
1366  begin
1367  Result.MoveToInsertRow;
1368  Privilege := Trim(PrivilegesList.Strings[I]);
1369  Result.UpdateString(1, Database);
1370  Result.UpdateNull(2);
1371  Result.UpdateString(3, Table);
1372  Result.UpdateString(4, ColumnName);
1373  Result.UpdateString(5, Grantor);
1374  Result.UpdateString(6, FullUser);
1375  Result.UpdateString(7, Privilege);
1376  Result.UpdateNull(8);
1377  Result.InsertRow;
1378  end;
1379  end;
1380  Close;
1381  end;
1382  finally
1383  PrivilegesList.Free;
1384  end;
1385 end;
1386 
1387 {**
1388  Gets a description of the access rights for each table available
1389  in a catalog. Note that a table privilege applies to one or
1390  more columns in the table. It would be wrong to assume that
1391  this priviledge applies to all columns (this may be true for
1392  some systems but is not true for all.)
1393 
1394  <P>Only privileges matching the schema and table name
1395  criteria are returned. They are ordered by TABLE_SCHEM,
1396  TABLE_NAME, and PRIVILEGE.
1397 
1398  <P>Each privilige description has the following columns:
1399  <OL>
1400  <LI><B>TABLE_CAT</B> String => table catalog (may be null)
1401  <LI><B>TABLE_SCHEM</B> String => table schema (may be null)
1402  <LI><B>TABLE_NAME</B> String => table name
1403  <LI><B>GRANTOR</B> => grantor of access (may be null)
1404  <LI><B>GRANTEE</B> String => grantee of access
1405  <LI><B>PRIVILEGE</B> String => name of access (SELECT,
1406  INSERT, UPDATE, REFRENCES, ...)
1407  <LI><B>IS_GRANTABLE</B> String => "YES" if grantee is permitted
1408  to grant to others; "NO" if not; null if unknown
1409  </OL>
1410 
1411  @param catalog a catalog name; "" retrieves those without a
1412  catalog; null means drop catalog name from the selection criteria
1413  @param schemaPattern a schema name pattern; "" retrieves those
1414  without a schema
1415  @param tableNamePattern a table name pattern
1416  @return <code>ResultSet</code> - each row is a table privilege description
1417  @see #getSearchStringEscape
1418 }
1419 function TZMySQLDatabaseMetadata.UncachedGetTablePrivileges(const Catalog: string;
1420  const SchemaPattern: string; const TableNamePattern: string): IZResultSet;
1421 var
1422  I: Integer;
1423  Host, Database, Table, Grantor, User, FullUser: String;
1424  AllPrivileges, Privilege: String;
1425  PrivilegesList: TStrings;
1426  TableNameCondition, SchemaCondition: string;
1427 begin
1428  Result:=inherited UncachedGetTablePrivileges(Catalog, SchemaPattern, TableNamePattern);
1429 
1430  If Catalog = '' then
1431  If SchemaPattern <> '' then
1432  SchemaCondition := ConstructNameCondition(SchemaPattern,'db')
1433  else
1434  SchemaCondition := ConstructNameCondition(FDatabase,'db')
1435  else
1436  SchemaCondition := ConstructNameCondition(Catalog,'db');
1437  TableNameCondition := ConstructNameCondition(TableNamePattern,'table_name');
1438  If SchemaCondition <> '' then
1439  SchemaCondition := ' and ' + SchemaCondition;
1440  If TableNameCondition <> '' then
1441  TableNameCondition := ' and ' + TableNameCondition;
1442 
1443  PrivilegesList := TStringList.Create;
1444  try
1445  with GetConnection.CreateStatement.ExecuteQuery(
1446  'SELECT host,db,table_name,grantor,user,table_priv'
1447  + ' from mysql.tables_priv WHERE 1=1'
1448  + SchemaCondition + TableNameCondition
1449  ) do
1450  begin
1451  while Next do
1452  begin
1453  Host := GetString(1);
1454  Database := GetString(2);
1455  Table := GetString(3);
1456  Grantor := GetString(4);
1457  User := GetString(5);
1458  if User = '' then
1459  User := '%';
1460  if Host <> '' then
1461  FullUser := User + '@' + Host;
1462 
1463  AllPrivileges := GetString(6);
1464  PutSplitString(PrivilegesList, AllPrivileges, ',');
1465 
1466  for I := 0 to PrivilegesList.Count - 1 do
1467  begin
1468  Result.MoveToInsertRow;
1469  Privilege := Trim(PrivilegesList.Strings[I]);
1470  Result.UpdateString(1, Database);
1471  Result.UpdateNull(2);
1472  Result.UpdateString(3, Table);
1473  Result.UpdateString(4, Grantor);
1474  Result.UpdateString(5, FullUser);
1475  Result.UpdateString(6, Privilege);
1476  Result.UpdateNull(7);
1477  Result.InsertRow;
1478  end;
1479  end;
1480  Close;
1481  end;
1482  finally
1483  PrivilegesList.Free;
1484  end;
1485 end;
1486 
1487 {**
1488  Gets a description of a table's primary key columns. They
1489  are ordered by COLUMN_NAME.
1490 
1491  <P>Each primary key column description has the following columns:
1492  <OL>
1493  <LI><B>TABLE_CAT</B> String => table catalog (may be null)
1494  <LI><B>TABLE_SCHEM</B> String => table schema (may be null)
1495  <LI><B>TABLE_NAME</B> String => table name
1496  <LI><B>COLUMN_NAME</B> String => column name
1497  <LI><B>KEY_SEQ</B> short => sequence number within primary key
1498  <LI><B>PK_NAME</B> String => primary key name (may be null)
1499  </OL>
1500 
1501  @param catalog a catalog name; "" retrieves those without a
1502  catalog; null means drop catalog name from the selection criteria
1503  @param schema a schema name; "" retrieves those
1504  without a schema
1505  @param table a table name
1506  @return <code>ResultSet</code> - each row is a primary key column description
1507  @exception SQLException if a database access error occurs
1508 }
1509 function TZMySQLDatabaseMetadata.UncachedGetPrimaryKeys(const Catalog: string;
1510  const Schema: string; const Table: string): IZResultSet;
1511 var
1512  KeyType: string;
1513  LCatalog, LTable: string;
1514  ColumnIndexes : Array[1..3] of integer;
1515 begin
1516  if Table = '' then
1517  raise Exception.Create(STableIsNotSpecified); //CHANGE IT!
1518 
1519  Result:=inherited UncachedGetPrimaryKeys(Catalog, Schema, Table);
1520 
1521  GetCatalogAndNamePattern(Catalog, Schema, Table,
1522  LCatalog, LTable);
1523 
1524  with GetConnection.CreateStatement.ExecuteQuery(
1525  Format('SHOW KEYS FROM %s.%s',
1526  [IC.Quote(LCatalog),
1527  IC.Quote(LTable)])) do
1528  begin
1529  ColumnIndexes[1] := FindColumn('Key_name');
1530  ColumnIndexes[2] := FindColumn('Column_name');
1531  ColumnIndexes[3] := FindColumn('Seq_in_index');
1532  while Next do
1533  begin
1534  KeyType := UpperCase(String(GetString(ColumnIndexes[1])));
1535  KeyType := Copy(KeyType, 1, 3);
1536  if KeyType = 'PRI' then
1537  begin
1538  Result.MoveToInsertRow;
1539  Result.UpdateString(1, LCatalog);
1540  Result.UpdateString(2, '');
1541  Result.UpdateString(3, Table);
1542  Result.UpdateString(4, GetString(ColumnIndexes[2]));
1543  Result.UpdateString(5, GetString(ColumnIndexes[3]));
1544  Result.UpdateNull(6);
1545  Result.InsertRow;
1546  end;
1547  end;
1548  Close;
1549  end;
1550 end;
1551 
1552 {**
1553  Gets a description of the primary key columns that are
1554  referenced by a table's foreign key columns (the primary keys
1555  imported by a table). They are ordered by PKTABLE_CAT,
1556  PKTABLE_SCHEM, PKTABLE_NAME, and KEY_SEQ.
1557 
1558  <P>Each primary key column description has the following columns:
1559  <OL>
1560  <LI><B>PKTABLE_CAT</B> String => primary key table catalog
1561  being imported (may be null)
1562  <LI><B>PKTABLE_SCHEM</B> String => primary key table schema
1563  being imported (may be null)
1564  <LI><B>PKTABLE_NAME</B> String => primary key table name
1565  being imported
1566  <LI><B>PKCOLUMN_NAME</B> String => primary key column name
1567  being imported
1568  <LI><B>FKTABLE_CAT</B> String => foreign key table catalog (may be null)
1569  <LI><B>FKTABLE_SCHEM</B> String => foreign key table schema (may be null)
1570  <LI><B>FKTABLE_NAME</B> String => foreign key table name
1571  <LI><B>FKCOLUMN_NAME</B> String => foreign key column name
1572  <LI><B>KEY_SEQ</B> short => sequence number within foreign key
1573  <LI><B>UPDATE_RULE</B> short => What happens to
1574  foreign key when primary is updated:
1575  <UL>
1576  <LI> importedNoAction - do not allow update of primary
1577  key if it has been imported
1578  <LI> importedKeyCascade - change imported key to agree
1579  with primary key update
1580  <LI> importedKeySetNull - change imported key to NULL if
1581  its primary key has been updated
1582  <LI> importedKeySetDefault - change imported key to default values
1583  if its primary key has been updated
1584  <LI> importedKeyRestrict - same as importedKeyNoAction
1585  (for ODBC 2.x compatibility)
1586  </UL>
1587  <LI><B>DELETE_RULE</B> short => What happens to
1588  the foreign key when primary is deleted.
1589  <UL>
1590  <LI> importedKeyNoAction - do not allow delete of primary
1591  key if it has been imported
1592  <LI> importedKeyCascade - delete rows that import a deleted key
1593  <LI> importedKeySetNull - change imported key to NULL if
1594  its primary key has been deleted
1595  <LI> importedKeyRestrict - same as importedKeyNoAction
1596  (for ODBC 2.x compatibility)
1597  <LI> importedKeySetDefault - change imported key to default if
1598  its primary key has been deleted
1599  </UL>
1600  <LI><B>FK_NAME</B> String => foreign key name (may be null)
1601  <LI><B>PK_NAME</B> String => primary key name (may be null)
1602  <LI><B>DEFERRABILITY</B> short => can the evaluation of foreign key
1603  constraints be deferred until commit
1604  <UL>
1605  <LI> importedKeyInitiallyDeferred - see SQL92 for definition
1606  <LI> importedKeyInitiallyImmediate - see SQL92 for definition
1607  <LI> importedKeyNotDeferrable - see SQL92 for definition
1608  </UL>
1609  </OL>
1610 
1611  @param catalog a catalog name; "" retrieves those without a
1612  catalog; null means drop catalog name from the selection criteria
1613  @param schema a schema name; "" retrieves those
1614  without a schema
1615  @param table a table name
1616  @return <code>ResultSet</code> - each row is a primary key column description
1617  @see #getExportedKeys
1618 }
1619 function TZMySQLDatabaseMetadata.UncachedGetImportedKeys(const Catalog: string;
1620  const Schema: string; const Table: string): IZResultSet;
1621 var
1622  I: Integer;
1623  KeySeq: Integer;
1624  LCatalog, LTable: string;
1625  TableType, Comment, Keys: String;
1626  CommentList, KeyList: TStrings;
1627  ColumnIndexes : Array[1..2] of integer;
1628 begin
1629  if Table = '' then
1630  raise Exception.Create(STableIsNotSpecified); //CHANGE IT!
1631 
1632  Result := inherited UncachedGetImportedKeys(Catalog, Schema, Table);
1633 
1634  GetCatalogAndNamePattern(Catalog, Schema, Table,
1635  LCatalog, LTable);
1636 
1637  KeyList := TStringList.Create;
1638  CommentList := TStringList.Create;
1639  try
1640  with GetConnection.CreateStatement.ExecuteQuery(
1641  Format('SHOW TABLE STATUS FROM %s LIKE ''%s''',
1642  [IC.Quote(LCatalog), LTable])) do
1643  begin
1644  ColumnIndexes[1] := FindColumn('Type');
1645  ColumnIndexes[2] := FindColumn('Comment');
1646  while Next do
1647  begin
1648  TableType := GetString(ColumnIndexes[1]);
1649  if (TableType <> '') and (LowerCase(TableType) = 'innodb') then
1650  begin
1651  Comment := GetString(ColumnIndexes[2]);
1652  if Comment <> '' then
1653  begin
1654  PutSplitString(CommentList, Comment, ';');
1655  KeySeq := 0;
1656 
1657  if CommentList.Count > 4 then
1658  begin
1659  for I := 0 to CommentList.Count - 1 do
1660  begin
1661  Keys := CommentList.Strings[1];
1662  Result.MoveToInsertRow;
1663  PutSplitString(KeyList, Keys, '() /');
1664 
1665  Result.UpdateString(1, KeyList.Strings[2]); // PKTABLE_CAT
1666  Result.UpdateNull(2); // PKTABLE_SCHEM
1667  Result.UpdateString(3, KeyList.Strings[3]); // PKTABLE_NAME
1668  Result.UpdateString(4, KeyList.Strings[4]); // PKCOLUMN_NAME
1669  Result.UpdateString(5, LCatalog);
1670  Result.UpdateNull(6);// FKTABLE_SCHEM
1671  Result.UpdateString(7, Table); // FKTABLE_NAME
1672  Result.UpdateString(8, KeyList.Strings[0]); // FKCOLUMN_NAME
1673 
1674  Result.UpdateInt(9, KeySeq); // KEY_SEQ
1675  Result.UpdateInt(10, Ord(ikSetDefault)); // UPDATE_RULE
1676  Result.UpdateInt(11, Ord(ikSetDefault)); // DELETE_RULE
1677  Result.UpdateNull(12); // FK_NAME
1678  Result.UpdateNull(13); // PK_NAME
1679  Result.UpdateInt(14, Ord(ikSetDefault)); // DEFERRABILITY
1680  Inc(KeySeq);
1681  Result.InsertRow;
1682  end;
1683  end;
1684  end;
1685  end;
1686  end;
1687  Close;
1688  end;
1689  finally
1690  KeyList.Free;
1691  CommentList.Free;
1692  end;
1693 end;
1694 
1695 {**
1696  Gets a description of the foreign key columns that reference a
1697  table's primary key columns (the foreign keys exported by a
1698  table). They are ordered by FKTABLE_CAT, FKTABLE_SCHEM,
1699  FKTABLE_NAME, and KEY_SEQ.
1700 
1701  <P>Each foreign key column description has the following columns:
1702  <OL>
1703  <LI><B>PKTABLE_CAT</B> String => primary key table catalog (may be null)
1704  <LI><B>PKTABLE_SCHEM</B> String => primary key table schema (may be null)
1705  <LI><B>PKTABLE_NAME</B> String => primary key table name
1706  <LI><B>PKCOLUMN_NAME</B> String => primary key column name
1707  <LI><B>FKTABLE_CAT</B> String => foreign key table catalog (may be null)
1708  being exported (may be null)
1709  <LI><B>FKTABLE_SCHEM</B> String => foreign key table schema (may be null)
1710  being exported (may be null)
1711  <LI><B>FKTABLE_NAME</B> String => foreign key table name
1712  being exported
1713  <LI><B>FKCOLUMN_NAME</B> String => foreign key column name
1714  being exported
1715  <LI><B>KEY_SEQ</B> short => sequence number within foreign key
1716  <LI><B>UPDATE_RULE</B> short => What happens to
1717  foreign key when primary is updated:
1718  <UL>
1719  <LI> importedNoAction - do not allow update of primary
1720  key if it has been imported
1721  <LI> importedKeyCascade - change imported key to agree
1722  with primary key update
1723  <LI> importedKeySetNull - change imported key to NULL if
1724  its primary key has been updated
1725  <LI> importedKeySetDefault - change imported key to default values
1726  if its primary key has been updated
1727  <LI> importedKeyRestrict - same as importedKeyNoAction
1728  (for ODBC 2.x compatibility)
1729  </UL>
1730  <LI><B>DELETE_RULE</B> short => What happens to
1731  the foreign key when primary is deleted.
1732  <UL>
1733  <LI> importedKeyNoAction - do not allow delete of primary
1734  key if it has been imported
1735  <LI> importedKeyCascade - delete rows that import a deleted key
1736  <LI> importedKeySetNull - change imported key to NULL if
1737  its primary key has been deleted
1738  <LI> importedKeyRestrict - same as importedKeyNoAction
1739  (for ODBC 2.x compatibility)
1740  <LI> importedKeySetDefault - change imported key to default if
1741  its primary key has been deleted
1742  </UL>
1743  <LI><B>FK_NAME</B> String => foreign key name (may be null)
1744  <LI><B>PK_NAME</B> String => primary key name (may be null)
1745  <LI><B>DEFERRABILITY</B> short => can the evaluation of foreign key
1746  constraints be deferred until commit
1747  <UL>
1748  <LI> importedKeyInitiallyDeferred - see SQL92 for definition
1749  <LI> importedKeyInitiallyImmediate - see SQL92 for definition
1750  <LI> importedKeyNotDeferrable - see SQL92 for definition
1751  </UL>
1752  </OL>
1753 
1754  @param catalog a catalog name; "" retrieves those without a
1755  catalog; null means drop catalog name from the selection criteria
1756  @param schema a schema name; "" retrieves those
1757  without a schema
1758  @param table a table name
1759  @return <code>ResultSet</code> - each row is a foreign key column description
1760  @see #getImportedKeys
1761 }
1762 function TZMySQLDatabaseMetadata.UncachedGetExportedKeys(const Catalog: string;
1763  const Schema: string; const Table: string): IZResultSet;
1764 var
1765  I: Integer;
1766  KeySeq: Integer;
1767  LCatalog, LTable: string;
1768  TableType, Comment, Keys: String;
1769  CommentList, KeyList: TStrings;
1770  ColumnIndexes : Array[1..3] of integer;
1771 begin
1772  if Table = '' then
1773  raise Exception.Create(STableIsNotSpecified); //CHANGE IT!
1774 
1775  Result:=inherited UncachedGetExportedKeys(Catalog, Schema, Table);
1776 
1777  GetCatalogAndNamePattern(Catalog, Schema, Table,
1778  LCatalog, LTable);
1779 
1780  KeyList := TStringList.Create;
1781  CommentList := TStringList.Create;
1782  try
1783  with GetConnection.CreateStatement.ExecuteQuery(
1784  Format('SHOW TABLE STATUS FROM %s',
1785  [IC.Quote(LCatalog)])) do
1786  begin
1787  ColumnIndexes[1] := FindColumn('Type');
1788  ColumnIndexes[2] := FindColumn('Comment');
1789  ColumnIndexes[3] := FindColumn('Name');
1790  while Next do
1791  begin
1792  TableType := GetString(ColumnIndexes[1]);
1793  if (TableType <> '') and (LowerCase(TableType) = 'innodb') then
1794  begin
1795  Comment := GetString(ColumnIndexes[2]);
1796  if Comment <> '' then
1797  begin
1798  PutSplitString(CommentList, Comment, ';');
1799  KeySeq := 0;
1800  if CommentList.Count > 4 then
1801  begin
1802  for I := 0 to CommentList.Count-1 do
1803  begin
1804  Keys := CommentList.Strings[1];
1805  Result.MoveToInsertRow;
1806  PutSplitString(KeyList, Keys, '() /');
1807 
1808  Result.UpdateString(5, LCatalog);
1809  Result.UpdateNull(6);// FKTABLE_SCHEM
1810  Result.UpdateString(7, GetString(ColumnIndexes[3])); // FKTABLE_NAME
1811  Result.UpdateString(8, KeyList.Strings[0]); // PKTABLE_CAT
1812 
1813  Result.UpdateString(1, KeyList.Strings[2]); // PKTABLE_CAT
1814  Result.UpdateNull(2); // PKTABLE_SCHEM
1815  Result.UpdateString(3, Table); // PKTABLE_NAME
1816  Result.UpdateInt(9, KeySeq); // KEY_SEQ
1817 
1818  Result.UpdateInt(10, Ord(ikSetDefault)); // UPDATE_RULE
1819  Result.UpdateInt(11, Ord(ikSetDefault)); // DELETE_RULE
1820  Result.UpdateNull(12); // FK_NAME
1821  Result.UpdateNull(13); // PK_NAME
1822  Result.UpdateInt(14, Ord(ikSetDefault)); // DEFERRABILITY
1823  Inc(KeySeq);
1824  Result.InsertRow;
1825  end;
1826  end;
1827  end;
1828  end;
1829  end;
1830  Close;
1831  end;
1832  finally
1833  KeyList.Free;
1834  CommentList.Free;
1835  end;
1836 end;
1837 
1838 {**
1839  Gets a description of the foreign key columns in the foreign key
1840  table that reference the primary key columns of the primary key
1841  table (describe how one table imports another's key.) This
1842  should normally return a single foreign key/primary key pair
1843  (most tables only import a foreign key from a table once.) They
1844  are ordered by FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, and
1845  KEY_SEQ.
1846 
1847  <P>Each foreign key column description has the following columns:
1848  <OL>
1849  <LI><B>PKTABLE_CAT</B> String => primary key table catalog (may be null)
1850  <LI><B>PKTABLE_SCHEM</B> String => primary key table schema (may be null)
1851  <LI><B>PKTABLE_NAME</B> String => primary key table name
1852  <LI><B>PKCOLUMN_NAME</B> String => primary key column name
1853  <LI><B>FKTABLE_CAT</B> String => foreign key table catalog (may be null)
1854  being exported (may be null)
1855  <LI><B>FKTABLE_SCHEM</B> String => foreign key table schema (may be null)
1856  being exported (may be null)
1857  <LI><B>FKTABLE_NAME</B> String => foreign key table name
1858  being exported
1859  <LI><B>FKCOLUMN_NAME</B> String => foreign key column name
1860  being exported
1861  <LI><B>KEY_SEQ</B> short => sequence number within foreign key
1862  <LI><B>UPDATE_RULE</B> short => What happens to
1863  foreign key when primary is updated:
1864  <UL>
1865  <LI> importedNoAction - do not allow update of primary
1866  key if it has been imported
1867  <LI> importedKeyCascade - change imported key to agree
1868  with primary key update
1869  <LI> importedKeySetNull - change imported key to NULL if
1870  its primary key has been updated
1871  <LI> importedKeySetDefault - change imported key to default values
1872  if its primary key has been updated
1873  <LI> importedKeyRestrict - same as importedKeyNoAction
1874  (for ODBC 2.x compatibility)
1875  </UL>
1876  <LI><B>DELETE_RULE</B> short => What happens to
1877  the foreign key when primary is deleted.
1878  <UL>
1879  <LI> importedKeyNoAction - do not allow delete of primary
1880  key if it has been imported
1881  <LI> importedKeyCascade - delete rows that import a deleted key
1882  <LI> importedKeySetNull - change imported key to NULL if
1883  its primary key has been deleted
1884  <LI> importedKeyRestrict - same as importedKeyNoAction
1885  (for ODBC 2.x compatibility)
1886  <LI> importedKeySetDefault - change imported key to default if
1887  its primary key has been deleted
1888  </UL>
1889  <LI><B>FK_NAME</B> String => foreign key name (may be null)
1890  <LI><B>PK_NAME</B> String => primary key name (may be null)
1891  <LI><B>DEFERRABILITY</B> short => can the evaluation of foreign key
1892  constraints be deferred until commit
1893  <UL>
1894  <LI> importedKeyInitiallyDeferred - see SQL92 for definition
1895  <LI> importedKeyInitiallyImmediate - see SQL92 for definition
1896  <LI> importedKeyNotDeferrable - see SQL92 for definition
1897  </UL>
1898  </OL>
1899 
1900  @param primaryCatalog a catalog name; "" retrieves those without a
1901  catalog; null means drop catalog name from the selection criteria
1902  @param primarySchema a schema name; "" retrieves those
1903  without a schema
1904  @param primaryTable the table name that exports the key
1905  @param foreignCatalog a catalog name; "" retrieves those without a
1906  catalog; null means drop catalog name from the selection criteria
1907  @param foreignSchema a schema name; "" retrieves those
1908  without a schema
1909  @param foreignTable the table name that imports the key
1910  @return <code>ResultSet</code> - each row is a foreign key column description
1911  @see #getImportedKeys
1912 }
1913 function TZMySQLDatabaseMetadata.UncachedGetCrossReference(const PrimaryCatalog: string;
1914  const PrimarySchema: string; const PrimaryTable: string; const ForeignCatalog: string;
1915  const ForeignSchema: string; const ForeignTable: string): IZResultSet;
1916 var
1917  I: Integer;
1918  KeySeq: Integer;
1919  LForeignCatalog: string;
1920  TableType, Comment, Keys: string;
1921  CommentList, KeyList: TStrings;
1922  ColumnIndexes : Array[1..3] of integer;
1923 begin
1924  if PrimaryTable = '' then
1925  raise Exception.Create(STableIsNotSpecified); //CHANGE IT!
1926 
1927  Result:=inherited UncachedGetCrossReference(PrimaryCatalog, PrimarySchema, PrimaryTable,
1928  ForeignCatalog, ForeignSchema, ForeignTable);
1929 
1930  if ForeignCatalog = '' then
1931  LForeignCatalog := FDatabase
1932  else
1933  LForeignCatalog := ForeignCatalog;
1934 
1935  KeyList := TStringList.Create;
1936  CommentList := TStringList.Create;
1937  try
1938  with GetConnection.CreateStatement.ExecuteQuery(
1939  Format('SHOW TABLE STATUS FROM %s',
1940  [IC.Quote(LForeignCatalog)])) do
1941  begin
1942  ColumnIndexes[1] := FindColumn('Type');
1943  ColumnIndexes[2] := FindColumn('Comment');
1944  ColumnIndexes[3] := FindColumn('Name');
1945  while Next do
1946  begin
1947  TableType := GetString(ColumnIndexes[1]);
1948  if (TableType <> '') and (LowerCase(TableType) = 'innodb') then
1949  begin
1950  Comment := GetString(ColumnIndexes[2]);
1951  if Comment = '' then
1952  begin
1953  PutSplitString(CommentList, Comment, ';');
1954  KeySeq := 0;
1955  if CommentList.Count > 4 then
1956  begin
1957  for I := 0 to CommentList.Count-1 do
1958  begin
1959  Keys := CommentList.Strings[1];
1960  Result.MoveToInsertRow;
1961  PutSplitString(KeyList, Keys, '() /');
1962 
1963  Result.UpdateString(5, LForeignCatalog);
1964  if ForeignSchema = '' then
1965  Result.UpdateNull(6) // FKTABLE_SCHEM
1966  else
1967  Result.UpdateString(6, ForeignSchema);
1968  if ForeignTable <> GetString(ColumnIndexes[3]) then
1969  Continue
1970  else
1971  Result.UpdateString(7, GetString(ColumnIndexes[3])); // FKTABLE_NAME
1972 
1973  Result.UpdateString(8, KeyList.Strings[0]); // PKTABLE_CAT
1974 
1975  Result.UpdateString(1, KeyList.Strings[2]); // PKTABLE_CAT
1976  if PrimarySchema = '' then
1977  Result.UpdateNull(2) // PKTABLE_SCHEM
1978  else
1979  Result.UpdateString(2, PrimarySchema); // PKTABLE_SCHEM
1980 
1981  if PrimaryTable = KeyList.Strings[3] then
1982  Continue;
1983 
1984  Result.UpdateString(3, PrimaryTable); // PKTABLE_NAME
1985  Result.UpdateString(4, KeyList.Strings[4]); // PKCOLUMN_NAME
1986  Result.UpdateInt(9, KeySeq); // KEY_SEQ
1987  Result.UpdateInt(10, Ord(ikSetDefault)); // UPDATE_RULE
1988  Result.UpdateInt(11, Ord(ikSetDefault)); // DELETE_RULE
1989  Result.UpdateNull(12); // FK_NAME
1990  Result.UpdateNull(13); // PK_NAME
1991  Result.UpdateInt(14, Ord(ikSetDefault)); // DEFERRABILITY
1992  Inc(KeySeq);
1993  Result.InsertRow;
1994  end;
1995  end;
1996  end;
1997  end;
1998  end;
1999  Close;
2000  end;
2001  finally
2002  KeyList.Free;
2003  CommentList.Free;
2004  end;
2005 end;
2006 
2007 {**
2008  Gets a description of all the standard SQL types supported by
2009  this database. They are ordered by DATA_TYPE and then by how
2010  closely the data type maps to the corresponding JDBC SQL type.
2011 
2012  <P>Each type description has the following columns:
2013  <OL>
2014  <LI><B>TYPE_NAME</B> String => Type name
2015  <LI><B>DATA_TYPE</B> short => SQL data type from java.sql.Types
2016  <LI><B>PRECISION</B> int => maximum precision
2017  <LI><B>LITERAL_PREFIX</B> String => prefix used to quote a literal
2018  (may be null)
2019  <LI><B>LITERAL_SUFFIX</B> String => suffix used to quote a literal
2020  (may be null)
2021  <LI><B>CREATE_PARAMS</B> String => parameters used in creating
2022  the type (may be null)
2023  <LI><B>NULLABLE</B> short => can you use NULL for this type?
2024  <UL>
2025  <LI> typeNoNulls - does not allow NULL values
2026  <LI> typeNullable - allows NULL values
2027  <LI> typeNullableUnknown - nullability unknown
2028  </UL>
2029  <LI><B>CASE_SENSITIVE</B> boolean=> is it case sensitive?
2030  <LI><B>SEARCHABLE</B> short => can you use "WHERE" based on this type:
2031  <UL>
2032  <LI> typePredNone - No support
2033  <LI> typePredChar - Only supported with WHERE .. LIKE
2034  <LI> typePredBasic - Supported except for WHERE .. LIKE
2035  <LI> typeSearchable - Supported for all WHERE ..
2036  </UL>
2037  <LI><B>UNSIGNED_ATTRIBUTE</B> boolean => is it unsigned?
2038  <LI><B>FIXED_PREC_SCALE</B> boolean => can it be a money value?
2039  <LI><B>AUTO_INCREMENT</B> boolean => can it be used for an
2040  auto-increment value?
2041  <LI><B>LOCAL_TYPE_NAME</B> String => localized version of type name
2042  (may be null)
2043  <LI><B>MINIMUM_SCALE</B> short => minimum scale supported
2044  <LI><B>MAXIMUM_SCALE</B> short => maximum scale supported
2045  <LI><B>SQL_DATA_TYPE</B> int => unused
2046  <LI><B>SQL_DATETIME_SUB</B> int => unused
2047  <LI><B>NUM_PREC_RADIX</B> int => usually 2 or 10
2048  </OL>
2049 
2050  @return <code>ResultSet</code> - each row is an SQL type description
2051 }
2052 function TZMySQLDatabaseMetadata.UncachedGetTypeInfo: IZResultSet;
2053 const
2054  MaxTypeCount = 33;
2055  TypeNames: array[1..MaxTypeCount] of string = (
2056  'BIT', 'BOOL', 'TINYINT', 'BIGINT', 'MEDIUMBLOB', 'LONG VARBINARY',
2057  'LONGBLOB', 'BLOB', 'TINYBLOB', 'VARBINARY', 'BINARY',
2058  'LONG VARCHAR', 'MEDIUMTEXT', 'LONGTEXT', 'TEXT', 'TINYTEXT',
2059  'CHAR', 'VARCHAR', 'NUMERIC', 'DECIMAL', 'INTEGER', 'INT',
2060  'MEDIUMINT', 'SMALLINT', 'DOUBLE', 'FLOAT', 'REAL', 'ENUM', 'SET',
2061  'DATE', 'TIME', 'DATETIME', 'TIMESTAMP');
2062  TypeCodes: array[1..MaxTypeCount] of TZSQLType = (
2063  stByte, stBoolean, stShort, stLong, stBinaryStream, stBinaryStream,
2064  stBinaryStream, stBinaryStream, stBinaryStream, stBytes, stBytes,
2065  stString, stAsciiStream, stAsciiStream, stAsciiStream, stAsciiStream,
2066  stString, stString, stBigDecimal, stBigDecimal, stInteger, stInteger,
2067  stInteger, stShort, stDouble, stFloat, stFloat, stString, stString,
2068  stDate, stTime, stTimestamp, stTimestamp);
2069  TypePrecision: array[1..MaxTypeCount] of Integer = (
2070  1, -1, 4, 16, 16777215, 16777215, MAXBUF, 65535, 255, 255, 255,
2071  16777215, 16777215, 2147483647, 65535, 255, 255, 255, 17, 17, 10, 10,
2072  7, 4, 17, 10, 10, 65535, 64, -1, -1, -1, -1);
2073 var
2074  I: Integer;
2075 begin
2076  Result:=inherited UncachedGetTypeInfo;
2077 
2078  for I := 1 to MaxTypeCount do
2079  begin
2080  Result.MoveToInsertRow;
2081 
2082  Result.UpdateString(1, TypeNames[I]);
2083  Result.UpdateInt(2, Ord(TypeCodes[I]));
2084  if TypePrecision[I] >= 0 then
2085  Result.UpdateInt(3, TypePrecision[I])
2086  else
2087  Result.UpdateNull(3);
2088  if TypeCodes[I] in [stString, stBytes, stDate, stTime,
2089  stTimeStamp, stBinaryStream, stAsciiStream] then
2090  begin
2091  Result.UpdateString(4, '''');
2092  Result.UpdateString(5, '''');
2093  end
2094  else
2095  begin
2096  Result.UpdateNull(4);
2097  Result.UpdateNull(5);
2098  end;
2099  Result.UpdateNull(6);
2100  Result.UpdateInt(7, Ord(ntNullable));
2101  Result.UpdateBoolean(8, False);
2102  Result.UpdateBoolean(9, False);
2103  Result.UpdateBoolean(11, False);
2104  Result.UpdateBoolean(12, False);
2105  Result.UpdateBoolean(12, TypeNames[I] = 'INTEGER');
2106  Result.UpdateNull(13);
2107  Result.UpdateNull(14);
2108  Result.UpdateNull(15);
2109  Result.UpdateNull(16);
2110  Result.UpdateNull(17);
2111  Result.UpdateInt(18, 10);
2112 
2113  Result.InsertRow;
2114  end;
2115 end;
2116 
2117 {**
2118  Gets a description of a table's indices and statistics. They are
2119  ordered by NON_UNIQUE, TYPE, INDEX_NAME, and ORDINAL_POSITION.
2120 
2121  <P>Each index column description has the following columns:
2122  <OL>
2123  <LI><B>TABLE_CAT</B> String => table catalog (may be null)
2124  <LI><B>TABLE_SCHEM</B> String => table schema (may be null)
2125  <LI><B>TABLE_NAME</B> String => table name
2126  <LI><B>NON_UNIQUE</B> boolean => Can index values be non-unique?
2127  false when TYPE is tableIndexStatistic
2128  <LI><B>INDEX_QUALIFIER</B> String => index catalog (may be null);
2129  null when TYPE is tableIndexStatistic
2130  <LI><B>INDEX_NAME</B> String => index name; null when TYPE is
2131  tableIndexStatistic
2132  <LI><B>TYPE</B> short => index type:
2133  <UL>
2134  <LI> tableIndexStatistic - this identifies table statistics that are
2135  returned in conjuction with a table's index descriptions
2136  <LI> tableIndexClustered - this is a clustered index
2137  <LI> tableIndexHashed - this is a hashed index
2138  <LI> tableIndexOther - this is some other style of index
2139  </UL>
2140  <LI><B>ORDINAL_POSITION</B> short => column sequence number
2141  within index; zero when TYPE is tableIndexStatistic
2142  <LI><B>COLUMN_NAME</B> String => column name; null when TYPE is
2143  tableIndexStatistic
2144  <LI><B>ASC_OR_DESC</B> String => column sort sequence, "A" => ascending,
2145  "D" => descending, may be null if sort sequence is not supported;
2146  null when TYPE is tableIndexStatistic
2147  <LI><B>CARDINALITY</B> int => When TYPE is tableIndexStatistic, then
2148  this is the number of rows in the table; otherwise, it is the
2149  number of unique values in the index.
2150  <LI><B>PAGES</B> int => When TYPE is tableIndexStatisic then
2151  this is the number of pages used for the table, otherwise it
2152  is the number of pages used for the current index.
2153  <LI><B>FILTER_CONDITION</B> String => Filter condition, if any.
2154  (may be null)
2155  </OL>
2156 
2157  @param catalog a catalog name; "" retrieves those without a
2158  catalog; null means drop catalog name from the selection criteria
2159  @param schema a schema name; "" retrieves those without a schema
2160  @param table a table name
2161  @param unique when true, return only indices for unique values;
2162  when false, return indices regardless of whether unique or not
2163  @param approximate when true, result is allowed to reflect approximate
2164  or out of data values; when false, results are requested to be
2165  accurate
2166  @return <code>ResultSet</code> - each row is an index column description
2167 }
2168 function TZMySQLDatabaseMetadata.UncachedGetIndexInfo(const Catalog: string;
2169  const Schema: string; const Table: string; Unique: Boolean;
2170  Approximate: Boolean): IZResultSet;
2171 var
2172  LCatalog, LTable: string;
2173  ColumnIndexes : Array[1..7] of integer;
2174 begin
2175  if Table = '' then
2176  raise Exception.Create(STableIsNotSpecified); //CHANGE IT!
2177 
2178  Result:=inherited UncachedGetIndexInfo(Catalog, Schema, Table, Unique, Approximate);
2179 
2180  GetCatalogAndNamePattern(Catalog, Schema, Table,
2181  LCatalog, LTable);
2182 
2183  with GetConnection.CreateStatement.ExecuteQuery(
2184  Format('SHOW INDEX FROM %s.%s',
2185  [IC.Quote(LCatalog),
2186  IC.Quote(LTable)])) do
2187  begin
2188  ColumnIndexes[1] := FindColumn('Table');
2189  ColumnIndexes[2] := FindColumn('Non_unique');
2190  ColumnIndexes[3] := FindColumn('Key_name');
2191  ColumnIndexes[4] := FindColumn('Seq_in_index');
2192  ColumnIndexes[5] := FindColumn('Column_name');
2193  ColumnIndexes[6] := FindColumn('Collation');
2194  ColumnIndexes[7] := FindColumn('Cardinality');
2195  while Next do
2196  begin
2197  Result.MoveToInsertRow;
2198  Result.UpdateString(1, LCatalog);
2199  Result.UpdateNull(2);
2200  Result.UpdateString(3, GetString(ColumnIndexes[1]));
2201  if GetInt(ColumnIndexes[2]) = 0 then
2202  Result.UpdateString(4, 'true')
2203  else
2204  Result.UpdateString(4, 'false');
2205  Result.UpdateNull(5);
2206  Result.UpdateString(6, GetString(ColumnIndexes[3]));
2207  Result.UpdateInt(7, Ord(tiOther));
2208  Result.UpdateInt(8, GetInt(ColumnIndexes[4]));
2209  Result.UpdateString(9, GetString(ColumnIndexes[5]));
2210  Result.UpdateString(10, GetString(ColumnIndexes[6]));
2211  Result.UpdateString(11, GetString(ColumnIndexes[7]));
2212  Result.UpdateInt(12, 0);
2213  Result.UpdateNull(13);
2214  Result.InsertRow;
2215  end;
2216  Close;
2217  end;
2218 end;
2219 
2220 {**
2221  Gets a description of the stored procedures available in a
2222  catalog.
2223 
2224  <P>Only procedure descriptions matching the schema and
2225  procedure name criteria are returned. They are ordered by
2226  PROCEDURE_SCHEM, and PROCEDURE_NAME.
2227 
2228  <P>Each procedure description has the the following columns:
2229  <OL>
2230  <LI><B>PROCEDURE_CAT</B> String => procedure catalog (may be null)
2231  <LI><B>PROCEDURE_SCHEM</B> String => procedure schema (may be null)
2232  <LI><B>PROCEDURE_NAME</B> String => procedure name
2233  <LI> reserved for future use
2234  <LI> reserved for future use
2235  <LI> reserved for future use
2236  <LI><B>REMARKS</B> String => explanatory comment on the procedure
2237  <LI><B>PROCEDURE_TYPE</B> short => kind of procedure:
2238  <UL>
2239  <LI> procedureResultUnknown - May return a result
2240  <LI> procedureNoResult - Does not return a result
2241  <LI> procedureReturnsResult - Returns a result
2242  </UL>
2243  </OL>
2244 
2245  @param catalog a catalog name; "" retrieves those without a
2246  catalog; null means drop catalog name from the selection criteria
2247  @param schemaPattern a schema name pattern; "" retrieves those
2248  without a schema
2249  @param procedureNamePattern a procedure name pattern
2250  @return <code>ResultSet</code> - each row is a procedure description
2251  @see #getSearchStringEscape
2252 }
2253 function TZMySQLDatabaseMetadata.UncachedGetProcedures(const Catalog: string;
2254  const SchemaPattern: string; const ProcedureNamePattern: string): IZResultSet;
2255 var
2256  SQL: string;
2257  ProcedureNameCondition, SchemaCondition: string;
2258 begin
2259  If Catalog = '' then
2260  If SchemaPattern <> '' then
2261  SchemaCondition := ConstructNameCondition(SchemaPattern,'p.db')
2262  else
2263  SchemaCondition := ConstructNameCondition(FDatabase,'p.db')
2264  else
2265  SchemaCondition := ConstructNameCondition(Catalog,'p.db');
2266  ProcedureNameCondition := ConstructNameCondition(ProcedureNamePattern,'p.name');
2267  If SchemaCondition <> '' then
2268  SchemaCondition := ' and ' + SchemaCondition;
2269  If ProcedureNameCondition <> '' then
2270  ProcedureNameCondition := ' and ' + ProcedureNameCondition;
2271 
2272  SQL := 'SELECT NULL AS PROCEDURE_CAT, p.db AS PROCEDURE_SCHEM, '+
2273  'p.name AS PROCEDURE_NAME, NULL AS RESERVED1, NULL AS RESERVED2, '+
2274  'NULL AS RESERVED3, p.comment AS REMARKS, '+
2275  IntToStr(ProcedureReturnsResult)+' AS PROCEDURE_TYPE from mysql.proc p '+
2276  'WHERE 1=1' + SchemaCondition + ProcedureNameCondition+
2277  ' ORDER BY p.db, p.name';
2278  Result := CopyToVirtualResultSet(
2279  GetConnection.CreateStatement.ExecuteQuery(SQL),
2280  ConstructVirtualResultSet(ProceduresColumnsDynArray));
2281 end;
2282 
2283 {**
2284  Gets a description of a catalog's stored procedure parameters
2285  and result columns.
2286 
2287  <P>Only descriptions matching the schema, procedure and
2288  parameter name criteria are returned. They are ordered by
2289  PROCEDURE_SCHEM and PROCEDURE_NAME. Within this, the return value,
2290  if any, is first. Next are the parameter descriptions in call
2291  order. The column descriptions follow in column number order.
2292 
2293  <P>Each row in the <code>ResultSet</code> is a parameter description or
2294  column description with the following fields:
2295  <OL>
2296  <LI><B>PROCEDURE_CAT</B> String => procedure catalog (may be null)
2297  <LI><B>PROCEDURE_SCHEM</B> String => procedure schema (may be null)
2298  <LI><B>PROCEDURE_NAME</B> String => procedure name
2299  <LI><B>COLUMN_NAME</B> String => column/parameter name
2300  <LI><B>COLUMN_TYPE</B> Short => kind of column/parameter:
2301  <UL>
2302  <LI> procedureColumnUnknown - nobody knows
2303  <LI> procedureColumnIn - IN parameter
2304  <LI> procedureColumnInOut - INOUT parameter
2305  <LI> procedureColumnOut - OUT parameter
2306  <LI> procedureColumnReturn - procedure return value
2307  <LI> procedureColumnResult - result column in <code>ResultSet</code>
2308  </UL>
2309  <LI><B>DATA_TYPE</B> short => SQL type from java.sql.Types
2310  <LI><B>TYPE_NAME</B> String => SQL type name, for a UDT type the
2311  type name is fully qualified
2312  <LI><B>PRECISION</B> int => precision
2313  <LI><B>LENGTH</B> int => length in bytes of data
2314  <LI><B>SCALE</B> short => scale
2315  <LI><B>RADIX</B> short => radix
2316  <LI><B>NULLABLE</B> short => can it contain NULL?
2317  <UL>
2318  <LI> procedureNoNulls - does not allow NULL values
2319  <LI> procedureNullable - allows NULL values
2320  <LI> procedureNullableUnknown - nullability unknown
2321  </UL>
2322  <LI><B>REMARKS</B> String => comment describing parameter/column
2323  </OL>
2324 
2325  <P><B>Note:</B> Some databases may not return the column
2326  descriptions for a procedure. Additional columns beyond
2327  REMARKS can be defined by the database.
2328 
2329  @param catalog a catalog name; "" retrieves those without a
2330  catalog; null means drop catalog name from the selection criteria
2331  @param schemaPattern a schema name pattern; "" retrieves those
2332  without a schema
2333  @param procedureNamePattern a procedure name pattern
2334  @param columnNamePattern a column name pattern
2335  @return <code>ResultSet</code> - each row describes a stored procedure parameter or
2336  column
2337  @see #getSearchStringEscape
2338 }
2339 function TZMySQLDatabaseMetadata.UncachedGetProcedureColumns(const Catalog: string;
2340  const SchemaPattern: string; const ProcedureNamePattern: string;
2341  const ColumnNamePattern: string): IZResultSet;
2342 var
2343  SQL, TypeName, Temp: string;
2344  ParamList, Params, Names, Returns: TStrings;
2345  I, ColumnSize, Precision: Integer;
2346  FieldType: TZSQLType;
2347  ProcedureNameCondition, SchemaCondition: string;
2348 
2349  function GetNextName(const AName: String; NameEmpty: Boolean = False): String;
2350  var N: Integer;
2351  begin
2352  if (Names.IndexOf(AName) = -1) and not NameEmpty then
2353  begin
2354  Names.Add(AName);
2355  Result := AName;
2356  end
2357  else
2358  for N := 1 to MaxInt do
2359  if Names.IndexOf(AName+IntToStr(N)) = -1 then
2360  begin
2361  Names.Add(AName+IntToStr(N));
2362  Result := AName+IntToStr(N);
2363  Break;
2364  end;
2365  end;
2366 
2367  function DecomposeParamFromList(AList: TStrings): String;
2368  var
2369  J, I, N: Integer;
2370  Temp: String;
2371  procedure AddTempString(Const Value: String);
2372  begin
2373  if Temp = '' then
2374  Temp := Trim(Value)
2375  else
2376  Temp := Temp + LineEnding+ Trim(Value);
2377  end;
2378 
2379  begin
2380  J := 0;
2381  Temp := '';
2382  for I := 0 to AList.Count -1 do
2383  if J < AList.Count then
2384  begin
2385  if (Pos('(', (AList[J])) > 0) and (Pos(')', (AList[J])) = 0) then
2386  if ( Pos('real', LowerCase(AList[J])) > 0 ) or
2387  ( Pos('float', LowerCase(AList[J])) > 0 ) or
2388  ( Pos('decimal', LowerCase(AList[J])) > 0 ) or
2389  ( Pos('numeric', LowerCase(AList[J])) > 0 ) or
2390  ( Pos('double', LowerCase(AList[J])) > 0 ) then
2391  begin
2392  AddTempString(AList[j]+','+AList[j+1]);
2393  Inc(j);
2394  end
2395  else
2396  if ( Pos('set', LowerCase(AList[J])) > 0 ) and
2397  ( Pos(')', LowerCase(AList[J])) = 0 ) then
2398  begin
2399  TypeName := AList[J];
2400  for N := J+1 to AList.Count-1 do
2401  begin
2402  TypeName := TypeName +','+AList[N];
2403  if Pos(')', AList[N]) > 0 then
2404  Break;
2405  end;
2406  AddTempString(TypeName);
2407  J := N;
2408  end
2409  else
2410  AddTempString(AList[j])
2411  else
2412  if not (AList[j] = '') then
2413  AddTempString(AList[j]);
2414  Inc(J);
2415  end;
2416  Result := Temp;
2417  end;
2418 begin
2419  If Catalog = '' then
2420  If SchemaPattern <> '' then
2421  SchemaCondition := ConstructNameCondition(SchemaPattern,'p.db')
2422  else
2423  SchemaCondition := ConstructNameCondition(FDatabase,'p.db')
2424  else
2425  SchemaCondition := ConstructNameCondition(Catalog,'p.db');
2426  ProcedureNameCondition := ConstructNameCondition(ProcedureNamePattern,'p.name');
2427  If SchemaCondition <> '' then
2428  SchemaCondition := ' and ' + SchemaCondition;
2429  If ProcedureNameCondition <> '' then
2430  ProcedureNameCondition := ' and ' + ProcedureNameCondition;
2431 
2432  Result := inherited UncachedGetProcedureColumns(Catalog, SchemaPattern, ProcedureNamePattern, ColumnNamePattern);
2433 
2434  SQL := 'SELECT p.db AS PROCEDURE_CAT, NULL AS PROCEDURE_SCHEM, '+
2435  'p.name AS PROCEDURE_NAME, p.param_list AS PARAMS, p.comment AS REMARKS, '+
2436  IntToStr(ProcedureReturnsResult)+' AS PROCEDURE_TYPE, p.returns AS RETURN_VALUES '+
2437  ' from mysql.proc p where 1 = 1'+SchemaCondition+ProcedureNameCondition+
2438  ' ORDER BY p.db, p.name';
2439 
2440  try
2441  with GetConnection.CreateStatement.ExecuteQuery(SQL) do
2442  begin
2443  ParamList := TStringList.Create;
2444  Params := TStringList.Create;
2445  Names := TStringList.Create;
2446  Returns := TStringList.Create;
2447  while Next do
2448  begin
2449  PutSplitString(ParamList, Trim(GetString(4)), ',');
2450  PutSplitString(ParamList, DecomposeParamFromList(ParamList), LineEnding);
2451 
2452  PutSplitString(Returns, Trim(GetString(7)), ',');
2453  PutSplitString(Returns, DecomposeParamFromList(Returns), LineEnding);
2454 
2455  for I := 0 to Returns.Count-1 do
2456  begin
2457  Returns[i] := 'RETURNS '+Returns[i];
2458  ParamList.Add(Returns[i]);
2459  end;
2460 
2461  for i := 0 to ParamList.Count -1 do
2462  begin
2463  PutSplitString(Params, ParamList[i], ' ');
2464  if Params.Count = 2 then {no name available}
2465  if Params[0] = 'RETURNS' then
2466  Params.Insert(1,'')
2467  else
2468  if (UpperCase(Params[1]) = 'IN') or
2469  (UpperCase(Params[1]) = 'INOUT') or
2470  (UpperCase(Params[1]) = 'OUT') then
2471  Params.Insert(1,'')
2472  else
2473  Params.Insert(0,'IN'); //Function in value
2474 
2475  Result.MoveToInsertRow;
2476  Result.UpdateString(1, GetString(1)); //PROCEDURE_CAT
2477  Result.UpdateString(2, GetString(2)); //PROCEDURE_SCHEM
2478  Result.UpdateString(3, GetString(3)); //PROCEDURE_NAME
2479  ConvertMySQLColumnInfoFromString(Params[2],
2480  ConSettings, TypeName, Temp,
2481  FieldType, ColumnSize, Precision);
2482  { process COLUMN_NAME }
2483  if Params[1] = '' then
2484  if Params[0] = 'RETURNS' then
2485  Result.UpdateString(4, 'ReturnValue')
2486  else
2487  Result.UpdateString(4, GetNextName('$', True))
2488  else
2489  if IC.IsQuoted(Params[1]) then
2490  Result.UpdateString(4, GetNextName(Copy(Params[1], 2, Length(Params[1])-2), (Length(Params[1])=2)))
2491  else
2492  Result.UpdateString(4, GetNextName(Params[1]));
2493  { COLUMN_TYPE }
2494  if UpperCase(Params[0]) = 'OUT' then
2495  Result.UpdateInt(5, Ord(pctOut))
2496  else
2497  if UpperCase(Params[0]) = 'INOUT' then
2498  Result.UpdateInt(5, Ord(pctInOut))
2499  else
2500  if UpperCase(Params[0]) = 'IN' then
2501  Result.UpdateInt(5, Ord(pctIn))
2502  else
2503  if UpperCase(Params[0]) = 'RETURNS' then
2504  Result.UpdateInt(5, Ord(pctReturn))
2505  else
2506  Result.UpdateInt(5, Ord(pctUnknown));
2507 
2508  { DATA_TYPE }
2509  Result.UpdateInt(6, Ord(FieldType));
2510  { TYPE_NAME }
2511  Result.UpdateString(7, TypeName);
2512  { PRECISION }
2513  Result.UpdateInt(8, ColumnSize);
2514  { LENGTH }
2515  Result.UpdateInt(9, Precision);
2516 
2517  Result.UpdateNull(10);
2518  Result.UpdateNull(11);
2519  Result.UpdateInt(12, Ord(ntNullableUnknown));
2520  Result.UpdateNull(13);
2521  Result.InsertRow;
2522  end;
2523  end;
2524  Close;
2525  end;
2526  finally
2527  FreeAndNil(Names);
2528  FreeAndNil(Params);
2529  FreeAndNil(ParamList);
2530  FreeAndNil(Returns);
2531  end;
2532 end;
2533 
2534 {**
2535  Gets a description of a table's columns that are automatically
2536  updated when any value in a row is updated. They are
2537  unordered.
2538 
2539  <P>Each column description has the following columns:
2540  <OL>
2541  <LI><B>SCOPE</B> short => is not used
2542  <LI><B>COLUMN_NAME</B> String => column name
2543  <LI><B>DATA_TYPE</B> short => SQL data type from java.sql.Types
2544  <LI><B>TYPE_NAME</B> String => Data source dependent type name
2545  <LI><B>COLUMN_SIZE</B> int => precision
2546  <LI><B>BUFFER_LENGTH</B> int => length of column value in bytes
2547  <LI><B>DECIMAL_DIGITS</B> short => scale
2548  <LI><B>PSEUDO_COLUMN</B> short => is this a pseudo column
2549  like an Oracle ROWID
2550  <UL>
2551  <LI> versionColumnUnknown - may or may not be pseudo column
2552  <LI> versionColumnNotPseudo - is NOT a pseudo column
2553  <LI> versionColumnPseudo - is a pseudo column
2554  </UL>
2555  </OL>
2556 
2557  @param catalog a catalog name; "" retrieves those without a
2558  catalog; null means drop catalog name from the selection criteria
2559  @param schema a schema name; "" retrieves those without a schema
2560  @param table a table name
2561  @return <code>ResultSet</code> - each row is a column description
2562  @exception SQLException if a database access error occurs
2563 }
2564 function TZMySQLDatabaseMetadata.UncachedGetVersionColumns(const Catalog, Schema,
2565  Table: string): IZResultSet;
2566 begin
2567  Result:=inherited UncachedGetVersionColumns(Catalog, Schema, Table);
2568 
2569  Result.MoveToInsertRow;
2570  Result.UpdateNull(1);
2571  Result.UpdateString(2, 'ctid');
2572  // Result.UpdateInt(3, GetSQLType('tid')); //FIX IT
2573  Result.UpdateString(4, 'tid');
2574  Result.UpdateNull(5);
2575  Result.UpdateNull(6);
2576  Result.UpdateNull(7);
2577  Result.UpdateInt(4, Ord(vcPseudo));
2578  Result.InsertRow;
2579 end;
2580 
2581 {**
2582  Gets the used Collation and CharacterSet of spezified Object.
2583 
2584  @param catalog a catalog name; "" retrieves those without a
2585  catalog; null means drop catalog name from the selection criteria
2586  @param schema a schema name; "" and Catolog "" retrieves nothing
2587  @param table a table name; "" retrieves the Schema Colloation and CharacterSet
2588  @param ColumnNamePattern ColumnPattern;"" retrieves the
2589  Table(if @param TablePattern is set) or
2590  Schema(if @param TablePattern is NULL)
2591  Colloation and CharacterSet
2592  @return <code>ResultSet</code> - each row is a Collation, CharacterSet, ID,
2593  and ByteLength per Char of speziefied Object
2594 }
2595 function TZMySQLDatabaseMetadata.UncachedGetCollationAndCharSet(const Catalog, SchemaPattern,
2596  TableNamePattern, ColumnNamePattern: string): IZResultSet; //EgonHugeist
2597 var
2598  SQL, LCatalog: string;
2599  ColumnNameCondition, TableNameCondition, SchemaCondition: string;
2600 begin
2601  if Catalog = '' then
2602  begin
2603  if SchemaPattern <> '' then
2604  LCatalog := SchemaPattern
2605  else
2606  LCatalog := FDatabase;
2607  end
2608  else
2609  LCatalog := Catalog;
2610  If Catalog = '' then
2611  If SchemaPattern <> '' then
2612  SchemaCondition := ConstructNameCondition(SchemaPattern,'TABLE_SCHEMA')
2613  else
2614  SchemaCondition := ConstructNameCondition(FDatabase,'TABLE_SCHEMA')
2615  else
2616  SchemaCondition := ConstructNameCondition(Catalog,'TABLE_SCHEMA');
2617  TableNameCondition := ConstructNameCondition(TableNamePattern,'TABLE_NAME');
2618  ColumnNameCondition := ConstructNameCondition(ColumnNamePattern,'COLUMN_NAME');
2619  If SchemaCondition <> '' then
2620  SchemaCondition := ' and ' + SchemaCondition;
2621  If TableNameCondition <> '' then
2622  TableNameCondition := ' and ' + TableNameCondition;
2623  If ColumnNameCondition <> '' then
2624  ColumnNameCondition := ' and ' + ColumnNameCondition;
2625 
2626  Result:=inherited UncachedGetCollationAndCharSet(Catalog, SchemaPattern, TableNamePattern, ColumnNamePattern);
2627 
2628  if SchemaCondition <> '' then
2629  begin
2630  if TableNamePattern <> '' then
2631  begin
2632  if ColumnNamePattern <> '' then
2633  begin
2634  SQL := 'SELECT CLMS.COLLATION_NAME, CLMS.CHARACTER_SET_NAME, CS.MAXLEN '+
2635  'FROM INFORMATION_SCHEMA.COLUMNS CLMS '+
2636  'LEFT JOIN INFORMATION_SCHEMA.CHARACTER_SETS CS '+
2637  'ON CS.DEFAULT_COLLATE_NAME = CLMS.COLLATION_NAME '+
2638  'WHERE 1=1'+ SchemaCondition + TableNameCondition + ColumnNameCondition;
2639  with GetConnection.CreateStatement.ExecuteQuery(SQL) do
2640  begin
2641  if Next then
2642  begin
2643  Result.MoveToInsertRow;
2644  Result.UpdateString(1, LCatalog); //COLLATION_CATALOG
2645  Result.UpdateString(2, LCatalog); //COLLATION_SCHEMA
2646  Result.UpdateString(3, TableNamePattern); //COLLATION_TABLE
2647  Result.UpdateString(4, ColumnNamePattern);//COLLATION_COLUMN
2648  Result.UpdateString(5, GetString(FindColumn('COLLATION_NAME'))); //COLLATION_NAME
2649  Result.UpdateString(6, GetString(FindColumn('CHARACTER_SET_NAME'))); //CHARACTER_SET_NAME
2650  Result.UpdateNull(7); //CHARACTER_SET_ID
2651  Result.UpdateShort(8, GetShort(FindColumn('MAXLEN'))); //CHARACTER_SET_SIZE
2652  Result.InsertRow;
2653  end;
2654  Close;
2655  end;
2656  end
2657  else
2658  begin
2659  SQL := 'SELECT TBLS.TABLE_COLLATION, CS.CHARACTER_SET_NAME, CS.MAXLEN '+
2660  'FROM INFORMATION_SCHEMA.TABLES TBLS LEFT JOIN '+
2661  'INFORMATION_SCHEMA.CHARACTER_SETS CS ON '+
2662  'TBLS.TABLE_COLLATION = CS.DEFAULT_COLLATE_NAME '+
2663  'WHERE 1=1'+ SchemaCondition + TableNameCondition;
2664  with GetConnection.CreateStatement.ExecuteQuery(SQL) do
2665  begin
2666  if Next then
2667  begin
2668  Result.MoveToInsertRow;
2669  Result.UpdateString(1, LCatalog); //COLLATION_CATALOG
2670  Result.UpdateString(2, LCatalog); //COLLATION_SCHEMA
2671  Result.UpdateString(3, TableNamePattern); //COLLATION_TABLE
2672  Result.UpdateString(5, GetString(FindColumn('TABLE_COLLATION'))); //COLLATION_NAME
2673  Result.UpdateString(6, GetString(FindColumn('CHARACTER_SET_NAME'))); //CHARACTER_SET_NAME
2674  Result.UpdateNull(7); //CHARACTER_SET_ID
2675  Result.UpdateShort(8, GetShort(FindColumn('MAXLEN'))); //CHARACTER_SET_SIZE
2676  Result.InsertRow;
2677  end;
2678  Close;
2679  end;
2680  end;
2681  end
2682  else
2683  begin
2684  SchemaCondition := ConstructNameCondition(LCatalog, 'and SCHEMA_NAME');
2685  SQL := 'SELECT S.DEFAULT_COLLATION_NAME, S.DEFAULT_CHARACTER_SET_NAME, '+
2686  'CS.MAXLEN FROM INFORMATION_SCHEMA.SCHEMATA S '+
2687  'LEFT JOIN INFORMATION_SCHEMA.CHARACTER_SETS CS '+
2688  'ON CS.DEFAULT_COLLATE_NAME = S.DEFAULT_COLLATION_NAME '+
2689  'WHERE 1=1 '+ SchemaCondition;
2690  with GetConnection.CreateStatement.ExecuteQuery(SQL) do
2691  begin
2692  if Next then
2693  begin
2694  Result.MoveToInsertRow;
2695  Result.UpdateString(1, LCatalog); //COLLATION_CATALOG
2696  Result.UpdateString(2, LCatalog); //COLLATION_SCHEMA
2697  Result.UpdateNull(3); //COLLATION_TABLE
2698  Result.UpdateNull(4);//COLLATION_COLUMN
2699  Result.UpdateString(5, GetString(FindColumn('DEFAULT_COLLATION_NAME'))); //COLLATION_NAME
2700  Result.UpdateString(6, GetString(FindColumn('DEFAULT_CHARACTER_SET_NAME'))); //CHARACTER_SET_NAME
2701  Result.UpdateNull(7); //CHARACTER_SET_ID
2702  Result.UpdateShort(8, GetShort(FindColumn('MAXLEN'))); //CHARACTER_SET_SIZE
2703  Result.InsertRow;
2704  end;
2705  Close;
2706  end;
2707  end;
2708  end;
2709 end;
2710 
2711 {**
2712  Gets the supported CharacterSets:
2713  @return <code>ResultSet</code> - each row is a CharacterSetName and it's ID
2714 }
2715 function TZMySQLDatabaseMetadata.UncachedGetCharacterSets: IZResultSet; //EgonHugeist
2716 begin
2717  Result:=inherited UncachedGetCharacterSets;
2718 
2719  with GetConnection.CreateStatement.ExecuteQuery(
2720  'SELECT CHARACTER_SET_NAME '+
2721  'FROM INFORMATION_SCHEMA.CHARACTER_SETS') do
2722  begin
2723  while Next do
2724  begin
2725  Result.MoveToInsertRow;
2726  Result.UpdateString(1, GetString(FindColumn('CHARACTER_SET_NAME'))); //CHARACTER_SET_NAME
2727  Result.UpdateNull(2); //CHARACTER_SET_ID
2728  Result.InsertRow;
2729  end;
2730  Close;
2731  end;
2732 end;
2733 
2734 
2735 
2736 end.
2737 
2738