zeoslib  UNKNOWN
 All Files
ZDbcPostgreSqlMetadata.pas
Go to the documentation of this file.
1 {*********************************************************}
2 { }
3 { Zeos Database Objects }
4 { PostgreSQL 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 ZDbcPostgreSqlMetadata;
54 
55 interface
56 
57 {$I ZDbc.inc}
58 
59 uses
60  Types, Classes, {$IFDEF MSEgui}mclasses,{$ENDIF} SysUtils,
61  ZSysUtils, ZDbcIntfs, ZDbcMetadata, ZCompatibility, ZDbcPostgreSqlUtils,
62  ZDbcConnection, ZSelectSchema;
63 
64 type
65  {** Implements a PostgreSQL Case Sensitive/Unsensitive identifier convertor. }
66  TZPostgreSQLIdentifierConvertor = class (TZDefaultIdentifierConvertor)
67  protected
68  function IsSpecialCase(const Value: string): Boolean; override;
69  public
70  function IsQuoted(const Value: string): Boolean; override;
71  function Quote(const Value: string): string; override;
72  function ExtractQuote(const Value: string): string; override;
73  end;
74 
75  {**
76  Database information interface for PostgreSQL. Adds some PostgreSQL-specific
77  methods to IZDatabaseInfo.
78  } // technobot 2008-06-27
79  IZPostgreSQLDatabaseInfo = interface(IZDatabaseInfo)
80  ['{7D48BBAA-FAE2-48EA-8B9E-663CCA5690EC}']
81  // database and driver info:
82  function HasMinimumServerVersion(MajorVersion: Integer;
83  MinorVersion: Integer): Boolean;
84  end;
85  IZPostgreDBInfo = IZPostgreSQLDatabaseInfo; // shorthand alias
86 
87  // technobot 2008-06-27 - methods moved as is from TZPostgreSQLDatabaseMetadata:
88  {** Implements PostgreSQL Database Information. }
89  TZPostgreSQLDatabaseInfo = class(TZAbstractDatabaseInfo, IZPostgreSQLDatabaseInfo)
90  protected
91  function GetMaxIndexKeys: Integer;
92  function GetMaxNameLength: Integer;
93 // function UncachedGetUDTs(const Catalog: string; const SchemaPattern: string;
94 // const TypeNamePattern: string; const Types: TIntegerDynArray): IZResultSet; override;
95  public
96  constructor Create(const Metadata: TZAbstractDatabaseMetadata);
97  destructor Destroy; override;
98 
99  // database/driver/server info:
100  function GetDatabaseProductName: string; override;
101  function GetDatabaseProductVersion: string; override;
102  function GetDriverName: string; override;
103 // function GetDriverVersion: string; override; -> Same as parent
104  function GetDriverMajorVersion: Integer; override;
105  function GetDriverMinorVersion: Integer; override;
106  function GetServerVersion: string; override;
107  function HasMinimumServerVersion(MajorVersion: Integer;
108  MinorVersion: Integer): Boolean; // was TZPostgreSQLDatabaseMetadata.HaveMinimumServerVersion
109 
110  // capabilities (what it can/cannot do):
111 // function AllProceduresAreCallable: Boolean; override; -> Not implemented
112 // function AllTablesAreSelectable: Boolean; override; -> Not implemented
113  function SupportsMixedCaseIdentifiers: Boolean; override;
114  function SupportsMixedCaseQuotedIdentifiers: Boolean; override;
115 // function SupportsAlterTableWithAddColumn: Boolean; override; -> Not implemented
116 // function SupportsAlterTableWithDropColumn: Boolean; override; -> Not implemented
117 // function SupportsColumnAliasing: Boolean; override; -> Not implemented
118 // function SupportsConvert: Boolean; override; -> Not implemented
119 // function SupportsConvertForTypes(FromType: TZSQLType; ToType: TZSQLType):
120 // Boolean; override; -> Not implemented
121 // function SupportsTableCorrelationNames: Boolean; override; -> Not implemented
122 // function SupportsDifferentTableCorrelationNames: Boolean; override; -> Not implemented
123  function SupportsExpressionsInOrderBy: Boolean; override;
124  function SupportsOrderByUnrelated: Boolean; override;
125  function SupportsGroupBy: Boolean; override;
126  function SupportsGroupByUnrelated: Boolean; override;
127  function SupportsGroupByBeyondSelect: Boolean; override;
128 // function SupportsLikeEscapeClause: Boolean; override; -> Not implemented
129 // function SupportsMultipleResultSets: Boolean; override; -> Not implemented
130 // function SupportsMultipleTransactions: Boolean; override; -> Not implemented
131 // function SupportsNonNullableColumns: Boolean; override; -> Not implemented
132 // function SupportsMinimumSQLGrammar: Boolean; override; -> Not implemented
133 // function SupportsCoreSQLGrammar: Boolean; override; -> Not implemented
134 // function SupportsExtendedSQLGrammar: Boolean; override; -> Not implemented
135 // function SupportsANSI92EntryLevelSQL: Boolean; override; -> Not implemented
136 // function SupportsANSI92IntermediateSQL: Boolean; override; -> Not implemented
137 // function SupportsANSI92FullSQL: Boolean; override; -> Not implemented
138  function SupportsIntegrityEnhancementFacility: Boolean; override;
139 // function SupportsOuterJoins: Boolean; override; -> Not implemented
140 // function SupportsFullOuterJoins: Boolean; override; -> Not implemented
141 // function SupportsLimitedOuterJoins: Boolean; override; -> Not implemented
142  function SupportsSchemasInDataManipulation: Boolean; override;
143  function SupportsSchemasInProcedureCalls: Boolean; override;
144  function SupportsSchemasInTableDefinitions: Boolean; override;
145  function SupportsSchemasInIndexDefinitions: Boolean; override;
146  function SupportsSchemasInPrivilegeDefinitions: Boolean; override;
147  function SupportsCatalogsInDataManipulation: Boolean; override;
148  function SupportsCatalogsInProcedureCalls: Boolean; override;
149  function SupportsCatalogsInTableDefinitions: Boolean; override;
150  function SupportsCatalogsInIndexDefinitions: Boolean; override;
151  function SupportsCatalogsInPrivilegeDefinitions: Boolean; override;
152  function SupportsPositionedDelete: Boolean; override;
153  function SupportsPositionedUpdate: Boolean; override;
154  function SupportsSelectForUpdate: Boolean; override;
155  function SupportsStoredProcedures: Boolean; override;
156  function SupportsSubqueriesInComparisons: Boolean; override;
157  function SupportsSubqueriesInExists: Boolean; override;
158  function SupportsSubqueriesInIns: Boolean; override;
159  function SupportsSubqueriesInQuantifieds: Boolean; override;
160  function SupportsCorrelatedSubqueries: Boolean; override;
161  function SupportsUnion: Boolean; override;
162  function SupportsUnionAll: Boolean; override;
163  function SupportsOpenCursorsAcrossCommit: Boolean; override;
164  function SupportsOpenCursorsAcrossRollback: Boolean; override;
165  function SupportsOpenStatementsAcrossCommit: Boolean; override;
166  function SupportsOpenStatementsAcrossRollback: Boolean; override;
167  function SupportsTransactions: Boolean; override;
168  function SupportsTransactionIsolationLevel(Level: TZTransactIsolationLevel):
169  Boolean; override;
170  function SupportsDataDefinitionAndDataManipulationTransactions: Boolean; override;
171  function SupportsDataManipulationTransactionsOnly: Boolean; override;
172  function SupportsResultSetType(_Type: TZResultSetType): Boolean; override;
173  function SupportsResultSetConcurrency(_Type: TZResultSetType;
174  Concurrency: TZResultSetConcurrency): Boolean; override;
175 // function SupportsBatchUpdates: Boolean; override; -> Not implemented
176 
177  // maxima:
178  function GetMaxBinaryLiteralLength: Integer; override;
179  function GetMaxCharLiteralLength: Integer; override;
180  function GetMaxColumnNameLength: Integer; override;
181  function GetMaxColumnsInGroupBy: Integer; override;
182  function GetMaxColumnsInIndex: Integer; override;
183  function GetMaxColumnsInOrderBy: Integer; override;
184  function GetMaxColumnsInSelect: Integer; override;
185  function GetMaxColumnsInTable: Integer; override;
186  function GetMaxConnections: Integer; override;
187  function GetMaxCursorNameLength: Integer; override;
188  function GetMaxIndexLength: Integer; override;
189  function GetMaxSchemaNameLength: Integer; override;
190  function GetMaxProcedureNameLength: Integer; override;
191  function GetMaxCatalogNameLength: Integer; override;
192  function GetMaxRowSize: Integer; override;
193  function GetMaxStatementLength: Integer; override;
194  function GetMaxStatements: Integer; override;
195  function GetMaxTableNameLength: Integer; override;
196  function GetMaxTablesInSelect: Integer; override;
197  function GetMaxUserNameLength: Integer; override;
198 
199  // policies (how are various data and operations handled):
200 // function IsReadOnly: Boolean; override; -> Not implemented
201 // function IsCatalogAtStart: Boolean; override; -> Not implemented
202  function DoesMaxRowSizeIncludeBlobs: Boolean; override;
203 // function NullsAreSortedHigh: Boolean; override; -> Not implemented
204 // function NullsAreSortedLow: Boolean; override; -> Not implemented
205 // function NullsAreSortedAtStart: Boolean; override; -> Not implemented
206 // function NullsAreSortedAtEnd: Boolean; override; -> Not implemented
207 // function NullPlusNonNullIsNull: Boolean; override; -> Not implemented
208 // function UsesLocalFiles: Boolean; override; -> Not implemented
209  function UsesLocalFilePerTable: Boolean; override;
210  function StoresUpperCaseIdentifiers: Boolean; override;
211  function StoresLowerCaseIdentifiers: Boolean; override;
212  function StoresMixedCaseIdentifiers: Boolean; override;
213  function StoresUpperCaseQuotedIdentifiers: Boolean; override;
214  function StoresLowerCaseQuotedIdentifiers: Boolean; override;
215  function StoresMixedCaseQuotedIdentifiers: Boolean; override;
216  function GetDefaultTransactionIsolation: TZTransactIsolationLevel; override;
217  function DataDefinitionCausesTransactionCommit: Boolean; override;
218  function DataDefinitionIgnoredInTransactions: Boolean; override;
219 
220  // interface details (terms, keywords, etc):
221  function GetSchemaTerm: string; override;
222  function GetProcedureTerm: string; override;
223  function GetCatalogTerm: string; override;
224  function GetCatalogSeparator: string; override;
225  function GetSQLKeywords: string; override;
226  function GetNumericFunctions: string; override;
227  function GetStringFunctions: string; override;
228  function GetSystemFunctions: string; override;
229  function GetTimeDateFunctions: string; override;
230  function GetSearchStringEscape: string; override;
231  function GetExtraNameCharacters: string; override;
232  end;
233 
234  {** Implements PostgreSQL Database Metadata. }
235  TZPostgreSQLDatabaseMetadata = class(TZAbstractDatabaseMetadata)
236  private
237  function GetRuleType(const Rule: String): TZImportedKey;
238  protected
239  function CreateDatabaseInfo: IZDatabaseInfo; override; // technobot 2008-06-27
240 
241  // (technobot) should any of these be moved to TZPostgreSQLDatabaseInfo?:
242  function GetPostgreSQLType(Oid: Integer): string;
243  function GetSQLTypeByOid(Oid: Integer): TZSQLType;
244  function GetSQLTypeByName(TypeName: string): TZSQLType;
245  function TableTypeSQLExpression(TableType: string; UseSchemas: Boolean):
246  string;
247  procedure ParseACLArray(List: TStrings; AclString: string);
248  function GetPrivilegeName(Permission: char): string;
249  // (technobot) end of questioned section
250 
251  function EscapeString(const S: string): string; override;
252  function UncachedGetTables(const Catalog: string; const SchemaPattern: string;
253  const TableNamePattern: string; const Types: TStringDynArray): IZResultSet; override;
254  function UncachedGetSchemas: IZResultSet; override;
255  function UncachedGetCatalogs: IZResultSet; override;
256  function UncachedGetTableTypes: IZResultSet; override;
257  function UncachedGetColumns(const Catalog: string; const SchemaPattern: string;
258  const TableNamePattern: string; const ColumnNamePattern: string): IZResultSet; override;
259  function UncachedGetTablePrivileges(const Catalog: string; const SchemaPattern: string;
260  const TableNamePattern: string): IZResultSet; override;
261  function UncachedGetColumnPrivileges(const Catalog: string; const Schema: string;
262  const Table: string; const ColumnNamePattern: string): IZResultSet; override;
263  function UncachedGetPrimaryKeys(const Catalog: string; const Schema: string;
264  const Table: string): IZResultSet; override;
265  function UncachedGetImportedKeys(const Catalog: string; const Schema: string;
266  const Table: string): IZResultSet; override;
267  function UncachedGetExportedKeys(const Catalog: string; const Schema: string;
268  const Table: string): IZResultSet; override;
269  function UncachedGetCrossReference(const PrimaryCatalog: string; const PrimarySchema: string;
270  const PrimaryTable: string; const ForeignCatalog: string; const ForeignSchema: string;
271  const ForeignTable: string): IZResultSet; override;
272  function UncachedGetIndexInfo(const Catalog: string; const Schema: string; const Table: string;
273  Unique: Boolean; Approximate: Boolean): IZResultSet; override;
274  function UncachedGetSequences(const Catalog: string; const SchemaPattern: string;
275  const SequenceNamePattern: string): IZResultSet; override;
276  function UncachedGetProcedures(const Catalog: string; const SchemaPattern: string;
277  const ProcedureNamePattern: string): IZResultSet; override;
278  function UncachedGetProcedureColumns(const Catalog: string; const SchemaPattern: string;
279  const ProcedureNamePattern: string; const ColumnNamePattern: string):
280  IZResultSet; override;
281  function UncachedGetVersionColumns(const Catalog: string; const Schema: string;
282  const Table: string): IZResultSet; override;
283  function UncachedGetTypeInfo: IZResultSet; override;
284  function UncachedGetCharacterSets: IZResultSet; override; //EgonHugeist
285 
286  public
287  destructor Destroy; override;
288  function GetIdentifierConvertor: IZIdentifierConvertor; override;
289  end;
290 
291 implementation
292 
293 uses
294  ZMessages, ZDbcUtils, ZDbcPostgreSql;
295 
296 { TZPostgreSQLDatabaseInfo }
297 
298 {**
299  Constructs this object.
300  @param Metadata the interface of the correpsonding database metadata object
301 }
302 constructor TZPostgreSQLDatabaseInfo.Create(const Metadata: TZAbstractDatabaseMetadata);
303 begin
304  inherited;
305 end;
306 
307 {**
308  Destroys this object and cleanups the memory.
309 }
310 destructor TZPostgreSQLDatabaseInfo.Destroy;
311 begin
312  inherited;
313 end;
314 
315 //----------------------------------------------------------------------
316 // First, a variety of minor information about the target database.
317 
318 {**
319  What's the name of this database product?
320  @return database product name
321 }
322 function TZPostgreSQLDatabaseInfo.GetDatabaseProductName: string;
323 begin
324  Result := 'PostgreSQL';
325 end;
326 
327 {**
328  What's the version of this database product?
329  @return database version
330 }
331 function TZPostgreSQLDatabaseInfo.GetDatabaseProductVersion: string;
332 begin
333  Result := '';
334 end;
335 
336 {**
337  What's the name of this JDBC driver?
338  @return JDBC driver name
339 }
340 function TZPostgreSQLDatabaseInfo.GetDriverName: string;
341 begin
342  Result := 'Zeos Database Connectivity Driver for PostgreSQL';
343 end;
344 
345 {**
346  What's this JDBC driver's major version number?
347  @return JDBC driver major version
348 }
349 function TZPostgreSQLDatabaseInfo.GetDriverMajorVersion: Integer;
350 begin
351  Result := 1;
352 end;
353 
354 {**
355  What's this JDBC driver's minor version number?
356  @return JDBC driver minor version number
357 }
358 function TZPostgreSQLDatabaseInfo.GetDriverMinorVersion: Integer;
359 begin
360  Result := 1;
361 end;
362 
363 {**
364  Returns the server version
365  @return the server version string
366 }
367 function TZPostgreSQLDatabaseInfo.GetServerVersion: string;
368 begin
369  with Metadata.GetConnection as IZPostgreSQLConnection do
370  Result := Format('%s.%s', [GetServerMajorVersion, GetServerMinorVersion]);
371 end;
372 
373 {**
374  Does the database use a file for each table?
375  @return true if the database uses a local file for each table
376 }
377 function TZPostgreSQLDatabaseInfo.UsesLocalFilePerTable: Boolean;
378 begin
379  Result := False;
380 end;
381 
382 {**
383  Does the database treat mixed case unquoted SQL identifiers as
384  case sensitive and as a result store them in mixed case?
385  A JDBC Compliant<sup><font size=-2>TM</font></sup> driver will always return false.
386  @return <code>true</code> if so; <code>false</code> otherwise
387 }
388 function TZPostgreSQLDatabaseInfo.SupportsMixedCaseIdentifiers: Boolean;
389 begin
390  Result := False;
391 end;
392 
393 {**
394  Does the database treat mixed case unquoted SQL identifiers as
395  case insensitive and store them in upper case?
396  @return <code>true</code> if so; <code>false</code> otherwise
397 }
398 function TZPostgreSQLDatabaseInfo.StoresUpperCaseIdentifiers: Boolean;
399 begin
400  Result := False;
401 end;
402 
403 {**
404  Does the database treat mixed case unquoted SQL identifiers as
405  case insensitive and store them in lower case?
406  @return <code>true</code> if so; <code>false</code> otherwise
407 }
408 function TZPostgreSQLDatabaseInfo.StoresLowerCaseIdentifiers: Boolean;
409 begin
410  Result := True;
411 end;
412 
413 {**
414  Does the database treat mixed case unquoted SQL identifiers as
415  case insensitive and store them in mixed case?
416  @return <code>true</code> if so; <code>false</code> otherwise
417 }
418 function TZPostgreSQLDatabaseInfo.StoresMixedCaseIdentifiers: Boolean;
419 begin
420  Result := False;
421 end;
422 
423 {**
424  Does the database treat mixed case quoted SQL identifiers as
425  case sensitive and as a result store them in mixed case?
426  A JDBC Compliant<sup><font size=-2>TM</font></sup> driver will always return true.
427  @return <code>true</code> if so; <code>false</code> otherwise
428 }
429 function TZPostgreSQLDatabaseInfo.SupportsMixedCaseQuotedIdentifiers: Boolean;
430 begin
431  Result := True;
432 end;
433 
434 {**
435  Does the database treat mixed case quoted SQL identifiers as
436  case insensitive and store them in upper case?
437  @return <code>true</code> if so; <code>false</code> otherwise
438 }
439 function TZPostgreSQLDatabaseInfo.StoresUpperCaseQuotedIdentifiers: Boolean;
440 begin
441  Result := False;
442 end;
443 
444 {**
445  Does the database treat mixed case quoted SQL identifiers as
446  case insensitive and store them in lower case?
447  @return <code>true</code> if so; <code>false</code> otherwise
448 }
449 function TZPostgreSQLDatabaseInfo.StoresLowerCaseQuotedIdentifiers: Boolean;
450 begin
451  Result := False;
452 end;
453 
454 {**
455  Does the database treat mixed case quoted SQL identifiers as
456  case insensitive and store them in mixed case?
457  @return <code>true</code> if so; <code>false</code> otherwise
458 }
459 function TZPostgreSQLDatabaseInfo.StoresMixedCaseQuotedIdentifiers: Boolean;
460 begin
461  Result := True;
462 end;
463 
464 {**
465  Gets a comma-separated list of all a database's SQL keywords
466  that are NOT also SQL92 keywords.
467  @return the list
468 }
469 function TZPostgreSQLDatabaseInfo.GetSQLKeywords: string;
470 begin
471  Result := 'abort,absolute,access,action,add,admin,after,aggregate,all,also,'+
472  'alter,always,analyse,analyze,and,any,array,asc,assertion,assignment,'+
473  'asymmetric,at,authorization,'+
474  'backward,before,begin,between,bigint,binary,bit,boolean,both,'+
475  'cache,called,cascade,cascaded,case,cast,catalog,chain,character,'+
476  'characteristics,check,checkpoint,class,close,cluster,coalesce,'+
477  'collate,column,comment,commit,committed,concurrently,configuration,'+
478  'connect,connection,constraint,constraints,content,continue,'+
479  'conversion,convert,copy,cost,createdb,createrole,createuser,cross,'+
480  'csv,current,current_catalog,current_date,current_role,current_schema,'+
481  'current_time,current_timestamp,current_user,cursor,cycle,'+
482  'data,database,day,deallocate,dec,decimal,declare,default,defaults,'+
483  'deferrable,deferred,definer,delimiter,delimiters,desc,dictionary,'+
484  'disable,discard,distinct,do,document,domain,double,'+
485  'each,else,enable,encoding,encrypted,end,end-exec,enum,escape,except,'+
486  'excluding,exclusive,exec,execute,exists,explain,external,extract,'+
487  'false,family,fetch,first,float,following,for,force,foreign,forward,'+
488  'freeze,full,function,'+
489  'global,grant,granted,greatest,'+
490  'handler,header,hold,hour,'+
491  'identity,if,ilike,immediate,immutable,implicit,in,including,'+
492  'increment,indexes,inherit,inherits,initially,inner,inout,input,'+
493  'insensitive,instead,int,intersect,interval,invoker,isnull,isolation,'+
494  'join,'+
495  'lancompiler,language,large,last,lc_collate,lc_ctype,leading,least,'+
496  'left,level,like,limit,listen,load,local,localtime,localtimestamp,'+
497  'location,lock,login,'+
498  'mapping,match,maxvalue,minute,minvalue,mode,month,move,'+
499  'name,names,national,natural,nchar,new,next,no,nocreatedb,nocreaterole,'+
500  'nocreateuser,noinherit,nologin,none,nosuperuser,not,nothing,notify,'+
501  'notnull,nowait,nullif,nulls,numeric,'+
502  'object,of,off,offset,oids,old,only,operator,option,options,or,out,'+
503  'outer,over,overlaps,overlay,owned,owner,'+
504  'parser,partial,partition,password,placing,plans,position,preceding,'+
505  'precision,prepare,prepared,preserve,prior,privileges,procedural,'+
506  'procedure,'+
507  'quote,'+
508  'range,read,real,reassign,recheck,recursive,references,reindex,'+
509  'relative,release,rename,repeatable,replace,replica,reset,restart,'+
510  'restrict,return,returning,returns,revoke,right,role,rollback,row,'+
511  'rows,rule,'+
512  'savepoint,schema,scroll,search,second,security,sequence,serializable,'+
513  'server,session,session_user,setof,share,show,similar,simple,smallint,'+
514  'some,stable,standalone,start,statement,statistics,stdin,stdout,'+
515  'storage,strict,strip,substring,superuser,symmetric,sysid,system,'+
516  'tablespace,temp,template,temporary,text,then,time,timestamp,to,'+
517  'trailing,transaction,treat,trigger,trim,true,truncate,trusted,type,'+
518  'unbounded,uncommitted,unencrypted,union,unique,unknown,unlisten,'+
519  'until,user,using,'+
520  'vacuum,valid,validator,value,variadic,varying,verbose,version,view,'+
521  'volatile,'+
522  'when,whitespace,window,with,without,work,wrapper,write,'+
523  'xml,xmlattributes,xmlconcat,xmlelement,xmlforest,xmlparse,xmlpi,'+
524  'xmlroot,xmlserialize,'+
525  'year,yes,'+
526  'zone';
527 end;
528 
529 {**
530  Gets a comma-separated list of math functions. These are the
531  X/Open CLI math function names used in the JDBC function escape
532  clause.
533  @return the list
534 }
535 function TZPostgreSQLDatabaseInfo.GetNumericFunctions: string;
536 begin
537  Result := '';
538 end;
539 
540 {**
541  Gets a comma-separated list of string functions. These are the
542  X/Open CLI string function names used in the JDBC function escape
543  clause.
544  @return the list
545 }
546 function TZPostgreSQLDatabaseInfo.GetStringFunctions: string;
547 begin
548  Result := '';
549 end;
550 
551 {**
552  Gets a comma-separated list of system functions. These are the
553  X/Open CLI system function names used in the JDBC function escape
554  clause.
555  @return the list
556 }
557 function TZPostgreSQLDatabaseInfo.GetSystemFunctions: string;
558 begin
559  Result := '';
560 end;
561 
562 {**
563  Gets a comma-separated list of time and date functions.
564  @return the list
565 }
566 function TZPostgreSQLDatabaseInfo.GetTimeDateFunctions: string;
567 begin
568  Result := '';
569 end;
570 
571 {**
572  Gets the string that can be used to escape wildcard characters.
573  This is the string that can be used to escape '_' or '%' in
574  the string pattern style catalog search parameters.
575 
576  <P>The '_' character represents any single character.
577  <P>The '%' character represents any sequence of zero or
578  more characters.
579 
580  @return the string used to escape wildcard characters
581 }
582 function TZPostgreSQLDatabaseInfo.GetSearchStringEscape: string;
583 begin
584  Result := '\';
585 end;
586 
587 {**
588  Gets all the "extra" characters that can be used in unquoted
589  identifier names (those beyond a-z, A-Z, 0-9 and _).
590  @return the string containing the extra characters
591 }
592 function TZPostgreSQLDatabaseInfo.GetExtraNameCharacters: string;
593 begin
594  Result := '';
595 end;
596 
597 //--------------------------------------------------------------------
598 // Functions describing which features are supported.
599 
600 {**
601  Are expressions in "ORDER BY" lists supported?
602  @return <code>true</code> if so; <code>false</code> otherwise
603 }
604 function TZPostgreSQLDatabaseInfo.SupportsExpressionsInOrderBy: Boolean;
605 begin
606  Result := True;
607 end;
608 
609 {**
610  Can an "ORDER BY" clause use columns not in the SELECT statement?
611  @return <code>true</code> if so; <code>false</code> otherwise
612 }
613 function TZPostgreSQLDatabaseInfo.SupportsOrderByUnrelated: Boolean;
614 begin
615  Result := HasMinimumServerVersion(6, 4);
616 end;
617 
618 {**
619  Is some form of "GROUP BY" clause supported?
620  @return <code>true</code> if so; <code>false</code> otherwise
621 }
622 function TZPostgreSQLDatabaseInfo.SupportsGroupBy: Boolean;
623 begin
624  Result := True;
625 end;
626 
627 {**
628  Can a "GROUP BY" clause use columns not in the SELECT?
629  @return <code>true</code> if so; <code>false</code> otherwise
630 }
631 function TZPostgreSQLDatabaseInfo.SupportsGroupByUnrelated: Boolean;
632 begin
633  Result := HasMinimumServerVersion(6, 4);
634 end;
635 
636 {**
637  Can a "GROUP BY" clause add columns not in the SELECT
638  provided it specifies all the columns in the SELECT?
639  @return <code>true</code> if so; <code>false</code> otherwise
640 }
641 function TZPostgreSQLDatabaseInfo.SupportsGroupByBeyondSelect: Boolean;
642 begin
643  Result := HasMinimumServerVersion(6, 4);
644 end;
645 
646 {**
647  Is the SQL Integrity Enhancement Facility supported?
648  @return <code>true</code> if so; <code>false</code> otherwise
649 }
650 function TZPostgreSQLDatabaseInfo.SupportsIntegrityEnhancementFacility: Boolean;
651 begin
652  Result := False;
653 end;
654 
655 {**
656  What's the database vendor's preferred term for "schema"?
657  @return the vendor term
658 }
659 function TZPostgreSQLDatabaseInfo.GetSchemaTerm: string;
660 begin
661  Result := 'schema';
662 end;
663 
664 {**
665  What's the database vendor's preferred term for "procedure"?
666  @return the vendor term
667 }
668 function TZPostgreSQLDatabaseInfo.GetProcedureTerm: string;
669 begin
670  Result := 'function';
671 end;
672 
673 {**
674  What's the database vendor's preferred term for "catalog"?
675  @return the vendor term
676 }
677 function TZPostgreSQLDatabaseInfo.GetCatalogTerm: string;
678 begin
679  Result := 'database';
680 end;
681 
682 {**
683  What's the separator between catalog and table name?
684  @return the separator string
685 }
686 function TZPostgreSQLDatabaseInfo.GetCatalogSeparator: string;
687 begin
688  Result := '.';
689 end;
690 
691 {**
692  Can a schema name be used in a data manipulation statement?
693  @return <code>true</code> if so; <code>false</code> otherwise
694 }
695 function TZPostgreSQLDatabaseInfo.SupportsSchemasInDataManipulation: Boolean;
696 begin
697  Result := HasMinimumServerVersion(7, 3);
698 end;
699 
700 {**
701  Can a schema name be used in a procedure call statement?
702  @return <code>true</code> if so; <code>false</code> otherwise
703 }
704 function TZPostgreSQLDatabaseInfo.SupportsSchemasInProcedureCalls: Boolean;
705 begin
706  Result := HasMinimumServerVersion(7, 3);
707 end;
708 
709 {**
710  Can a schema name be used in a table definition statement?
711  @return <code>true</code> if so; <code>false</code> otherwise
712 }
713 function TZPostgreSQLDatabaseInfo.SupportsSchemasInTableDefinitions: Boolean;
714 begin
715  Result := HasMinimumServerVersion(7, 3);
716 end;
717 
718 {**
719  Can a schema name be used in an index definition statement?
720  @return <code>true</code> if so; <code>false</code> otherwise
721 }
722 function TZPostgreSQLDatabaseInfo.SupportsSchemasInIndexDefinitions: Boolean;
723 begin
724  Result := HasMinimumServerVersion(7, 3);
725 end;
726 
727 {**
728  Can a schema name be used in a privilege definition statement?
729  @return <code>true</code> if so; <code>false</code> otherwise
730 }
731 function TZPostgreSQLDatabaseInfo.SupportsSchemasInPrivilegeDefinitions: Boolean;
732 begin
733  Result := HasMinimumServerVersion(7, 3);
734 end;
735 
736 {**
737  Can a catalog name be used in a data manipulation statement?
738  @return <code>true</code> if so; <code>false</code> otherwise
739 }
740 function TZPostgreSQLDatabaseInfo.SupportsCatalogsInDataManipulation: Boolean;
741 begin
742  Result := False;
743 end;
744 
745 {**
746  Can a catalog name be used in a procedure call statement?
747  @return <code>true</code> if so; <code>false</code> otherwise
748 }
749 function TZPostgreSQLDatabaseInfo.SupportsCatalogsInProcedureCalls: Boolean;
750 begin
751  Result := HasMinimumServerVersion(7, 3);
752 end;
753 
754 {**
755  Can a catalog name be used in a table definition statement?
756  @return <code>true</code> if so; <code>false</code> otherwise
757 }
758 function TZPostgreSQLDatabaseInfo.SupportsCatalogsInTableDefinitions: Boolean;
759 begin
760  Result := HasMinimumServerVersion(7, 3);
761 end;
762 
763 {**
764  Can a catalog name be used in an index definition statement?
765  @return <code>true</code> if so; <code>false</code> otherwise
766 }
767 function TZPostgreSQLDatabaseInfo.SupportsCatalogsInIndexDefinitions: Boolean;
768 begin
769  Result := False;
770 end;
771 
772 {**
773  Can a catalog name be used in a privilege definition statement?
774  @return <code>true</code> if so; <code>false</code> otherwise
775 }
776 function TZPostgreSQLDatabaseInfo.SupportsCatalogsInPrivilegeDefinitions: Boolean;
777 begin
778  Result := False;
779 end;
780 
781 {**
782  Is positioned DELETE supported?
783  @return <code>true</code> if so; <code>false</code> otherwise
784 }
785 function TZPostgreSQLDatabaseInfo.SupportsPositionedDelete: Boolean;
786 begin
787  Result := False;
788 end;
789 
790 {**
791  Is positioned UPDATE supported?
792  @return <code>true</code> if so; <code>false</code> otherwise
793 }
794 function TZPostgreSQLDatabaseInfo.SupportsPositionedUpdate: Boolean;
795 begin
796  Result := False;
797 end;
798 
799 {**
800  Is SELECT for UPDATE supported?
801  @return <code>true</code> if so; <code>false</code> otherwise
802 }
803 function TZPostgreSQLDatabaseInfo.SupportsSelectForUpdate: Boolean;
804 begin
805  Result := HasMinimumServerVersion(6, 5);
806 end;
807 
808 {**
809  Are stored procedure calls using the stored procedure escape
810  syntax supported?
811  @return <code>true</code> if so; <code>false</code> otherwise
812 }
813 function TZPostgreSQLDatabaseInfo.SupportsStoredProcedures: Boolean;
814 begin
815  Result := False;
816 end;
817 
818 {**
819  Are subqueries in comparison expressions supported?
820  A JDBC Compliant<sup><font size=-2>TM</font></sup> driver always returns true.
821  @return <code>true</code> if so; <code>false</code> otherwise
822 }
823 function TZPostgreSQLDatabaseInfo.SupportsSubqueriesInComparisons: Boolean;
824 begin
825  Result := True;
826 end;
827 
828 {**
829  Are subqueries in 'exists' expressions supported?
830  A JDBC Compliant<sup><font size=-2>TM</font></sup> driver always returns true.
831  @return <code>true</code> if so; <code>false</code> otherwise
832 }
833 function TZPostgreSQLDatabaseInfo.SupportsSubqueriesInExists: Boolean;
834 begin
835  Result := True;
836 end;
837 
838 {**
839  Are subqueries in 'in' statements supported?
840  A JDBC Compliant<sup><font size=-2>TM</font></sup> driver always returns true.
841  @return <code>true</code> if so; <code>false</code> otherwise
842 }
843 function TZPostgreSQLDatabaseInfo.SupportsSubqueriesInIns: Boolean;
844 begin
845  Result := True;
846 end;
847 
848 {**
849  Are subqueries in quantified expressions supported?
850  A JDBC Compliant<sup><font size=-2>TM</font></sup> driver always returns true.
851  @return <code>true</code> if so; <code>false</code> otherwise
852 }
853 function TZPostgreSQLDatabaseInfo.SupportsSubqueriesInQuantifieds: Boolean;
854 begin
855  Result := True;
856 end;
857 
858 {**
859  Are correlated subqueries supported?
860  A JDBC Compliant<sup><font size=-2>TM</font></sup> driver always returns true.
861  @return <code>true</code> if so; <code>false</code> otherwise
862 }
863 function TZPostgreSQLDatabaseInfo.SupportsCorrelatedSubqueries: Boolean;
864 begin
865  Result := HasMinimumServerVersion(7, 1);
866 end;
867 
868 {**
869  Is SQL UNION supported?
870  @return <code>true</code> if so; <code>false</code> otherwise
871 }
872 function TZPostgreSQLDatabaseInfo.SupportsUnion: Boolean;
873 begin
874  Result := True;
875 end;
876 
877 {**
878  Is SQL UNION ALL supported?
879  @return <code>true</code> if so; <code>false</code> otherwise
880 }
881 function TZPostgreSQLDatabaseInfo.SupportsUnionAll: Boolean;
882 begin
883  Result := HasMinimumServerVersion(7, 1);
884 end;
885 
886 {**
887  Can cursors remain open across commits?
888  @return <code>true</code> if cursors always remain open;
889  <code>false</code> if they might not remain open
890 }
891 function TZPostgreSQLDatabaseInfo.SupportsOpenCursorsAcrossCommit: Boolean;
892 begin
893  Result := False;
894 end;
895 
896 {**
897  Can cursors remain open across rollbacks?
898  @return <code>true</code> if cursors always remain open;
899  <code>false</code> if they might not remain open
900 }
901 function TZPostgreSQLDatabaseInfo.SupportsOpenCursorsAcrossRollback: Boolean;
902 begin
903  Result := False;
904 end;
905 
906 {**
907  Can statements remain open across commits?
908  @return <code>true</code> if statements always remain open;
909  <code>false</code> if they might not remain open
910 }
911 function TZPostgreSQLDatabaseInfo.SupportsOpenStatementsAcrossCommit: Boolean;
912 begin
913  Result := True;
914 end;
915 
916 {**
917  Can statements remain open across rollbacks?
918  @return <code>true</code> if statements always remain open;
919  <code>false</code> if they might not remain open
920 }
921 function TZPostgreSQLDatabaseInfo.SupportsOpenStatementsAcrossRollback: Boolean;
922 begin
923  Result := True;
924 end;
925 
926 //----------------------------------------------------------------------
927 // The following group of methods exposes various limitations
928 // based on the target database with the current driver.
929 // Unless otherwise specified, a result of zero means there is no
930 // limit, or the limit is not known.
931 
932 {**
933  How many hex characters can you have in an inline binary literal?
934  @return max binary literal length in hex characters;
935  a result of zero means that there is no limit or the limit is not known
936 }
937 function TZPostgreSQLDatabaseInfo.GetMaxBinaryLiteralLength: Integer;
938 begin
939  Result := 0;
940 end;
941 
942 {**
943  What's the max length for a character literal?
944  @return max literal length;
945  a result of zero means that there is no limit or the limit is not known
946 }
947 function TZPostgreSQLDatabaseInfo.GetMaxCharLiteralLength: Integer;
948 begin
949  Result := 0;
950 end;
951 
952 {**
953  What's the limit on column name length?
954  @return max column name length;
955  a result of zero means that there is no limit or the limit is not known
956 }
957 function TZPostgreSQLDatabaseInfo.GetMaxColumnNameLength: Integer;
958 begin
959  Result := GetMaxNameLength;
960 end;
961 
962 {**
963  What's the maximum number of columns in a "GROUP BY" clause?
964  @return max number of columns;
965  a result of zero means that there is no limit or the limit is not known
966 }
967 function TZPostgreSQLDatabaseInfo.GetMaxColumnsInGroupBy: Integer;
968 begin
969  Result := 0;
970 end;
971 
972 {**
973  What's the maximum number of columns allowed in an index?
974  @return max number of columns;
975  a result of zero means that there is no limit or the limit is not known
976 }
977 function TZPostgreSQLDatabaseInfo.GetMaxColumnsInIndex: Integer;
978 begin
979  Result := GetMaxIndexKeys;
980 end;
981 
982 {**
983  What's the maximum number of columns in an "ORDER BY" clause?
984  @return max number of columns;
985  a result of zero means that there is no limit or the limit is not known
986 }
987 function TZPostgreSQLDatabaseInfo.GetMaxColumnsInOrderBy: Integer;
988 begin
989  Result := 0;
990 end;
991 
992 {**
993  What's the maximum number of columns in a "SELECT" list?
994  @return max number of columns;
995  a result of zero means that there is no limit or the limit is not known
996 }
997 function TZPostgreSQLDatabaseInfo.GetMaxColumnsInSelect: Integer;
998 begin
999  Result := 0;
1000 end;
1001 
1002 {**
1003  What's the maximum number of columns in a table?
1004  @return max number of columns;
1005  a result of zero means that there is no limit or the limit is not known
1006 }
1007 function TZPostgreSQLDatabaseInfo.GetMaxColumnsInTable: Integer;
1008 begin
1009  Result := 1600;
1010 end;
1011 
1012 {**
1013  How many active connections can we have at a time to this database?
1014  @return max number of active connections;
1015  a result of zero means that there is no limit or the limit is not known
1016 }
1017 function TZPostgreSQLDatabaseInfo.GetMaxConnections: Integer;
1018 begin
1019  Result := 8192;
1020 end;
1021 
1022 {**
1023  What's the maximum cursor name length?
1024  @return max cursor name length in bytes;
1025  a result of zero means that there is no limit or the limit is not known
1026 }
1027 function TZPostgreSQLDatabaseInfo.GetMaxCursorNameLength: Integer;
1028 begin
1029  Result := GetMaxNameLength;
1030 end;
1031 
1032 {**
1033  Retrieves the maximum number of bytes for an index, including all
1034  of the parts of the index.
1035  @return max index length in bytes, which includes the composite of all
1036  the constituent parts of the index;
1037  a result of zero means that there is no limit or the limit is not known
1038 }
1039 function TZPostgreSQLDatabaseInfo.GetMaxIndexLength: Integer;
1040 begin
1041  Result := 0;
1042 end;
1043 
1044 {**
1045  What's the maximum length allowed for a schema name?
1046  @return max name length in bytes;
1047  a result of zero means that there is no limit or the limit is not known
1048 }
1049 function TZPostgreSQLDatabaseInfo.GetMaxSchemaNameLength: Integer;
1050 begin
1051  Result := GetMaxNameLength;
1052 end;
1053 
1054 {**
1055  What's the maximum length of a procedure name?
1056  @return max name length in bytes;
1057  a result of zero means that there is no limit or the limit is not known
1058 }
1059 function TZPostgreSQLDatabaseInfo.GetMaxProcedureNameLength: Integer;
1060 begin
1061  Result := GetMaxNameLength;
1062 end;
1063 
1064 {**
1065  What's the maximum length of a catalog name?
1066  @return max name length in bytes;
1067  a result of zero means that there is no limit or the limit is not known
1068 }
1069 function TZPostgreSQLDatabaseInfo.GetMaxCatalogNameLength: Integer;
1070 begin
1071  Result := GetMaxNameLength;
1072 end;
1073 
1074 {**
1075  What's the maximum length of a single row?
1076  @return max row size in bytes;
1077  a result of zero means that there is no limit or the limit is not known
1078 }
1079 function TZPostgreSQLDatabaseInfo.GetMaxRowSize: Integer;
1080 begin
1081  if HasMinimumServerVersion(7, 1) then
1082  Result := 1073741824
1083  else Result := 8192;
1084 end;
1085 
1086 {**
1087  Did getMaxRowSize() include LONGVARCHAR and LONGVARBINARY
1088  blobs?
1089  @return <code>true</code> if so; <code>false</code> otherwise
1090 }
1091 function TZPostgreSQLDatabaseInfo.DoesMaxRowSizeIncludeBlobs: Boolean;
1092 begin
1093  Result := True;
1094 end;
1095 
1096 {**
1097  What's the maximum length of an SQL statement?
1098  @return max length in bytes;
1099  a result of zero means that there is no limit or the limit is not known
1100 }
1101 function TZPostgreSQLDatabaseInfo.GetMaxStatementLength: Integer;
1102 begin
1103  if HasMinimumServerVersion(7, 0) then
1104  Result := 0
1105  else Result := 16348
1106 end;
1107 
1108 {**
1109  How many active statements can we have open at one time to this
1110  database?
1111  @return the maximum number of statements that can be open at one time;
1112  a result of zero means that there is no limit or the limit is not known
1113 }
1114 function TZPostgreSQLDatabaseInfo.GetMaxStatements: Integer;
1115 begin
1116  Result := 1;
1117 end;
1118 
1119 {**
1120  What's the maximum length of a table name?
1121  @return max name length in bytes;
1122  a result of zero means that there is no limit or the limit is not known
1123 }
1124 function TZPostgreSQLDatabaseInfo.GetMaxTableNameLength: Integer;
1125 begin
1126  Result := GetMaxNameLength;
1127 end;
1128 
1129 {**
1130  What's the maximum number of tables in a SELECT statement?
1131  @return the maximum number of tables allowed in a SELECT statement;
1132  a result of zero means that there is no limit or the limit is not known
1133 }
1134 function TZPostgreSQLDatabaseInfo.GetMaxTablesInSelect: Integer;
1135 begin
1136  Result := 0;
1137 end;
1138 
1139 {**
1140  What's the maximum length of a user name?
1141  @return max user name length in bytes;
1142  a result of zero means that there is no limit or the limit is not known
1143 }
1144 function TZPostgreSQLDatabaseInfo.GetMaxUserNameLength: Integer;
1145 begin
1146  Result := GetMaxNameLength;
1147 end;
1148 
1149 //----------------------------------------------------------------------
1150 
1151 {**
1152  What's the database's default transaction isolation level? The
1153  values are defined in <code>java.sql.Connection</code>.
1154  @return the default isolation level
1155  @see Connection
1156 }
1157 function TZPostgreSQLDatabaseInfo.GetDefaultTransactionIsolation:
1158  TZTransactIsolationLevel;
1159 begin
1160  Result := tiReadCommitted;
1161 end;
1162 
1163 {**
1164  Are transactions supported? If not, invoking the method
1165  <code>commit</code> is a noop and the isolation level is TRANSACTION_NONE.
1166  @return <code>true</code> if transactions are supported; <code>false</code> otherwise
1167 }
1168 function TZPostgreSQLDatabaseInfo.SupportsTransactions: Boolean;
1169 begin
1170  Result := True;
1171 end;
1172 
1173 {**
1174  Does this database support the given transaction isolation level?
1175  @param level the values are defined in <code>java.sql.Connection</code>
1176  @return <code>true</code> if so; <code>false</code> otherwise
1177  @see Connection
1178 }
1179 function TZPostgreSQLDatabaseInfo.SupportsTransactionIsolationLevel(
1180  Level: TZTransactIsolationLevel): Boolean;
1181 begin
1182  Result := (Level = tiSerializable) or (Level = tiReadCommitted);
1183 end;
1184 
1185 {**
1186  Are both data definition and data manipulation statements
1187  within a transaction supported?
1188  @return <code>true</code> if so; <code>false</code> otherwise
1189 }
1190 function TZPostgreSQLDatabaseInfo.
1191  SupportsDataDefinitionAndDataManipulationTransactions: Boolean;
1192 begin
1193  Result := True;
1194 end;
1195 
1196 {**
1197  Are only data manipulation statements within a transaction
1198  supported?
1199  @return <code>true</code> if so; <code>false</code> otherwise
1200 }
1201 function TZPostgreSQLDatabaseInfo.
1202  SupportsDataManipulationTransactionsOnly: Boolean;
1203 begin
1204  Result := False;
1205 end;
1206 
1207 {**
1208  Does a data definition statement within a transaction force the
1209  transaction to commit?
1210  @return <code>true</code> if so; <code>false</code> otherwise
1211 }
1212 function TZPostgreSQLDatabaseInfo.DataDefinitionCausesTransactionCommit: Boolean;
1213 begin
1214  Result := False;
1215 end;
1216 
1217 {**
1218  Is a data definition statement within a transaction ignored?
1219  @return <code>true</code> if so; <code>false</code> otherwise
1220 }
1221 function TZPostgreSQLDatabaseInfo.DataDefinitionIgnoredInTransactions: Boolean;
1222 begin
1223  Result := False;
1224 end;
1225 
1226 {**
1227  Does the database support the given result set type?
1228  @param type defined in <code>java.sql.ResultSet</code>
1229  @return <code>true</code> if so; <code>false</code> otherwise
1230 }
1231 function TZPostgreSQLDatabaseInfo.SupportsResultSetType(
1232  _Type: TZResultSetType): Boolean;
1233 begin
1234  Result := _Type = rtScrollInsensitive;
1235 end;
1236 
1237 {**
1238  Does the database support the concurrency type in combination
1239  with the given result set type?
1240 
1241  @param type defined in <code>java.sql.ResultSet</code>
1242  @param concurrency type defined in <code>java.sql.ResultSet</code>
1243  @return <code>true</code> if so; <code>false</code> otherwise
1244 }
1245 function TZPostgreSQLDatabaseInfo.SupportsResultSetConcurrency(
1246  _Type: TZResultSetType; Concurrency: TZResultSetConcurrency): Boolean;
1247 begin
1248  Result := (_Type = rtScrollInsensitive) and (Concurrency = rcReadOnly);
1249 end;
1250 
1251 //----------------------------------------------------------------------
1252 // Additional functions.
1253 
1254 function TZPostgreSQLDatabaseInfo.HasMinimumServerVersion(
1255  MajorVersion: Integer; MinorVersion: Integer): Boolean;
1256 var
1257  PostgreSQLConnection: IZPostgreSQLConnection;
1258 begin
1259  PostgreSQLConnection := Metadata.GetConnection as IZPostgreSQLConnection;
1260  Result := (MajorVersion < PostgreSQLConnection.GetServerMajorVersion)
1261  or ((MajorVersion = PostgreSQLConnection.GetServerMajorVersion)
1262  and (MinorVersion <= PostgreSQLConnection.GetServerMinorVersion));
1263 end;
1264 
1265 function TZPostgreSQLDatabaseInfo.GetMaxIndexKeys: Integer;
1266 var
1267  SQL, From: string;
1268 begin
1269  if HasMinimumServerVersion(7, 3) then
1270  begin
1271  From := ' pg_catalog.pg_namespace n, pg_catalog.pg_type t1,'
1272  + ' pg_catalog.pg_type t2 WHERE t1.typnamespace=n.oid'
1273  + ' AND n.nspname=''pg_catalog'' AND ';
1274  end else
1275  From := ' pg_type t1, pg_type t2 WHERE ';
1276  SQL := ' SELECT t1.typlen/t2.typlen FROM ' + From
1277  + ' t1.typelem=t2.oid AND t1.typname=''oidvector'' ';
1278 
1279  with Metadata.GetConnection.CreateStatement.ExecuteQuery(SQL) do
1280  begin
1281  if not Next then
1282  raise Exception.Create(SUnknownError); //CHANGE IT!
1283  Result := GetInt(1);
1284  Close;
1285  end;
1286 end;
1287 
1288 function TZPostgreSQLDatabaseInfo.GetMaxNameLength: Integer;
1289 var
1290  SQL: string;
1291 begin
1292  if HasMinimumServerVersion(7, 3) then
1293  begin
1294  SQL := ' SELECT t.typlen FROM pg_catalog.pg_type t,'
1295  + ' pg_catalog.pg_namespace n WHERE t.typnamespace=n.oid'
1296  + ' AND t.typname=''name'' AND n.nspname=''pg_catalog'' ';
1297  end else
1298  SQL := ' SELECT typlen FROM pg_type WHERE typname=''name'' ';
1299 
1300  with Metadata.GetConnection.CreateStatement.ExecuteQuery(SQL) do
1301  begin
1302  if not Next then
1303  raise Exception.Create(SUnknownError); //CHANGE IT!
1304  Result := GetIntByName('typlen');
1305  Close;
1306  end;
1307 end;
1308 
1309 
1310 { TZPostgreSQLDatabaseMetadata }
1311 
1312 
1313 {**
1314  Destroys this object and cleanups the memory.
1315 }
1316 destructor TZPostgreSQLDatabaseMetadata.Destroy;
1317 begin
1318  inherited Destroy;
1319 end;
1320 
1321 {**
1322  Constructs a database information object and returns the interface to it. Used
1323  internally by the constructor.
1324  @return the database information object interface
1325 }
1326 function TZPostgreSQLDatabaseMetadata.CreateDatabaseInfo: IZDatabaseInfo;
1327 begin
1328  Result := TZPostgreSQLDatabaseInfo.Create(Self);
1329 end;
1330 
1331 {**
1332  @param S a string.
1333  @return escaped string
1334 }
1335 function TZPostgreSQLDatabaseMetadata.EscapeString(const S: string): string;
1336 var
1337  I: Integer;
1338 begin
1339  Result := S;
1340  for I := Length(Result) downto 1 do
1341  if (Result[I] = '''') or (Result[I] = '\') then
1342  Insert('\', Result, I);
1343  Result := '''' + Result + '''';
1344  if (GetDatabaseInfo as IZPostgreDBInfo).HasMinimumServerVersion(8, 1) then
1345  Result := 'E' + Result;
1346 end;
1347 
1348 function TZPostgreSQLDatabaseMetadata.GetRuleType(const Rule: String): TZImportedKey;
1349 begin
1350  if Rule = 'RESTRICT' then
1351  Result := ikRestrict
1352  else if Rule = 'NO ACTION' then
1353  Result := ikNoAction
1354  else if Rule = 'CASCADE' then
1355  Result := ikCascade
1356  else if Rule = 'SET DEFAULT' then
1357  Result := ikSetDefault
1358  else if Rule = 'SET NULL' then
1359  Result := ikSetNull
1360  else
1361  Result := ikNotDeferrable; //impossible!
1362 end;
1363 
1364 {**
1365  Gets a description of the stored procedures available in a
1366  catalog.
1367 
1368  <P>Only procedure descriptions matching the schema and
1369  procedure name criteria are returned. They are ordered by
1370  PROCEDURE_SCHEM, and PROCEDURE_NAME.
1371 
1372  <P>Each procedure description has the the following columns:
1373  <OL>
1374  <LI><B>PROCEDURE_CAT</B> String => procedure catalog (may be null)
1375  <LI><B>PROCEDURE_SCHEM</B> String => procedure schema (may be null)
1376  <LI><B>PROCEDURE_NAME</B> String => procedure name
1377  <LI> reserved for future use
1378  <LI> reserved for future use
1379  <LI> reserved for future use
1380  <LI><B>REMARKS</B> String => explanatory comment on the procedure
1381  <LI><B>PROCEDURE_TYPE</B> short => kind of procedure:
1382  <UL>
1383  <LI> procedureResultUnknown - May return a result
1384  <LI> procedureNoResult - Does not return a result
1385  <LI> procedureReturnsResult - Returns a result
1386  </UL>
1387  </OL>
1388 
1389  @param catalog a catalog name; "" retrieves those without a
1390  catalog; null means drop catalog name from the selection criteria
1391  @param schemaPattern a schema name pattern; "" retrieves those
1392  without a schema
1393  @param procedureNamePattern a procedure name pattern
1394  @return <code>ResultSet</code> - each row is a procedure description
1395  @see #getSearchStringEscape
1396 }
1397 function TZPostgreSQLDatabaseMetadata.UncachedGetProcedures(const Catalog: string;
1398  const SchemaPattern: string; const ProcedureNamePattern: string): IZResultSet;
1399 var
1400  SQL, ProcedureCondition, SchemaCondition: string;
1401 begin
1402  SchemaCondition := ConstructNameCondition(SchemaPattern,'n.nspname');
1403  ProcedureCondition := ConstructNameCondition(ProcedureNamePattern,'p.proname');
1404  if (GetDatabaseInfo as IZPostgreDBInfo).HasMinimumServerVersion(7, 3) then
1405  begin
1406  SQL := 'SELECT NULL AS PROCEDURE_CAT, n.nspname AS PROCEDURE_SCHEM,'
1407  + ' p.proname AS PROCEDURE_NAME, NULL AS RESERVED1, NULL AS RESERVED2,'
1408  + ' NULL AS RESERVED3, d.description AS REMARKS, '
1409  + IntToStr(ProcedureReturnsResult) + ' AS PROCEDURE_TYPE '
1410  + ' FROM pg_catalog.pg_namespace n, pg_catalog.pg_proc p '
1411  + ' LEFT JOIN pg_catalog.pg_description d ON (p.oid=d.objoid) '
1412  + ' LEFT JOIN pg_catalog.pg_class c ON (d.classoid=c.oid AND'
1413  + ' c.relname=''pg_proc'') LEFT JOIN pg_catalog.pg_namespace pn ON'
1414  + ' (c.relnamespace=pn.oid AND pn.nspname=''pg_catalog'') '
1415  + ' WHERE p.pronamespace=n.oid';
1416  if SchemaCondition <> '' then
1417  SQL := SQL + ' AND ' + Schemacondition;
1418  if ProcedureCondition <> '' then
1419  SQL := SQL + ' AND ' + ProcedureCondition;
1420  SQL := SQL + ' ORDER BY PROCEDURE_SCHEM, PROCEDURE_NAME';
1421  end
1422  else
1423  begin
1424  SQL := 'SELECT NULL AS PROCEDURE_CAT, NULL AS PROCEDURE_SCHEM,'
1425  + ' p.proname AS PROCEDURE_NAME, NULL AS RESERVED1, NULL AS RESERVED2,'
1426  + ' NULL AS RESERVED3, NULL AS REMARKS, '
1427  + IntToStr(ProcedureReturnsResult) + ' AS PROCEDURE_TYPE'
1428  + ' FROM pg_proc p';
1429  if ProcedureCondition <> '' then
1430  SQL := SQL + ' WHERE ' + ProcedureCondition;
1431  SQL := SQL + ' ORDER BY PROCEDURE_NAME';
1432  end;
1433 
1434  Result := CopyToVirtualResultSet(
1435  GetConnection.CreateStatement.ExecuteQuery(SQL),
1436  ConstructVirtualResultSet(ProceduresColumnsDynArray));
1437 end;
1438 
1439 {**
1440  Gets a description of a catalog's stored procedure parameters
1441  and result columns.
1442 
1443  <P>Only descriptions matching the schema, procedure and
1444  parameter name criteria are returned. They are ordered by
1445  PROCEDURE_SCHEM and PROCEDURE_NAME. Within this, the return value,
1446  if any, is first. Next are the parameter descriptions in call
1447  order. The column descriptions follow in column number order.
1448 
1449  <P>Each row in the <code>ResultSet</code> is a parameter description or
1450  column description with the following fields:
1451  <OL>
1452  <LI><B>PROCEDURE_CAT</B> String => procedure catalog (may be null)
1453  <LI><B>PROCEDURE_SCHEM</B> String => procedure schema (may be null)
1454  <LI><B>PROCEDURE_NAME</B> String => procedure name
1455  <LI><B>COLUMN_NAME</B> String => column/parameter name
1456  <LI><B>COLUMN_TYPE</B> Short => kind of column/parameter:
1457  <UL>
1458  <LI> procedureColumnUnknown - nobody knows
1459  <LI> procedureColumnIn - IN parameter
1460  <LI> procedureColumnInOut - INOUT parameter
1461  <LI> procedureColumnOut - OUT parameter
1462  <LI> procedureColumnReturn - procedure return value
1463  <LI> procedureColumnResult - result column in <code>ResultSet</code>
1464  </UL>
1465  <LI><B>DATA_TYPE</B> short => SQL type from java.sql.Types
1466  <LI><B>TYPE_NAME</B> String => SQL type name, for a UDT type the
1467  type name is fully qualified
1468  <LI><B>PRECISION</B> int => precision
1469  <LI><B>LENGTH</B> int => length in bytes of data
1470  <LI><B>SCALE</B> short => scale
1471  <LI><B>RADIX</B> short => radix
1472  <LI><B>NULLABLE</B> short => can it contain NULL?
1473  <UL>
1474  <LI> procedureNoNulls - does not allow NULL values
1475  <LI> procedureNullable - allows NULL values
1476  <LI> procedureNullableUnknown - nullability unknown
1477  </UL>
1478  <LI><B>REMARKS</B> String => comment describing parameter/column
1479  </OL>
1480 
1481  <P><B>Note:</B> Some databases may not return the column
1482  descriptions for a procedure. Additional columns beyond
1483  REMARKS can be defined by the database.
1484 
1485  @param catalog a catalog name; "" retrieves those without a
1486  catalog; null means drop catalog name from the selection criteria
1487  @param schemaPattern a schema name pattern; "" retrieves those
1488  without a schema
1489  @param procedureNamePattern a procedure name pattern
1490  @param columnNamePattern a column name pattern
1491  @return <code>ResultSet</code> - each row describes a stored procedure parameter or
1492  column
1493  @see #getSearchStringEscape
1494 }
1495 function TZPostgreSQLDatabaseMetadata.UncachedGetProcedureColumns(const Catalog: string;
1496  const SchemaPattern: string; const ProcedureNamePattern: string;
1497  const ColumnNamePattern: string): IZResultSet;
1498 
1499  procedure InsertProcedureColumnRow(AResultSet: IZResultSet;
1500  const ASchema, AProcedureName, AColumnName: string;
1501  const AColumnType, ADataType: integer; const ATypeName: string;
1502  const ANullable: integer);
1503  begin
1504  AResultSet.MoveToInsertRow;
1505  AResultSet.UpdateNullByName('PROCEDURE_CAT');
1506  AResultSet.UpdateStringByName('PROCEDURE_SCHEM', ASchema);
1507  AResultSet.UpdateStringByName('PROCEDURE_NAME', AProcedureName);
1508  AResultSet.UpdateStringByName('COLUMN_NAME', AColumnName);
1509  AResultSet.UpdateIntByName('COLUMN_TYPE', AColumnType);
1510  AResultSet.UpdateIntByName('DATA_TYPE', ADataType);
1511  AResultSet.UpdateStringByName('TYPE_NAME', ATypeName);
1512  AResultSet.UpdateNullByName('PRECISION');
1513  AResultSet.UpdateNullByName('LENGTH');
1514  AResultSet.UpdateNullByName('SCALE');
1515  AResultSet.UpdateNullByName('RADIX');
1516  AResultSet.UpdateIntByName('NULLABLE', ANullable);
1517  AResultSet.UpdateNullByName('REMARKS');
1518  AResultSet.InsertRow;
1519  end;
1520 
1521 var
1522  I, ReturnType, ColumnTypeOid, ArgOid: Integer;
1523  SQL, ReturnTypeType: string;
1524  IsInParam, IsOutParam: Boolean;
1525  ArgTypes, ArgNames, ArgModes: TStrings;
1526  Ver73Up, Ver80Up: Boolean;
1527  ResultSet: IZResultSet;
1528  ColumnsRS: IZResultSet;
1529  ArgMode: Char;
1530  OutParamCount: Integer;
1531  ColumnName: string;
1532  ColumnType: Integer;
1533  ProcedureCondition, SchemaCondition: string;
1534 begin
1535  SchemaCondition := ConstructNameCondition(SchemaPattern,'n.nspname');
1536  ProcedureCondition := ConstructNameCondition(ProcedureNamePattern,'p.proname');
1537  Result := inherited UncachedGetProcedureColumns(Catalog, SchemaPattern, ProcedureNamePattern, ColumnNamePattern);
1538 
1539  Ver80Up := (GetDatabaseInfo as IZPostgreDBInfo).HasMinimumServerVersion(8, 0);
1540  Ver73Up := Ver80Up or (GetDatabaseInfo as IZPostgreDBInfo).HasMinimumServerVersion(7, 3);
1541  if Ver80Up then
1542  begin
1543  SQL := 'SELECT n.nspname,p.proname,p.prorettype,p.proargtypes,t.typtype,'
1544  + 'p.proallargtypes,p.proargnames,p.proargmodes,t.typrelid '
1545  + 'FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n, pg_catalog.pg_type t '
1546  + 'WHERE p.pronamespace=n.oid AND p.prorettype=t.oid';
1547  if SchemaPattern <> '' then
1548  SQL := SQL + ' AND ' + SchemaCondition;
1549  if ProcedureNamePattern <> '' then
1550  SQL := SQL + ' AND ' + ProcedureCondition;
1551  SQL := SQL + ' ORDER BY n.nspname, p.proname';
1552  end
1553  else
1554  if Ver73Up then
1555  begin
1556  SQL := 'SELECT n.nspname,p.proname,p.prorettype,p.proargtypes,t.typtype,'
1557  + 'NULL AS proallargtypes,NULL AS proargnames,NULL AS proargnames,t.typrelid '
1558  + 'FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n,'
1559  + ' pg_catalog.pg_type t WHERE p.pronamespace=n.oid AND p.prorettype=t.oid';
1560  if SchemaPattern <> '' then
1561  SQL := SQL + ' AND ' + SchemaCondition;
1562  if ProcedureNamePattern <> '' then
1563  SQL := SQL + ' AND ' + ProcedureCondition;
1564  SQL := SQL + ' ORDER BY n.nspname, p.proname';
1565  end
1566  else
1567  begin
1568  SQL := 'SELECT NULL AS nspname,p.proname,p.prorettype,p.proargtypes,'
1569  + ' NULL AS proallargtypes,NULL AS proargnames,NULL AS proargnames,t.typtype,t.typrelid'
1570  + ' FROM pg_proc p, pg_type t'
1571  + ' WHERE p.prorettype=t.oid';
1572  if ProcedureNamePattern <> '' then
1573  SQL := SQL + ' AND ' + ProcedureCondition;
1574  SQL := SQL + ' ORDER BY p.proname';
1575  end;
1576 
1577  ArgTypes := TStringList.Create;
1578  ArgNames := TStringList.Create;
1579  ArgModes := TStringList.Create;
1580  try
1581  ResultSet := GetConnection.CreateStatement.ExecuteQuery(SQL); //FirmOS Patch
1582  with ResultSet do
1583  begin
1584  while Next do
1585  begin
1586  ReturnType := StrToInt(GetStringByName('prorettype'));
1587  ReturnTypeType := GetStringByName('typtype');
1588 
1589  ArgTypes.Clear;
1590  ArgNames.Clear;
1591  ArgModes.Clear;
1592 
1593  if (IsNullByName('proallargtypes')) then
1594  PutSplitString(ArgTypes, GetStringByName('proargtypes'), #10#13#9' ')
1595  else
1596  ParseACLArray(ArgTypes, GetStringByName('proallargtypes'));
1597  ParseACLArray(ArgNames, GetStringByName('proargnames'));
1598  ParseACLArray(ArgModes, GetStringByName('proargmodes'));
1599 
1600  OutParamCount := 0;
1601  for I := 0 to ArgTypes.Count - 1 do
1602  begin
1603  IsInParam := True;
1604  IsOutParam := False;
1605  if ArgModes.Count > I then
1606  begin
1607  ArgMode := ArgModes[I][1];
1608  IsInParam := CharInSet(ArgMode, ['i', 'b', 'v']);
1609  IsOutParam := CharInSet(ArgMode, ['o', 'b', 't']);
1610  end;
1611 
1612  if IsOutParam then
1613  Inc(OutParamCount);
1614 
1615  // column name
1616  ArgOid := StrToInt(ArgTypes.Strings[i]);
1617  if ArgNames.Count > I then
1618  ColumnName := ArgNames.Strings[I]
1619  else
1620  ColumnName := '$' + IntToStr(I + 1);
1621 
1622  // column type
1623  if IsInParam then
1624  begin
1625  if IsOutParam then
1626  ColumnType := Ord(pctInOut)
1627  else
1628  ColumnType := Ord(pctIn);
1629  end
1630  else
1631  begin
1632  if IsOutParam then
1633  ColumnType := Ord(pctOut)
1634  else
1635  ColumnType := Ord(pctUnknown);
1636  end;
1637 
1638  InsertProcedureColumnRow(Result, GetStringByName('nspname'),
1639  GetStringByName('proname'), ColumnName, ColumnType,
1640  Ord(GetSQLTypeByOid(ArgOid)), GetPostgreSQLType(ArgOid),
1641  Ord(ntNullableUnknown));
1642  end;
1643 
1644  if (OutParamCount > 0) then
1645  Continue;
1646 
1647  if (ReturnTypeType = 'c') then // Extract composit type columns
1648  begin
1649  ColumnsRS := GetConnection.CreateStatement.ExecuteQuery(
1650  Format('SELECT a.attname,a.atttypid'
1651  + ' FROM pg_catalog.pg_attribute a WHERE a.attrelid=%s'
1652  + ' ORDER BY a.attnum',
1653  [ResultSet.GetStringByName('typrelid')]));
1654  while ColumnsRS.Next do
1655  begin
1656  ColumnTypeOid := ColumnsRS.GetIntByName('atttypid');
1657  InsertProcedureColumnRow(Result, GetStringByName('nspname'),
1658  GetStringByName('proname'), ColumnsRS.GetStringByName('attname'),
1659  Ord(pctResultSet), Ord(GetSQLTypeByOid(ColumnTypeOid)),
1660  GetPostgreSQLType(ColumnTypeOid), Ord(ntNullableUnknown));
1661  end;
1662  ColumnsRS.Close;
1663  end
1664  else
1665  begin
1666  if (ReturnTypeType <> 'p') then // Single non-pseudotype return value
1667  begin
1668  InsertProcedureColumnRow(Result, GetStringByName('nspname'),
1669  GetStringByName('proname'), 'returnValue', Ord(pctReturn),
1670  Ord(GetSQLTypeByOid(ReturnType)), GetPostgreSQLType(ReturnType),
1671  Ord(ntNullableUnknown));
1672  end;
1673  end;
1674  end;
1675  Close;
1676  end;
1677  finally
1678  ArgTypes.Free;
1679  ArgNames.Free;
1680  ArgModes.Free;
1681  end;
1682 end;
1683 
1684 {**
1685  Gets a description of tables available in a catalog.
1686 
1687  <P>Only table descriptions matching the catalog, schema, table
1688  name and type criteria are returned. They are ordered by
1689  TABLE_TYPE, TABLE_SCHEM and TABLE_NAME.
1690 
1691  <P>Each table description has the following columns:
1692  <OL>
1693  <LI><B>TABLE_CAT</B> String => table catalog (may be null)
1694  <LI><B>TABLE_SCHEM</B> String => table schema (may be null)
1695  <LI><B>TABLE_NAME</B> String => table name
1696  <LI><B>TABLE_TYPE</B> String => table type. Typical types are "TABLE",
1697  "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY",
1698  "LOCAL TEMPORARY", "ALIAS", "SYNONYM".
1699  <LI><B>REMARKS</B> String => explanatory comment on the table
1700  </OL>
1701 
1702  <P><B>Note:</B> Some databases may not return information for
1703  all tables.
1704 
1705  @param catalog a catalog name; "" retrieves those without a
1706  catalog; null means drop catalog name from the selection criteria
1707  @param schemaPattern a schema name pattern; "" retrieves those
1708  without a schema
1709  @param tableNamePattern a table name pattern
1710  @param types a list of table types to include; null returns all types
1711  @return <code>ResultSet</code> - each row is a table description
1712  @see #getSearchStringEscape
1713 }
1714 function TZPostgreSQLDatabaseMetadata.UncachedGetTables(const Catalog: string;
1715  const SchemaPattern: string; const TableNamePattern: string;
1716  const Types: TStringDynArray): IZResultSet;
1717 var
1718  I: Integer;
1719  TableType, OrderBy, SQL: string;
1720  UseSchemas: Boolean;
1721  LTypes: TStringDynArray;
1722  TableNameCondition, SchemaCondition: string;
1723 begin
1724  SchemaCondition := ConstructNameCondition(SchemaPattern,'n.nspname');
1725  TableNameCondition := ConstructNameCondition(TableNamePattern,'c.relname');
1726  UseSchemas := True;
1727 
1728  if (GetDatabaseInfo as IZPostgreDBInfo).HasMinimumServerVersion(7, 3) then
1729  begin
1730  SQL := ' SELECT NULL AS TABLE_CAT, n.nspname AS TABLE_SCHEM,'
1731  + ' c.relname AS TABLE_NAME, '
1732  + ' CASE (n.nspname LIKE ''pg\\_%'')'
1733  + ' OR (n.nspname=''information_schema'')'
1734  + ' WHEN true THEN CASE n.nspname '
1735  + ' WHEN ''pg_catalog'' THEN CASE c.relkind '
1736  + ' WHEN ''r'' THEN ''SYSTEM TABLE'''
1737  + ' WHEN ''v'' THEN ''SYSTEM VIEW'' '
1738  + ' WHEN ''i'' THEN ''SYSTEM INDEX'' '
1739  + ' ELSE NULL '
1740  + ' END '
1741  + ' WHEN ''information_schema'' THEN CASE c.relkind '
1742  + ' WHEN ''r'' THEN ''SYSTEM TABLE'''
1743  + ' WHEN ''v'' THEN ''SYSTEM VIEW'' '
1744  + ' WHEN ''i'' THEN ''SYSTEM INDEX'' '
1745  + ' ELSE NULL '
1746  + ' END '
1747  + ' WHEN ''pg_toast'' THEN CASE c.relkind '
1748  + ' WHEN ''r'' THEN ''SYSTEM TOAST TABLE'' '
1749  + ' WHEN ''i'' THEN ''SYSTEM TOAST INDEX'' '
1750  + ' ELSE NULL '
1751  + ' END '
1752  + ' ELSE CASE c.relkind '
1753  + ' WHEN ''r'' THEN ''TEMPORARY TABLE'' '
1754  + ' WHEN ''i'' THEN ''TEMPORARY INDEX'' '
1755  + ' ELSE NULL '
1756  + ' END '
1757  + ' END '
1758  + ' WHEN false THEN CASE c.relkind '
1759  + ' WHEN ''r'' THEN ''TABLE'' '
1760  + ' WHEN ''i'' THEN ''INDEX'' '
1761  + ' WHEN ''S'' THEN ''SEQUENCE'' '
1762  + ' WHEN ''v'' THEN ''VIEW'' '
1763  + ' ELSE NULL '
1764  + ' END '
1765  + ' ELSE NULL '
1766  + ' END '
1767  + ' AS TABLE_TYPE, d.description AS REMARKS '
1768  + ' FROM pg_catalog.pg_namespace n, pg_catalog.pg_class c '
1769  + ' LEFT JOIN pg_catalog.pg_description d'
1770  + ' ON (c.oid = d.objoid AND d.objsubid = 0) '
1771  + ' LEFT JOIN pg_catalog.pg_class dc ON (d.classoid=dc.oid'
1772  + ' AND dc.relname=''pg_class'') LEFT JOIN pg_catalog.pg_namespace dn'
1773  + ' ON (dn.oid=dc.relnamespace AND dn.nspname=''pg_catalog'') '
1774  + ' WHERE c.relnamespace = n.oid ';
1775  if SchemaPattern <> '' then
1776  begin
1777  SQL := SQL + ' AND ' + SchemaCondition;
1778  end;
1779  OrderBy := ' ORDER BY TABLE_TYPE,TABLE_SCHEM,TABLE_NAME';
1780  end
1781  else
1782  begin
1783  UseSchemas := False;
1784  TableType := ' CASE c.relname LIKE ''pg\\_%'' '
1785  + 'WHEN true THEN CASE c.relname LIKE ''pg\\_toast\\_%'' '
1786  + 'WHEN true THEN CASE c.relkind '
1787  + ' WHEN ''r'' THEN ''SYSTEM TOAST TABLE'' '
1788  + ' WHEN ''i'' THEN ''SYSTEM TOAST INDEX'' '
1789  + ' ELSE NULL '
1790  + 'END '
1791  + 'WHEN false THEN CASE c.relname LIKE ''pg\\_temp\\_%'' '
1792  + ' WHEN true THEN CASE c.relkind '
1793  + ' WHEN ''r'' THEN ''TEMPORARY TABLE'' '
1794  + ' WHEN ''i'' THEN ''TEMPORARY INDEX'' '
1795  + ' ELSE NULL '
1796  + ' END '
1797  + ' WHEN false THEN CASE c.relkind '
1798  + ' WHEN ''r'' THEN ''SYSTEM TABLE'' '
1799  + ' WHEN ''v'' THEN ''SYSTEM VIEW'' '
1800  + ' WHEN ''i'' THEN ''SYSTEM INDEX'' '
1801  + ' ELSE NULL '
1802  + ' END '
1803  + ' ELSE NULL '
1804  + 'END '
1805  + 'ELSE NULL '
1806  + 'END '
1807  + 'WHEN false THEN CASE c.relkind '
1808  + ' WHEN ''r'' THEN ''TABLE'' '
1809  + ' WHEN ''i'' THEN ''INDEX'' '
1810  + ' WHEN ''S'' THEN ''SEQUENCE'' '
1811  + ' WHEN ''v'' THEN ''VIEW'' '
1812  + ' ELSE NULL '
1813  + 'END '
1814  + 'ELSE NULL '
1815  + ' END ';
1816  OrderBy := ' ORDER BY TABLE_TYPE,TABLE_NAME ';
1817  SQL := 'SELECT NULL AS TABLE_CAT, NULL AS TABLE_SCHEM,'
1818  + ' c.relname AS TABLE_NAME, ' + TableType + ' AS TABLE_TYPE,'
1819  + ' NULL AS REMARKS FROM pg_class c WHERE true ';
1820  end;
1821 
1822  if (Types = nil) or (Length(Types) = 0) then
1823  begin
1824  SetLength(LTypes, 3);
1825  // SetLength(LTypes, 6);
1826  LTypes[0] := 'TABLE';
1827  LTypes[1] := 'VIEW';
1828  LTypes[2] := 'TEMPORARY TABLE';
1829  // LTypes[3] := 'SYSTEM TABLE';
1830  // LTypes[4] := 'SYSTEM TOAST TABLE';
1831  // LTypes[5] := 'SYSTEM VIEW';
1832  end
1833  else
1834  LTypes := Types;
1835 
1836  If TableNameCondition <> '' then
1837  SQL := SQL + ' AND ' + TableNameCondition;
1838 
1839  SQL := SQL + ' AND (false';
1840  for I := 0 to High(LTypes) do
1841  SQL := SQL + ' OR (' + TableTypeSQLExpression(LTypes[i], UseSchemas) + ')';
1842  SQL := SQL + ')' + OrderBy;
1843 
1844  Result := CopyToVirtualResultSet(
1845  GetConnection.CreateStatement.ExecuteQuery(SQL),
1846  ConstructVirtualResultSet(TableColumnsDynArray));
1847 end;
1848 
1849 {**
1850  Gets the schema names available in this database. The results
1851  are ordered by schema name.
1852 
1853  <P>The schema column is:
1854  <OL>
1855  <LI><B>TABLE_SCHEM</B> String => schema name
1856  </OL>
1857 
1858  @return <code>ResultSet</code> - each row has a single String column that is a
1859  schema name
1860 }
1861 function TZPostgreSQLDatabaseMetadata.UncachedGetSchemas: IZResultSet;
1862 var
1863  SQL: string;
1864 begin
1865  if (GetDatabaseInfo as IZPostgreDBInfo).HasMinimumServerVersion(7, 3) then
1866  begin
1867  SQL := 'SELECT nspname AS TABLE_SCHEM FROM pg_catalog.pg_namespace'
1868  + ' WHERE nspname <> ''pg_toast'' AND nspname NOT'
1869  + ' LIKE ''pg\\_temp\\_%'' ORDER BY TABLE_SCHEM';
1870  end else
1871  SQL := 'SELECT ''''::text AS TABLE_SCHEM ORDER BY TABLE_SCHEM';
1872 
1873  Result := CopyToVirtualResultSet(
1874  GetConnection.CreateStatement.ExecuteQuery(SQL),
1875  ConstructVirtualResultSet(SchemaColumnsDynArray));
1876 end;
1877 
1878 {**
1879  Gets the catalog names available in this database. The results
1880  are ordered by catalog name.
1881 
1882  <P>The catalog column is:
1883  <OL>
1884  <LI><B>TABLE_CAT</B> String => catalog name
1885  </OL>
1886 
1887  @return <code>ResultSet</code> - each row has a single String column that is a
1888  catalog name
1889 }
1890 function TZPostgreSQLDatabaseMetadata.UncachedGetCatalogs: IZResultSet;
1891 var
1892  SQL: string;
1893 begin
1894  if (GetDatabaseInfo as IZPostgreDBInfo).HasMinimumServerVersion(7, 3) then
1895  begin
1896  SQL := 'SELECT datname AS TABLE_CAT FROM pg_catalog.pg_database'
1897  + ' ORDER BY TABLE_CAT';
1898  end else
1899  SQL := 'SELECT datname AS TABLE_CAT FROM pg_database ORDER BY TABLE_CAT';
1900 
1901  Result := CopyToVirtualResultSet(
1902  GetConnection.CreateStatement.ExecuteQuery(SQL),
1903  ConstructVirtualResultSet(CatalogColumnsDynArray));
1904 end;
1905 
1906 {**
1907  Gets the table types available in this database. The results
1908  are ordered by table type.
1909 
1910  <P>The table type is:
1911  <OL>
1912  <LI><B>TABLE_TYPE</B> String => table type. Typical types are "TABLE",
1913  "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY",
1914  "LOCAL TEMPORARY", "ALIAS", "SYNONYM".
1915  </OL>
1916 
1917  @return <code>ResultSet</code> - each row has a single String column that is a
1918  table type
1919 }
1920 function TZPostgreSQLDatabaseMetadata.UncachedGetTableTypes: IZResultSet;
1921 const
1922  Types: array [0..10] of string = ('TABLE', 'VIEW', 'INDEX',
1923  'SEQUENCE', 'SYSTEM TABLE', 'SYSTEM TOAST TABLE',
1924  'SYSTEM TOAST INDEX', 'SYSTEM VIEW', 'SYSTEM INDEX',
1925  'TEMPORARY TABLE', 'TEMPORARY INDEX');
1926 var
1927  I: Integer;
1928 begin
1929  Result:=inherited UncachedGetTableTypes;
1930 
1931  for I := 0 to 10 do
1932  begin
1933  Result.MoveToInsertRow;
1934  Result.UpdateString(1, Types[I]);
1935  Result.InsertRow;
1936  end;
1937 end;
1938 
1939 {**
1940  Gets a description of table columns available in
1941  the specified catalog.
1942 
1943  <P>Only column descriptions matching the catalog, schema, table
1944  and column name criteria are returned. They are ordered by
1945  TABLE_SCHEM, TABLE_NAME and ORDINAL_POSITION.
1946 
1947  <P>Each column description has the following columns:
1948  <OL>
1949  <LI><B>TABLE_CAT</B> String => table catalog (may be null)
1950  <LI><B>TABLE_SCHEM</B> String => table schema (may be null)
1951  <LI><B>TABLE_NAME</B> String => table name
1952  <LI><B>COLUMN_NAME</B> String => column name
1953  <LI><B>DATA_TYPE</B> short => SQL type from java.sql.Types
1954  <LI><B>TYPE_NAME</B> String => Data source dependent type name,
1955  for a UDT the type name is fully qualified
1956  <LI><B>COLUMN_SIZE</B> int => column size. For char or date
1957  types this is the maximum number of characters, for numeric or
1958  decimal types this is precision.
1959  <LI><B>BUFFER_LENGTH</B> is not used.
1960  <LI><B>DECIMAL_DIGITS</B> int => the number of fractional digits
1961  <LI><B>NUM_PREC_RADIX</B> int => Radix (typically either 10 or 2)
1962  <LI><B>NULLABLE</B> int => is NULL allowed?
1963  <UL>
1964  <LI> columnNoNulls - might not allow NULL values
1965  <LI> columnNullable - definitely allows NULL values
1966  <LI> columnNullableUnknown - nullability unknown
1967  </UL>
1968  <LI><B>REMARKS</B> String => comment describing column (may be null)
1969  <LI><B>COLUMN_DEF</B> String => default value (may be null)
1970  <LI><B>SQL_DATA_TYPE</B> int => unused
1971  <LI><B>SQL_DATETIME_SUB</B> int => unused
1972  <LI><B>CHAR_OCTET_LENGTH</B> int => for char types the
1973  maximum number of bytes in the column
1974  <LI><B>ORDINAL_POSITION</B> int => index of column in table
1975  (starting at 1)
1976  <LI><B>IS_NULLABLE</B> String => "NO" means column definitely
1977  does not allow NULL values; "YES" means the column might
1978  allow NULL values. An empty string means nobody knows.
1979  </OL>
1980 
1981  @param catalog a catalog name; "" retrieves those without a
1982  catalog; null means drop catalog name from the selection criteria
1983  @param schemaPattern a schema name pattern; "" retrieves those
1984  without a schema
1985  @param tableNamePattern a table name pattern
1986  @param columnNamePattern a column name pattern
1987  @return <code>ResultSet</code> - each row is a column description
1988  @see #getSearchStringEscape
1989 }
1990 function TZPostgreSQLDatabaseMetadata.UncachedGetColumns(const Catalog: string;
1991  const SchemaPattern: string; const TableNamePattern: string;
1992  const ColumnNamePattern: string): IZResultSet;
1993 var
1994  TypeOid, AttTypMod: Integer;
1995  SQL, PgType: string;
1996  SQLType: TZSQLType;
1997  CheckVisibility: Boolean;
1998  ColumnNameCondition, TableNameCondition, SchemaCondition: string;
1999 begin
2000  CheckVisibility := (GetConnection as IZPostgreSQLConnection).CheckFieldVisibility; //http://zeoslib.sourceforge.net/viewtopic.php?f=40&t=11174
2001  SchemaCondition := ConstructNameCondition(SchemaPattern,'n.nspname');
2002  TableNameCondition := ConstructNameCondition(TableNamePattern,'c.relname');
2003  ColumnNameCondition := ConstructNameCondition(ColumnNamePattern,'a.attname');
2004  Result:=inherited UncachedGetColumns(Catalog, SchemaPattern, TableNamePattern, ColumnNamePattern);
2005 
2006  if (GetDatabaseInfo as IZPostgreDBInfo).HasMinimumServerVersion(7, 3) then
2007  begin
2008  SQL := 'SELECT n.nspname,' {nspname_index}
2009  + 'c.relname,' {relname_index}
2010  + 'a.attname,' {attname_index}
2011  + 'a.atttypid,' {atttypid_index}
2012  + 'a.attnotnull,' {attnotnull_index}
2013  + 'a.atttypmod,' {atttypmod_index}
2014  + 'a.attlen,' {attlen_index}
2015  + 'a.attnum,' {attnum_index}
2016  + 'pg_get_expr(def.adbin, def.adrelid) as adsrc,' {adsrc_index}
2017  + 'dsc.description ' {description_index}
2018  + ' FROM pg_catalog.pg_namespace n '
2019  + ' JOIN pg_catalog.pg_class c ON (c.relnamespace = n.oid) '
2020  + ' JOIN pg_catalog.pg_attribute a ON (a.attrelid=c.oid) '
2021  + ' LEFT JOIN pg_catalog.pg_attrdef def ON (a.attrelid=def.adrelid'
2022  + ' AND a.attnum = def.adnum) LEFT JOIN pg_catalog.pg_description dsc'
2023  + ' ON (c.oid=dsc.objoid AND a.attnum = dsc.objsubid) '
2024  + ' LEFT JOIN pg_catalog.pg_class dc ON (dc.oid=dsc.classoid'
2025  + ' AND dc.relname=''pg_class'') LEFT JOIN pg_catalog.pg_namespace dn'
2026  + ' ON (dc.relnamespace=dn.oid AND dn.nspname=''pg_catalog'') '
2027  + ' WHERE a.attnum > 0 AND NOT a.attisdropped';
2028  if SchemaPattern <> '' then
2029  SQL := SQL + ' AND ' + SchemaCondition
2030  else
2031  //not by default: because of Speed decrease: http://http://zeoslib.sourceforge.net/viewtopic.php?p=16646&sid=130
2032  if CheckVisibility then
2033  SQL := SQL + ' AND pg_table_is_visible (c.oid) ';
2034  end
2035  else
2036  begin
2037  SQL := 'SELECT NULL::text AS nspname,' {1}
2038  + 'c.relname,' {2}
2039  + 'a.attname,' {3}
2040  + 'a.atttypid,' {4}
2041  + 'a.attnotnull,' {5}
2042  + 'a.atttypmod,' {6}
2043  + 'a.attlen,' {7}
2044  + 'a.attnum,' {8}
2045  + 'NULL AS adsrc,' {9}
2046  + 'NULL AS description' {10}
2047  + 'FROM pg_class c, pg_attribute a '
2048  + ' WHERE a.attrelid=c.oid AND a.attnum > 0 ';
2049  end;
2050 
2051  If TableNameCondition <> '' then
2052  SQL := SQL + ' AND ' + TableNameCondition;
2053  If ColumnNameCondition <> '' then
2054  SQL := SQL+ ' AND ' + ColumnNameCondition;
2055  SQL := SQL+ ' ORDER BY nspname,relname,attnum';
2056 
2057  with GetConnection.CreateStatement.ExecuteQuery(SQL) do
2058  begin
2059  while Next do
2060  begin
2061  AttTypMod := GetInt(6 {atttypmod});
2062 
2063  TypeOid := GetInt(4 {atttypid});
2064  PgType := GetPostgreSQLType(TypeOid);
2065 
2066  Result.MoveToInsertRow;
2067  Result.UpdateNull(1);
2068  Result.UpdateString(2, GetString(1 {nspname}));
2069  Result.UpdateString(3, GetString(2 {relname}));
2070  Result.UpdateString(4, GetString(3 {attname}));
2071  SQLType := GetSQLTypeByOid(TypeOid);
2072  Result.UpdateInt(5, Ord(SQLType));
2073  Result.UpdateString(6, PgType);
2074  Result.UpdateInt(8, 0);
2075 
2076  if (PgType = 'bpchar') or (PgType = 'varchar') or (PgType = 'enum') then
2077  begin
2078  if AttTypMod <> -1 then
2079  Result.UpdateInt(7, GetFieldSize(SQLType, ConSettings, (AttTypMod - 4),
2080  ConSettings.ClientCodePage.CharWidth))
2081  else
2082  if (PgType = 'varchar') then
2083  if ( (GetConnection as IZPostgreSQLConnection).GetUndefinedVarcharAsStringLength = 0 ) then
2084  begin
2085  Result.UpdateInt(5, Ord(GetSQLTypeByOid(25))); //Assume text-lob instead
2086  Result.UpdateInt(7, 0); // need no size for streams
2087  end
2088  else //keep the string type but with user defined count of chars
2089  Result.UpdateInt(7, (GetConnection as IZPostgreSQLConnection).GetUndefinedVarcharAsStringLength )
2090  else
2091  Result.UpdateInt(7, 0);
2092  end
2093  else if (PgType = 'numeric') or (PgType = 'decimal') then
2094  begin
2095  Result.UpdateInt(7, ((AttTypMod - 4) div 65536)); //precision
2096  Result.UpdateInt(9, ((AttTypMod -4) mod 65536)); //scale
2097  Result.UpdateInt(10, 10); //base? ten as default
2098  end
2099  else if (PgType = 'bit') or (PgType = 'varbit') then
2100  begin
2101  Result.UpdateInt(7, AttTypMod);
2102  Result.UpdateInt(10, 2);
2103  end
2104  else
2105  begin
2106  Result.UpdateInt(7, GetInt(7 {attlen}));
2107  Result.UpdateInt(10, 2);
2108  end;
2109 
2110  Result.UpdateNull(8);
2111  if GetBoolean(5 {attnotnull}) then
2112  begin
2113  Result.UpdateString(18, 'NO');
2114  Result.UpdateInt(11, Ord(ntNoNulls));
2115  end
2116  else
2117  begin
2118  Result.UpdateString(18, 'YES');
2119  Result.UpdateInt(11, Ord(ntNullable));
2120  end;
2121 
2122  Result.UpdateString(12, GetString(10 {description}));
2123  Result.UpdateString(13, GetString(9 {adsrc}));
2124  Result.UpdateNull(14);
2125  Result.UpdateNull(15);
2126  Result.UpdateInt(16, Result.GetInt(7));
2127  Result.UpdateInt(17, GetInt(8 {attnum}));
2128 
2129  Result.UpdateNullByName('AUTO_INCREMENT');
2130  Result.UpdateBooleanByName('CASE_SENSITIVE',
2131  IC.IsCaseSensitive(GetString(3 {attname})));
2132  Result.UpdateBooleanByName('SEARCHABLE', True);
2133  Result.UpdateBooleanByName('WRITABLE', True);
2134  Result.UpdateBooleanByName('DEFINITELYWRITABLE', True);
2135  Result.UpdateBooleanByName('READONLY', False);
2136 
2137  Result.InsertRow;
2138  end;
2139  Close;
2140  end;
2141 end;
2142 
2143 {**
2144  Gets a description of the access rights for a table's columns.
2145 
2146  <P>Only privileges matching the column name criteria are
2147  returned. They are ordered by COLUMN_NAME and PRIVILEGE.
2148 
2149  <P>Each privilige description has the following columns:
2150  <OL>
2151  <LI><B>TABLE_CAT</B> String => table catalog (may be null)
2152  <LI><B>TABLE_SCHEM</B> String => table schema (may be null)
2153  <LI><B>TABLE_NAME</B> String => table name
2154  <LI><B>COLUMN_NAME</B> String => column name
2155  <LI><B>GRANTOR</B> => grantor of access (may be null)
2156  <LI><B>GRANTEE</B> String => grantee of access
2157  <LI><B>PRIVILEGE</B> String => name of access (SELECT,
2158  INSERT, UPDATE, REFRENCES, ...)
2159  <LI><B>IS_GRANTABLE</B> String => "YES" if grantee is permitted
2160  to grant to others; "NO" if not; null if unknown
2161  </OL>
2162 
2163  @param catalog a catalog name; "" retrieves those without a
2164  catalog; null means drop catalog name from the selection criteria
2165  @param schema a schema name; "" retrieves those without a schema
2166  @param table a table name
2167  @param columnNamePattern a column name pattern
2168  @return <code>ResultSet</code> - each row is a column privilege description
2169  @see #getSearchStringEscape
2170 }
2171 function TZPostgreSQLDatabaseMetadata.UncachedGetColumnPrivileges(const Catalog: string;
2172  const Schema: string; const Table: string; const ColumnNamePattern: string): IZResultSet;
2173 var
2174  I, J: Integer;
2175  SQL, Column, Owner: string;
2176  Privileges, Grantable, Grantee: string;
2177  Permissions, PermissionsExp: TStrings;
2178  ColumnNameCondition, TableNameCondition, SchemaCondition: string;
2179 begin
2180  SchemaCondition := ConstructNameCondition(Schema,'n.nspname');
2181  TableNameCondition := ConstructNameCondition(Table,'c.relname');
2182  ColumnNameCondition := ConstructNameCondition(ColumnNamePattern,'a.attname');
2183  Result:=inherited UncachedGetColumnPrivileges(Catalog, Schema, Table, ColumnNamePattern);
2184 
2185  if (GetDatabaseInfo as IZPostgreDBInfo).HasMinimumServerVersion(7, 3) then
2186  begin
2187  SQL := 'SELECT n.nspname,c.relname,u.usename,c.relacl,a.attname '
2188  + ' FROM pg_catalog.pg_namespace n, pg_catalog.pg_class c,'
2189  + ' pg_catalog.pg_user u, pg_catalog.pg_attribute a '
2190  + ' WHERE c.relnamespace = n.oid AND u.usesysid = c.relowner '
2191  + ' AND c.oid = a.attrelid AND c.relkind = ''r'''
2192  + ' AND a.attnum > 0 AND NOT a.attisdropped';
2193  if Schema <> '' then
2194  SQL := SQL + ' AND ' + SchemaCondition;
2195  end
2196  else
2197  begin
2198  SQL := 'SELECT NULL::text AS nspname,c.relname,u.usename,c.relacl,'
2199  + 'a.attname FROM pg_class c, pg_user u,pg_attribute a '
2200  + ' WHERE u.usesysid = c.relowner AND c.oid = a.attrelid '
2201  + ' AND a.attnum > 0 AND c.relkind = ''r''';
2202  end;
2203 
2204  If TableNameCondition <> '' then
2205  SQL := SQL + ' AND ' + TableNameCondition;
2206  If ColumnNameCondition <> '' then
2207  SQL := SQL + ' AND '+ ColumnNameCondition;
2208  SQL := SQL + ' ORDER BY attname';
2209 
2210  Permissions := TStringList.Create;
2211  PermissionsExp := TStringList.Create;
2212  try
2213  with GetConnection.CreateStatement.ExecuteQuery(SQL) do
2214  begin
2215  while Next do
2216  begin
2217  //SchemaName := GetStringByName('nspname');
2218  //TableName := GetStringByName('relname');
2219  Column := GetStringByName('attname');
2220  Owner := GetStringByName('usename');
2221  Permissions.Clear;
2222  ParseACLArray(Permissions, GetStringByName('relacl'));
2223  for I := 0 to Permissions.Count-1 do
2224  begin
2225  PutSplitString(PermissionsExp, Permissions.Strings[I], '=');
2226  if PermissionsExp.Count < 2 then
2227  Continue;
2228  Grantee := PermissionsExp.Strings[0];
2229  if Grantee = '' then
2230  Grantee := 'PUBLIC';
2231  Privileges := PermissionsExp.Strings[1];
2232  for J := 1 to Length(Privileges) do
2233  begin
2234  if Owner = Grantee then
2235  Grantable := 'YES'
2236  else Grantable := 'NO';
2237  Result.MoveToInsertRow;
2238  Result.UpdateNull(1);
2239  Result.UpdateString(2, Schema);
2240  Result.UpdateString(3, Table);
2241  Result.UpdateString(4, Column);
2242  Result.UpdateString(5, Owner);
2243  Result.UpdateString(6, Grantee);
2244  Result.UpdateString(7, GetPrivilegeName(Privileges[J]));
2245  Result.UpdateString(8, grantable);
2246  Result.InsertRow;
2247  end;
2248  end;
2249  end;
2250  Close;
2251  end;
2252  finally
2253  Permissions.Free;
2254  PermissionsExp.Free;
2255  end;
2256 end;
2257 
2258 {**
2259  Gets a description of the access rights for each table available
2260  in a catalog. Note that a table privilege applies to one or
2261  more columns in the table. It would be wrong to assume that
2262  this priviledge applies to all columns (this may be true for
2263  some systems but is not true for all.)
2264 
2265  <P>Only privileges matching the schema and table name
2266  criteria are returned. They are ordered by TABLE_SCHEM,
2267  TABLE_NAME, and PRIVILEGE.
2268 
2269  <P>Each privilige description has the following columns:
2270  <OL>
2271  <LI><B>TABLE_CAT</B> String => table catalog (may be null)
2272  <LI><B>TABLE_SCHEM</B> String => table schema (may be null)
2273  <LI><B>TABLE_NAME</B> String => table name
2274  <LI><B>GRANTOR</B> => grantor of access (may be null)
2275  <LI><B>GRANTEE</B> String => grantee of access
2276  <LI><B>PRIVILEGE</B> String => name of access (SELECT,
2277  INSERT, UPDATE, REFRENCES, ...)
2278  <LI><B>IS_GRANTABLE</B> String => "YES" if grantee is permitted
2279  to grant to others; "NO" if not; null if unknown
2280  </OL>
2281 
2282  @param catalog a catalog name; "" retrieves those without a
2283  catalog; null means drop catalog name from the selection criteria
2284  @param schemaPattern a schema name pattern; "" retrieves those
2285  without a schema
2286  @param tableNamePattern a table name pattern
2287  @return <code>ResultSet</code> - each row is a table privilege description
2288  @see #getSearchStringEscape
2289 }
2290 function TZPostgreSQLDatabaseMetadata.UncachedGetTablePrivileges(const Catalog: string;
2291  const SchemaPattern: string; const TableNamePattern: string): IZResultSet;
2292 var
2293  I, J: Integer;
2294  SQL, SchemaName, TableName, Owner: string;
2295  Privileges, Grantable, Grantee: string;
2296  Permissions, PermissionsExp: TStringList;
2297  TableNameCondition, SchemaCondition: string;
2298 begin
2299  SchemaCondition := ConstructNameCondition(SchemaPattern,'n.nspname');
2300  TableNameCondition := ConstructNameCondition(TableNamePattern,'c.relname');
2301  Result:=inherited UncachedGetTablePrivileges(Catalog, SchemaPattern, TableNamePattern);
2302 
2303  if (GetDatabaseInfo as IZPostgreDBInfo).HasMinimumServerVersion(7, 3) then
2304  begin
2305  SQL := 'SELECT n.nspname,c.relname,u.usename,c.relacl '
2306  + ' FROM pg_catalog.pg_namespace n, pg_catalog.pg_class c,'
2307  + ' pg_catalog.pg_user u WHERE c.relnamespace = n.oid '
2308  + ' AND u.usesysid = c.relowner AND c.relkind = ''r'' ';
2309  if SchemaPattern <> '' then
2310  SQL := SQL + ' AND ' + SchemaCondition;
2311  end
2312  else
2313  begin
2314  SQL := 'SELECT NULL::text AS nspname,c.relname,u.usename,c.relacl '
2315  + ' FROM pg_class c, pg_user u WHERE u.usesysid = c.relowner '
2316  + ' AND c.relkind = ''r'' ';
2317  end;
2318 
2319  SQL := SQL + ' AND ' + TableNameCondition
2320  + ' ORDER BY nspname, relname';
2321 
2322  Permissions := TStringList.Create;
2323  PermissionsExp := TStringList.Create;
2324  try
2325  with GetConnection.CreateStatement.ExecuteQuery(SQL) do
2326  begin
2327  while Next do
2328  begin
2329  SchemaName := GetStringByName('nspname');
2330  TableName := GetStringByName('relname');
2331  Owner := GetStringByName('usename');
2332  SchemaName := GetStringByName('nspname');
2333  Permissions.Clear;
2334  ParseACLArray(Permissions, GetStringByName('relacl'));
2335  Permissions.Sort;
2336  for I := 0 to Permissions.Count-1 do
2337  begin
2338  PutSplitString(PermissionsExp, Permissions.Strings[I], '=');
2339  if PermissionsExp.Count < 2 then
2340  Continue;
2341  Grantee := PermissionsExp.Strings[0];
2342  if Grantee = '' then
2343  Grantee := 'PUBLIC';
2344  Privileges := PermissionsExp.Strings[1];
2345  for J := 1 to Length(Privileges) do
2346  begin
2347  if Owner = Grantee then
2348  Grantable := 'YES'
2349  else Grantable := 'NO';
2350  Result.MoveToInsertRow;
2351  Result.UpdateNull(1);
2352  Result.UpdateString(2, SchemaName);
2353  Result.UpdateString(3, TableName);
2354  Result.UpdateString(4, Owner);
2355  Result.UpdateString(5, Grantee);
2356  Result.UpdateString(6, GetPrivilegeName(Privileges[J]));
2357  Result.UpdateString(7, grantable);
2358  Result.InsertRow;
2359  end;
2360  end;
2361  end;
2362  Close;
2363  end;
2364  finally
2365  Permissions.Free;
2366  PermissionsExp.Free;
2367  end;
2368 end;
2369 
2370 {**
2371  Gets a description of a table's columns that are automatically
2372  updated when any value in a row is updated. They are
2373  unordered.
2374 
2375  <P>Each column description has the following columns:
2376  <OL>
2377  <LI><B>SCOPE</B> short => is not used
2378  <LI><B>COLUMN_NAME</B> String => column name
2379  <LI><B>DATA_TYPE</B> short => SQL data type from java.sql.Types
2380  <LI><B>TYPE_NAME</B> String => Data source dependent type name
2381  <LI><B>COLUMN_SIZE</B> int => precision
2382  <LI><B>BUFFER_LENGTH</B> int => length of column value in bytes
2383  <LI><B>DECIMAL_DIGITS</B> short => scale
2384  <LI><B>PSEUDO_COLUMN</B> short => is this a pseudo column
2385  like an Oracle ROWID
2386  <UL>
2387  <LI> versionColumnUnknown - may or may not be pseudo column
2388  <LI> versionColumnNotPseudo - is NOT a pseudo column
2389  <LI> versionColumnPseudo - is a pseudo column
2390  </UL>
2391  </OL>
2392 
2393  @param catalog a catalog name; "" retrieves those without a
2394  catalog; null means drop catalog name from the selection criteria
2395  @param schema a schema name; "" retrieves those without a schema
2396  @param table a table name
2397  @return <code>ResultSet</code> - each row is a column description
2398  @exception SQLException if a database access error occurs
2399 }
2400 function TZPostgreSQLDatabaseMetadata.UncachedGetVersionColumns(const Catalog: string;
2401  const Schema: string; const Table: string): IZResultSet;
2402 begin
2403  Result:=inherited UncachedGetVersionColumns(Catalog, Schema, Table);
2404 
2405  Result.MoveToInsertRow;
2406  Result.UpdateNull(1);
2407  Result.UpdateString(2, 'ctid');
2408  Result.UpdateInt(3, Ord(GetSQLTypeByName('tid')));
2409  Result.UpdateString(4, 'tid');
2410  Result.UpdateNull(5);
2411  Result.UpdateNull(6);
2412  Result.UpdateNull(7);
2413  Result.UpdateInt(4, Ord(vcPseudo));
2414  Result.InsertRow;
2415 end;
2416 
2417 {**
2418  Gets a description of a table's primary key columns. They
2419  are ordered by COLUMN_NAME.
2420 
2421  <P>Each primary key column description has the following columns:
2422  <OL>
2423  <LI><B>TABLE_CAT</B> String => table catalog (may be null)
2424  <LI><B>TABLE_SCHEM</B> String => table schema (may be null)
2425  <LI><B>TABLE_NAME</B> String => table name
2426  <LI><B>COLUMN_NAME</B> String => column name
2427  <LI><B>KEY_SEQ</B> short => sequence number within primary key
2428  <LI><B>PK_NAME</B> String => primary key name (may be null)
2429  </OL>
2430 
2431  @param catalog a catalog name; "" retrieves those without a
2432  catalog; null means drop catalog name from the selection criteria
2433  @param schema a schema name; "" retrieves those
2434  without a schema
2435  @param table a table name
2436  @return <code>ResultSet</code> - each row is a primary key column description
2437  @exception SQLException if a database access error occurs
2438 }
2439 function TZPostgreSQLDatabaseMetadata.UncachedGetPrimaryKeys(const Catalog: string;
2440  const Schema: string; const Table: string): IZResultSet;
2441 var
2442  SQL, Select, From, Where: string;
2443  TableNameCondition, SchemaCondition: string;
2444 begin
2445  SchemaCondition := ConstructNameCondition(Schema,'n.nspname');
2446  TableNameCondition := ConstructNameCondition(Table,'ct.relname');
2447  if (GetDatabaseInfo as IZPostgreDBInfo).HasMinimumServerVersion(7, 3) then
2448  begin
2449  Select := 'SELECT NULL AS TABLE_CAT, n.nspname AS TABLE_SCHEM,';
2450  From := ' FROM pg_catalog.pg_namespace n, pg_catalog.pg_class ct,'
2451  + ' pg_catalog.pg_class ci, pg_catalog.pg_attribute a,'
2452  + ' pg_catalog.pg_index i';
2453  Where := ' AND ct.relnamespace = n.oid';
2454  if Schema <> '' then
2455  Where := Where + ' AND ' + SchemaCondition;
2456  end
2457  else
2458  begin
2459  Select := 'SELECT NULL AS TABLE_CAT, NULL AS TABLE_SCHEM,';
2460  From := ' FROM pg_class ct, pg_class ci, pg_attribute a, pg_index i';
2461  end;
2462  SQL := Select + ' ct.relname AS TABLE_NAME, a.attname AS COLUMN_NAME,'
2463  + ' a.attnum AS KEY_SEQ, ci.relname AS PK_NAME'
2464  + From
2465  + ' WHERE ct.oid=i.indrelid AND ci.oid=i.indexrelid'
2466  + ' AND a.attrelid=ci.oid AND i.indisprimary';
2467  if Table <> '' then
2468  SQL := SQL + ' AND ' + TableNameCondition;
2469  SQL := SQL + Where + ' ORDER BY table_name, pk_name, key_seq';
2470 
2471  Result := CopyToVirtualResultSet(
2472  GetConnection.CreateStatement.ExecuteQuery(SQL),
2473  ConstructVirtualResultSet(PrimaryKeyColumnsDynArray));
2474 end;
2475 
2476 {**
2477  Gets a description of the primary key columns that are
2478  referenced by a table's foreign key columns (the primary keys
2479  imported by a table). They are ordered by PKTABLE_CAT,
2480  PKTABLE_SCHEM, PKTABLE_NAME, and KEY_SEQ.
2481 
2482  <P>Each primary key column description has the following columns:
2483  <OL>
2484  <LI><B>PKTABLE_CAT</B> String => primary key table catalog
2485  being imported (may be null)
2486  <LI><B>PKTABLE_SCHEM</B> String => primary key table schema
2487  being imported (may be null)
2488  <LI><B>PKTABLE_NAME</B> String => primary key table name
2489  being imported
2490  <LI><B>PKCOLUMN_NAME</B> String => primary key column name
2491  being imported
2492  <LI><B>FKTABLE_CAT</B> String => foreign key table catalog (may be null)
2493  <LI><B>FKTABLE_SCHEM</B> String => foreign key table schema (may be null)
2494  <LI><B>FKTABLE_NAME</B> String => foreign key table name
2495  <LI><B>FKCOLUMN_NAME</B> String => foreign key column name
2496  <LI><B>KEY_SEQ</B> short => sequence number within foreign key
2497  <LI><B>UPDATE_RULE</B> short => What happens to
2498  foreign key when primary is updated:
2499  <UL>
2500  <LI> importedNoAction - do not allow update of primary
2501  key if it has been imported
2502  <LI> importedKeyCascade - change imported key to agree
2503  with primary key update
2504  <LI> importedKeySetNull - change imported key to NULL if
2505  its primary key has been updated
2506  <LI> importedKeySetDefault - change imported key to default values
2507  if its primary key has been updated
2508  <LI> importedKeyRestrict - same as importedKeyNoAction
2509  (for ODBC 2.x compatibility)
2510  </UL>
2511  <LI><B>DELETE_RULE</B> short => What happens to
2512  the foreign key when primary is deleted.
2513  <UL>
2514  <LI> importedKeyNoAction - do not allow delete of primary
2515  key if it has been imported
2516  <LI> importedKeyCascade - delete rows that import a deleted key
2517  <LI> importedKeySetNull - change imported key to NULL if
2518  its primary key has been deleted
2519  <LI> importedKeyRestrict - same as importedKeyNoAction
2520  (for ODBC 2.x compatibility)
2521  <LI> importedKeySetDefault - change imported key to default if
2522  its primary key has been deleted
2523  </UL>
2524  <LI><B>FK_NAME</B> String => foreign key name (may be null)
2525  <LI><B>PK_NAME</B> String => primary key name (may be null)
2526  <LI><B>DEFERRABILITY</B> short => can the evaluation of foreign key
2527  constraints be deferred until commit
2528  <UL>
2529  <LI> importedKeyInitiallyDeferred - see SQL92 for definition
2530  <LI> importedKeyInitiallyImmediate - see SQL92 for definition
2531  <LI> importedKeyNotDeferrable - see SQL92 for definition
2532  </UL>
2533  </OL>
2534 
2535  @param catalog a catalog name; "" retrieves those without a
2536  catalog; null means drop catalog name from the selection criteria
2537  @param schema a schema name; "" retrieves those
2538  without a schema
2539  @param table a table name
2540  @return <code>ResultSet</code> - each row is a primary key column description
2541  @see #getExportedKeys
2542 }
2543 function TZPostgreSQLDatabaseMetadata.UncachedGetImportedKeys(const Catalog: string;
2544  const Schema: string; const Table: string): IZResultSet;
2545 var
2546  SQL: string;
2547  KeySequence: Integer;
2548  TableNameCondition, SchemaCondition, CatalogCondition: string;
2549 begin
2550  CatalogCondition := ConstructNameCondition(Catalog,'kcu.table_catalog');
2551  SchemaCondition := ConstructNameCondition(Schema,'kcu.constraint_schema');
2552  TableNameCondition := ConstructNameCondition(Table,'kcu.table_name');
2553  if (GetDatabaseInfo as IZPostgreDBInfo).HasMinimumServerVersion(7, 4) then
2554  begin
2555  Result:=inherited UncachedGetImportedKeys(Catalog, Schema, Table);
2556  SQL := 'SELECT '+
2557  'tc.constraint_catalog as PKTABLE_CAT, '+
2558  'tc.constraint_schema as PKTABLE_SCHEM, '+
2559  'ccu.table_name as PKTABLE_NAME, '+
2560  'ccu.column_name as PKCOLUMN_NAME, '+
2561  'kcu.table_catalog as FKTABLE_CAT, '+
2562  'kcu.constraint_schema as FKTABLE_SCHEM, '+
2563  'kcu.table_name as PKTABLE_NAME, '+
2564  'kcu.column_name as FKCOLUMN_NAME, '+
2565  'rf.update_rule as UPDATE_RULE, '+
2566  'rf.delete_rule as DELETE_RULE, '+
2567  'kcu.constraint_name as FK_NAME, '+
2568  'kcu.ordinal_position as PK_NAME, '+
2569  'tc.is_deferrable as DEFERRABILITY '+
2570  'FROM information_schema.table_constraints AS tc '+
2571  'JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name '+
2572  'JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name '+
2573  'join information_schema.referential_constraints as rf on rf.constraint_name = tc.constraint_name '+
2574  'WHERE constraint_type = ''FOREIGN KEY''';
2575  if Catalog <> '' then
2576  SQL := SQL + ' and ' + CatalogCondition;
2577  if Schema <> '' then
2578  SQL := SQL + ' and ' + SchemaCondition;
2579  if Table <> '' then
2580  SQL := SQL + ' and ' + TableNameCondition;
2581 
2582  KeySequence := 0;
2583  with GetConnection.CreateStatement.ExecuteQuery(SQL) do
2584  begin
2585  while Next do
2586  begin
2587  Inc(KeySequence);
2588  Result.MoveToInsertRow;
2589  Result.UpdateNull(1); //PKTABLE_CAT
2590  Result.UpdateString(2, GetString(2)); //PKTABLE_SCHEM
2591  Result.UpdateString(3, GetString(3)); //PKTABLE_NAME
2592  Result.UpdateString(4, GetString(4)); //PKCOLUMN_NAME
2593  //Result.UpdateString(5, GetString(5)); //PKTABLE_CAT
2594  Result.UpdateString(5, Catalog); //PKTABLE_CAT
2595  Result.UpdateString(6, GetString(6)); //FKTABLE_SCHEM
2596  Result.UpdateString(7, GetString(7)); //FKTABLE_NAME
2597  Result.UpdateString(8, GetString(8)); //FKCOLUMN_NAME
2598  Result.UpdateShort(9, KeySequence); //KEY_SEQ
2599  Result.UpdateShort(10, Ord(GetRuleType(GetString(9)))); //UPDATE_RULE
2600  Result.UpdateShort(11, Ord(GetRuleType(GetString(10)))); //DELETE_RULE
2601  Result.UpdateString(12, GetString(11)); //FK_NAME
2602  Result.UpdateString(13, GetString(12)); //PK_NAME
2603  if GetString(13) = 'NO' then
2604  Result.UpdateShort(14, Ord(ikNotDeferrable)) //DEFERRABILITY
2605  else
2606  Result.UpdateShort(14, Ord(ikInitiallyDeferred)); //DEFERRABILITY
2607  Result.InsertRow;
2608  end;
2609  Close;
2610  end;
2611  end
2612  else
2613  Result := UncachedGetCrossReference('', '', '', Catalog, Schema, Table);
2614 end;
2615 
2616 {**
2617  Gets a description of the foreign key columns that reference a
2618  table's primary key columns (the foreign keys exported by a
2619  table). They are ordered by FKTABLE_CAT, FKTABLE_SCHEM,
2620  FKTABLE_NAME, and KEY_SEQ.
2621 
2622  <P>Each foreign key column description has the following columns:
2623  <OL>
2624  <LI><B>PKTABLE_CAT</B> String => primary key table catalog (may be null)
2625  <LI><B>PKTABLE_SCHEM</B> String => primary key table schema (may be null)
2626  <LI><B>PKTABLE_NAME</B> String => primary key table name
2627  <LI><B>PKCOLUMN_NAME</B> String => primary key column name
2628  <LI><B>FKTABLE_CAT</B> String => foreign key table catalog (may be null)
2629  being exported (may be null)
2630  <LI><B>FKTABLE_SCHEM</B> String => foreign key table schema (may be null)
2631  being exported (may be null)
2632  <LI><B>FKTABLE_NAME</B> String => foreign key table name
2633  being exported
2634  <LI><B>FKCOLUMN_NAME</B> String => foreign key column name
2635  being exported
2636  <LI><B>KEY_SEQ</B> short => sequence number within foreign key
2637  <LI><B>UPDATE_RULE</B> short => What happens to
2638  foreign key when primary is updated:
2639  <UL>
2640  <LI> importedNoAction - do not allow update of primary
2641  key if it has been imported
2642  <LI> importedKeyCascade - change imported key to agree
2643  with primary key update
2644  <LI> importedKeySetNull - change imported key to NULL if
2645  its primary key has been updated
2646  <LI> importedKeySetDefault - change imported key to default values
2647  if its primary key has been updated
2648  <LI> importedKeyRestrict - same as importedKeyNoAction
2649  (for ODBC 2.x compatibility)
2650  </UL>
2651  <LI><B>DELETE_RULE</B> short => What happens to
2652  the foreign key when primary is deleted.
2653  <UL>
2654  <LI> importedKeyNoAction - do not allow delete of primary
2655  key if it has been imported
2656  <LI> importedKeyCascade - delete rows that import a deleted key
2657  <LI> importedKeySetNull - change imported key to NULL if
2658  its primary key has been deleted
2659  <LI> importedKeyRestrict - same as importedKeyNoAction
2660  (for ODBC 2.x compatibility)
2661  <LI> importedKeySetDefault - change imported key to default if
2662  its primary key has been deleted
2663  </UL>
2664  <LI><B>FK_NAME</B> String => foreign key name (may be null)
2665  <LI><B>PK_NAME</B> String => primary key name (may be null)
2666  <LI><B>DEFERRABILITY</B> short => can the evaluation of foreign key
2667  constraints be deferred until commit
2668  <UL>
2669  <LI> importedKeyInitiallyDeferred - see SQL92 for definition
2670  <LI> importedKeyInitiallyImmediate - see SQL92 for definition
2671  <LI> importedKeyNotDeferrable - see SQL92 for definition
2672  </UL>
2673  </OL>
2674 
2675  @param catalog a catalog name; "" retrieves those without a
2676  catalog; null means drop catalog name from the selection criteria
2677  @param schema a schema name; "" retrieves those
2678  without a schema
2679  @param table a table name
2680  @return <code>ResultSet</code> - each row is a foreign key column description
2681  @see #getImportedKeys
2682 }
2683 function TZPostgreSQLDatabaseMetadata.UncachedGetExportedKeys(const Catalog: string;
2684  const Schema: string; const Table: string): IZResultSet;
2685 var
2686  SQL: string;
2687  KeySequence: Integer;
2688  TableNameCondition, SchemaCondition, CatalogCondition: string;
2689 begin
2690  CatalogCondition := ConstructNameCondition(Catalog,'tc.constraint_catalog');
2691  SchemaCondition := ConstructNameCondition(Schema,'tc.constraint_schema');
2692  TableNameCondition := ConstructNameCondition(Table,'ccu.table_name');
2693  if (GetDatabaseInfo as IZPostgreDBInfo).HasMinimumServerVersion(7, 4) then
2694  begin
2695  Result:=inherited UncachedGetImportedKeys(Catalog, Schema, Table);
2696  SQL := 'SELECT '+
2697  'tc.constraint_catalog as PKTABLE_CAT, '+
2698  'tc.constraint_schema as PKTABLE_SCHEM, '+
2699  'ccu.table_name as PKTABLE_NAME, '+
2700  'ccu.column_name as PKCOLUMN_NAME, '+
2701  'kcu.table_catalog as FKTABLE_CAT, '+
2702  'kcu.constraint_schema as FKTABLE_SCHEM, '+
2703  'kcu.table_name as PKTABLE_NAME, '+
2704  'kcu.column_name as FKCOLUMN_NAME, '+
2705  'rf.update_rule as UPDATE_RULE, '+
2706  'rf.delete_rule as DELETE_RULE, '+
2707  'kcu.constraint_name as FK_NAME, '+
2708  'kcu.ordinal_position as PK_NAME, '+
2709  'tc.is_deferrable as DEFERRABILITY '+
2710  'FROM information_schema.table_constraints AS tc '+
2711  'JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name '+
2712  'JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name '+
2713  'join information_schema.referential_constraints as rf on rf.constraint_name = tc.constraint_name '+
2714  'WHERE constraint_type = ''FOREIGN KEY''';
2715  if Catalog <> '' then
2716  SQL := SQL + ' and ' + CatalogCondition;
2717  if Schema <> '' then
2718  SQL := SQL + ' and ' + SchemaCondition;
2719  if Table <> '' then
2720  SQL := SQL + ' and ' + TableNameCondition;
2721  SQL := SQL + ' order by kcu.table_name;';
2722 
2723  KeySequence := 0;
2724  with GetConnection.CreateStatement.ExecuteQuery(SQL) do
2725  begin
2726  while Next do
2727  begin
2728  Inc(KeySequence);
2729  Result.MoveToInsertRow;
2730  Result.UpdateNull(1); //PKTABLE_CAT
2731  Result.UpdateString(2, GetString(2)); //PKTABLE_SCHEM
2732  Result.UpdateString(3, GetString(3)); //PKTABLE_NAME
2733  Result.UpdateString(4, GetString(4)); //PKCOLUMN_NAME
2734  //Result.UpdateString(5, GetString(5)); //PKTABLE_CAT
2735  Result.UpdateString(5, Catalog); //PKTABLE_CAT
2736  Result.UpdateString(6, GetString(6)); //FKTABLE_SCHEM
2737  Result.UpdateString(7, GetString(7)); //FKTABLE_NAME
2738  Result.UpdateString(8, GetString(8)); //FKCOLUMN_NAME
2739  Result.UpdateShort(9, KeySequence); //KEY_SEQ
2740  Result.UpdateShort(10, Ord(GetRuleType(GetString(9)))); //UPDATE_RULE
2741  Result.UpdateShort(11, Ord(GetRuleType(GetString(10)))); //DELETE_RULE
2742  Result.UpdateString(12, GetString(11)); //FK_NAME
2743  Result.UpdateString(13, GetString(12)); //PK_NAME
2744  if GetString(13) = 'NO' then
2745  Result.UpdateShort(14, Ord(ikNotDeferrable)) //DEFERRABILITY
2746  else
2747  Result.UpdateShort(14, Ord(ikInitiallyDeferred)); //DEFERRABILITY
2748  Result.InsertRow;
2749  end;
2750  Close;
2751  end;
2752  end
2753  else
2754  Result := UncachedGetCrossReference('', '', '', Catalog, Schema, Table);
2755 end;
2756 
2757 {**
2758  Gets a description of the foreign key columns in the foreign key
2759  table that reference the primary key columns of the primary key
2760  table (describe how one table imports another's key.) This
2761  should normally return a single foreign key/primary key pair
2762  (most tables only import a foreign key from a table once.) They
2763  are ordered by FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, and
2764  KEY_SEQ.
2765 
2766  <P>Each foreign key column description has the following columns:
2767  <OL>
2768  <LI><B>PKTABLE_CAT</B> String => primary key table catalog (may be null)
2769  <LI><B>PKTABLE_SCHEM</B> String => primary key table schema (may be null)
2770  <LI><B>PKTABLE_NAME</B> String => primary key table name
2771  <LI><B>PKCOLUMN_NAME</B> String => primary key column name
2772  <LI><B>FKTABLE_CAT</B> String => foreign key table catalog (may be null)
2773  being exported (may be null)
2774  <LI><B>FKTABLE_SCHEM</B> String => foreign key table schema (may be null)
2775  being exported (may be null)
2776  <LI><B>FKTABLE_NAME</B> String => foreign key table name
2777  being exported
2778  <LI><B>FKCOLUMN_NAME</B> String => foreign key column name
2779  being exported
2780  <LI><B>KEY_SEQ</B> short => sequence number within foreign key
2781  <LI><B>UPDATE_RULE</B> short => What happens to
2782  foreign key when primary is updated:
2783  <UL>
2784  <LI> importedNoAction - do not allow update of primary
2785  key if it has been imported
2786  <LI> importedKeyCascade - change imported key to agree
2787  with primary key update
2788  <LI> importedKeySetNull - change imported key to NULL if
2789  its primary key has been updated
2790  <LI> importedKeySetDefault - change imported key to default values
2791  if its primary key has been updated
2792  <LI> importedKeyRestrict - same as importedKeyNoAction
2793  (for ODBC 2.x compatibility)
2794  </UL>
2795  <LI><B>DELETE_RULE</B> short => What happens to
2796  the foreign key when primary is deleted.
2797  <UL>
2798  <LI> importedKeyNoAction - do not allow delete of primary
2799  key if it has been imported
2800  <LI> importedKeyCascade - delete rows that import a deleted key
2801  <LI> importedKeySetNull - change imported key to NULL if
2802  its primary key has been deleted
2803  <LI> importedKeyRestrict - same as importedKeyNoAction
2804  (for ODBC 2.x compatibility)
2805  <LI> importedKeySetDefault - change imported key to default if
2806  its primary key has been deleted
2807  </UL>
2808  <LI><B>FK_NAME</B> String => foreign key name (may be null)
2809  <LI><B>PK_NAME</B> String => primary key name (may be null)
2810  <LI><B>DEFERRABILITY</B> short => can the evaluation of foreign key
2811  constraints be deferred until commit
2812  <UL>
2813  <LI> importedKeyInitiallyDeferred - see SQL92 for definition
2814  <LI> importedKeyInitiallyImmediate - see SQL92 for definition
2815  <LI> importedKeyNotDeferrable - see SQL92 for definition
2816  </UL>
2817  </OL>
2818 
2819  @param primaryCatalog a catalog name; "" retrieves those without a
2820  catalog; null means drop catalog name from the selection criteria
2821  @param primarySchema a schema name; "" retrieves those
2822  without a schema
2823  @param primaryTable the table name that exports the key
2824  @param foreignCatalog a catalog name; "" retrieves those without a
2825  catalog; null means drop catalog name from the selection criteria
2826  @param foreignSchema a schema name; "" retrieves those
2827  without a schema
2828  @param foreignTable the table name that imports the key
2829  @return <code>ResultSet</code> - each row is a foreign key column description
2830  @see #getImportedKeys
2831 }
2832 function TZPostgreSQLDatabaseMetadata.UncachedGetCrossReference(const PrimaryCatalog: string;
2833  const PrimarySchema: string; const PrimaryTable: string; const ForeignCatalog: string;
2834  const ForeignSchema: string; const ForeignTable: string): IZResultSet;
2835 var
2836  SQL, Select, From, Where: string;
2837  DeleteRule, UpdateRule, Rule: string;
2838  {FKeyName, }FKeyColumn, PKeyColumn, Targs: string;
2839  Action, KeySequence, Advance: Integer;
2840  List: TStrings;
2841  Deferrability: Integer;
2842  Deferrable, InitiallyDeferred: Boolean;
2843 begin
2844  Result:=inherited UncachedGetCrossReference(PrimaryCatalog, PrimarySchema, PrimaryTable,
2845  ForeignCatalog, ForeignSchema, ForeignTable);
2846 
2847  if (GetDatabaseInfo as IZPostgreDBInfo).HasMinimumServerVersion(7, 4) then
2848  begin
2849  SQL := 'SELECT '+
2850  'tc.constraint_catalog as PKTABLE_CAT, '+
2851  'tc.constraint_schema as PKTABLE_SCHEM, '+
2852  'ccu.table_name as PKTABLE_NAME, '+
2853  'ccu.column_name as PKCOLUMN_NAME, '+
2854  'kcu.table_catalog as FKTABLE_CAT, '+
2855  'kcu.constraint_schema as FKTABLE_SCHEM, '+
2856  'kcu.table_name as PKTABLE_NAME, '+
2857  'kcu.column_name as FKCOLUMN_NAME, '+
2858  'rf.update_rule as UPDATE_RULE, '+
2859  'rf.delete_rule as DELETE_RULE, '+
2860  'kcu.constraint_name as FK_NAME, '+
2861  'kcu.ordinal_position as PK_NAME, '+
2862  'tc.is_deferrable as DEFERRABILITY '+
2863  'FROM information_schema.table_constraints AS tc '+
2864  'JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name '+
2865  'JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name '+
2866  'join information_schema.referential_constraints as rf on rf.constraint_name = tc.constraint_name '+
2867  'WHERE constraint_type = ''FOREIGN KEY''';
2868  if PrimaryCatalog <> '' then
2869  SQL := SQL + ' and tc.constraint_catalog = '''+PrimaryCatalog+'''';
2870  if PrimarySchema <> '' then
2871  SQL := SQL + ' and tc.constraint_schema = '''+PrimarySchema+'''';
2872  if PrimaryTable <> '' then
2873  SQL := SQL + ' and ccu.table_name = '''+PrimaryTable+'''';
2874  if ForeignCatalog <> '' then
2875  SQL := SQL + ' and kcu.table_catalog = '''+ForeignCatalog+'''';
2876  if ForeignSchema <> '' then
2877  SQL := SQL + ' and kcu.constraint_schema = '''+ForeignSchema+'''';
2878  if ForeignTable <> '' then
2879  SQL := SQL + ' and kcu.table_name = '''+ForeignTable+'''';
2880 
2881  KeySequence := 0;
2882  with GetConnection.CreateStatement.ExecuteQuery(SQL) do
2883  begin
2884  while Next do
2885  begin
2886  Inc(KeySequence);
2887  Result.MoveToInsertRow;
2888  //Result.UpdateString(1, GetString(1)); //PKTABLE_CAT
2889  Result.UpdateString(1, PrimaryCatalog); //PKTABLE_CAT
2890  Result.UpdateString(2, GetString(2)); //PKTABLE_SCHEM
2891  Result.UpdateString(3, GetString(3)); //PKTABLE_NAME
2892  Result.UpdateString(4, GetString(4)); //PKCOLUMN_NAME
2893  //Result.UpdateString(5, GetString(5)); //PKTABLE_CAT
2894  Result.UpdateString(5, ForeignCatalog); //PKTABLE_CAT
2895  Result.UpdateString(6, GetString(6)); //FKTABLE_SCHEM
2896  Result.UpdateString(7, GetString(7)); //FKTABLE_NAME
2897  Result.UpdateString(8, GetString(8)); //FKCOLUMN_NAME
2898  Result.UpdateShort(9, KeySequence); //KEY_SEQ
2899  Result.UpdateShort(10, Ord(GetRuleType(GetString(9)))); //UPDATE_RULE
2900  Result.UpdateShort(11, Ord(GetRuleType(GetString(10)))); //DELETE_RULE
2901  Result.UpdateString(12, GetString(11)); //FK_NAME
2902  Result.UpdateString(13, GetString(12)); //PK_NAME
2903  if GetString(13) = 'NO' then
2904  Result.UpdateShort(14, Ord(ikNotDeferrable)) //DEFERRABILITY
2905  else
2906  Result.UpdateShort(14, Ord(ikInitiallyDeferred)); //DEFERRABILITY
2907  Result.InsertRow;
2908  end;
2909  Close;
2910  end;
2911  end
2912  else
2913  begin
2914  if (GetDatabaseInfo as IZPostgreDBInfo).HasMinimumServerVersion(7, 3) then
2915  begin
2916  Select := 'SELECT DISTINCT n1.nspname as pnspname,n2.nspname as fnspname,';
2917  From := ' FROM pg_catalog.pg_namespace n1 JOIN pg_catalog.pg_class c1'
2918  + ' ON (c1.relnamespace = n1.oid) JOIN pg_catalog.pg_index i'
2919  + ' ON (c1.oid=i.indrelid) JOIN pg_catalog.pg_class ic'
2920  + ' ON (i.indexrelid=ic.oid) JOIN pg_catalog.pg_attribute a'
2921  + ' ON (ic.oid=a.attrelid), pg_catalog.pg_namespace n2'
2922  + ' JOIN pg_catalog.pg_class c2 ON (c2.relnamespace=n2.oid),'
2923  + ' pg_catalog.pg_trigger t1 JOIN pg_catalog.pg_proc p1'
2924  + ' ON (t1.tgfoid=p1.oid), pg_catalog.pg_trigger t2'
2925  + ' JOIN pg_catalog.pg_proc p2 ON (t2.tgfoid=p2.oid)';
2926  Where := '';
2927  if PrimarySchema <> ''then
2928  begin
2929  Where := Where + ' AND n1.nspname = '
2930  + EscapeString(PrimarySchema);
2931  end;
2932  if ForeignSchema <> '' then
2933  begin
2934  Where := Where + ' AND n2.nspname = '
2935  + EscapeString(ForeignSchema);
2936  end;
2937  end
2938  else
2939  begin
2940  Select := 'SELECT DISTINCT NULL::text as pnspname, NULL::text as fnspname,';
2941  From := ' FROM pg_class c1 JOIN pg_index i ON (c1.oid=i.indrelid)'
2942  + ' JOIN pg_class ic ON (i.indexrelid=ic.oid) JOIN pg_attribute a'
2943  + ' ON (ic.oid=a.attrelid), pg_class c2, pg_trigger t1'
2944  + ' JOIN pg_proc p1 ON (t1.tgfoid=p1.oid), pg_trigger t2'
2945  + ' JOIN pg_proc p2 ON (t2.tgfoid=p2.oid)';
2946  end;
2947 
2948  SQL := Select + ' c1.relname as prelname, c2.relname as frelname,'
2949  + ' t1.tgconstrname, a.attnum as keyseq, ic.relname as fkeyname,'
2950  + ' t1.tgdeferrable, t1.tginitdeferred, t1.tgnargs,t1.tgargs,'
2951  + ' p1.proname as updaterule, p2.proname as deleterule'
2952  + From
2953  + ' WHERE (t1.tgrelid=c1.oid AND t1.tgisconstraint'
2954  + ' AND t1.tgconstrrelid=c2.oid AND p1.proname'
2955  + ' LIKE ' + EscapeString('RI\_FKey\_%\_upd')
2956  + ') AND (t2.tgrelid=c1.oid'
2957  + ' AND t2.tgisconstraint AND t2.tgconstrrelid=c2.oid '
2958  + ' AND p2.proname LIKE ' + EscapeString('RI\_FKey\_%\_del')
2959  + ') AND i.indisprimary'
2960  + Where;
2961  if PrimaryTable <> '' then
2962  SQL := SQL + ' AND c1.relname=' + EscapeString(PrimaryTable);
2963  if ForeignTable <> '' then
2964  SQL := SQL + ' AND c2.relname=' + EscapeString(ForeignTable);
2965  SQL := SQL + ' ORDER BY ';
2966 
2967  if PrimaryTable <> '' then
2968  begin
2969  if (GetDatabaseInfo as IZPostgreDBInfo).HasMinimumServerVersion(7, 3) then
2970  SQL := SQL + 'fnspname, ';
2971  SQL := SQL + 'frelname';
2972  end
2973  else
2974  begin
2975  if (GetDatabaseInfo as IZPostgreDBInfo).HasMinimumServerVersion(7, 3) then
2976  SQL := SQL + 'pnspname, ';
2977  SQL := SQL + 'prelname';
2978  end;
2979 
2980  SQL := SQL + ', keyseq';
2981 
2982  List := TStringList.Create;
2983  try
2984  with GetConnection.CreateStatement.ExecuteQuery(SQL) do
2985  begin
2986  while Next do
2987  begin
2988  Result.MoveToInsertRow;
2989  Result.UpdateString(2, GetString(1));
2990  Result.UpdateString(6, GetString(2));
2991  Result.UpdateString(3, GetString(3));
2992  Result.UpdateString(7, GetString(4));
2993 
2994  //FKeyName := GetString(5);
2995  UpdateRule := GetString(12);
2996  if UpdateRule <> '' then
2997  begin
2998  Rule := Copy(UpdateRule, 9, Length(UpdateRule) - 12);
2999  Action := Ord(ikNoAction);
3000  if (Rule = '') or (Rule = 'noaction') then
3001  Action := Ord(ikNoAction);
3002  if Rule = 'cascade' then
3003  Action := Ord(ikCascade);
3004  if Rule = 'setnull' then
3005  Action := Ord(ikSetNull);
3006  if Rule = 'setdefault' then
3007  Action := Ord(ikSetDefault);
3008  if Rule = 'restrict' then
3009  Action := Ord(ikRestrict);
3010  Result.UpdateInt(10, Action);
3011  end;
3012 
3013  DeleteRule := GetString(13);
3014  if DeleteRule <> '' then
3015  begin
3016  Rule := Copy(DeleteRule, 9, Length(DeleteRule) - 12);
3017  Action := Ord(ikNoAction);
3018  if Rule = 'cascade' then
3019  Action := Ord(ikCascade);
3020  if Rule = 'setnull' then
3021  Action := Ord(ikSetNull);
3022  if Rule = 'setdefault' then
3023  Action := Ord(ikSetDefault);
3024  if Rule = 'restrict' then
3025  Action := Ord(ikRestrict);
3026  Result.UpdateInt(11, Action);
3027  end;
3028 
3029  KeySequence := GetInt(6);
3030  Targs := GetString(11);
3031 
3032  //<unnamed>\000ww\000vv\000UNSPECIFIED\000m\000a\000n\000b\000
3033  //for Postgresql 7.3
3034  //$1\000ww\000vv\000UNSPECIFIED\000m\000a\000n\000b\000
3035  //$2\000ww\000vv\000UNSPECIFIED\000m\000a\000n\000b\000
3036 
3037  Advance := 4 + (KeySequence - 1) * 2;
3038  PutSplitStringEx(List, Targs, '\000');
3039 
3040  if Advance <= List.Count-1 then
3041  FKeyColumn := List.Strings[Advance];
3042  if Advance + 1 <= List.Count-1 then
3043  PKeyColumn := List.Strings[Advance+1];
3044  Result.UpdateString(4, PKeyColumn);
3045  Result.UpdateString(8, FKeyColumn);
3046  Result.UpdateString(9, GetString(6)); //KEY_SEQ
3047 
3048  if List.Strings[0] = '<unnamed>' then
3049  Result.UpdateString(12, Targs) //FK_NAME
3050  else Result.UpdateString(12, List.Strings[0]); //FK_NAME
3051 
3052  Result.UpdateString(13, GetString(6)); //PK_ NAME
3053 
3054  Deferrability := Ord(ikNotDeferrable);
3055  Deferrable := GetBoolean(8);
3056  InitiallyDeferred := GetBoolean(9);
3057  if Deferrable then
3058  begin
3059  if InitiallyDeferred then
3060  Deferrability := Ord(ikInitiallyDeferred)
3061  else Deferrability := Ord(ikInitiallyImmediate);
3062  end;
3063  Result.UpdateInt(14, Deferrability);
3064  Result.InsertRow;
3065  end;
3066  Close;
3067  end;
3068  finally
3069  List.Free;
3070  end;
3071  end;
3072 end;
3073 
3074 {**
3075  Gets a description of all the standard SQL types supported by
3076  this database. They are ordered by DATA_TYPE and then by how
3077  closely the data type maps to the corresponding JDBC SQL type.
3078 
3079  <P>Each type description has the following columns:
3080  <OL>
3081  <LI><B>TYPE_NAME</B> String => Type name
3082  <LI><B>DATA_TYPE</B> short => SQL data type from java.sql.Types
3083  <LI><B>PRECISION</B> int => maximum precision
3084  <LI><B>LITERAL_PREFIX</B> String => prefix used to quote a literal
3085  (may be null)
3086  <LI><B>LITERAL_SUFFIX</B> String => suffix used to quote a literal
3087  (may be null)
3088  <LI><B>CREATE_PARAMS</B> String => parameters used in creating
3089  the type (may be null)
3090  <LI><B>NULLABLE</B> short => can you use NULL for this type?
3091  <UL>
3092  <LI> typeNoNulls - does not allow NULL values
3093  <LI> typeNullable - allows NULL values
3094  <LI> typeNullableUnknown - nullability unknown
3095  </UL>
3096  <LI><B>CASE_SENSITIVE</B> Boolean=> is it case sensitive?
3097  <LI><B>SEARCHABLE</B> short => can you use "WHERE" based on this type:
3098  <UL>
3099  <LI> typePredNone - No support
3100  <LI> typePredChar - Only supported with WHERE .. LIKE
3101  <LI> typePredBasic - Supported except for WHERE .. LIKE
3102  <LI> typeSearchable - Supported for all WHERE ..
3103  </UL>
3104  <LI><B>UNSIGNED_ATTRIBUTE</B> Boolean => is it unsigned?
3105  <LI><B>FIXED_PREC_SCALE</B> Boolean => can it be a money value?
3106  <LI><B>AUTO_INCREMENT</B> Boolean => can it be used for an
3107  auto-increment value?
3108  <LI><B>LOCAL_TYPE_NAME</B> String => localized version of type name
3109  (may be null)
3110  <LI><B>MINIMUM_SCALE</B> short => minimum scale supported
3111  <LI><B>MAXIMUM_SCALE</B> short => maximum scale supported
3112  <LI><B>SQL_DATA_TYPE</B> int => unused
3113  <LI><B>SQL_DATETIME_SUB</B> int => unused
3114  <LI><B>NUM_PREC_RADIX</B> int => usually 2 or 10
3115  </OL>
3116 
3117  @return <code>ResultSet</code> - each row is an SQL type description
3118 }
3119 function TZPostgreSQLDatabaseMetadata.UncachedGetTypeInfo: IZResultSet;
3120 var
3121  SQL: string;
3122 begin
3123  Result:=inherited UncachedGetTypeInfo;
3124 
3125  if (GetDatabaseInfo as IZPostgreDBInfo).HasMinimumServerVersion(7, 3) then
3126  SQL := ' SELECT typname FROM pg_catalog.pg_type '
3127  else SQL := ' SELECT typname FROM pg_type ';
3128 
3129  with GetConnection.CreateStatement.ExecuteQuery(SQL) do
3130  begin
3131  while Next do
3132  begin
3133  Result.MoveToInsertRow;
3134  Result.UpdateString(1, GetString(1));
3135  Result.UpdateInt(2, Ord(GetSQLTypeByName(GetString(1))));
3136  Result.UpdateInt(3, 9);
3137  Result.UpdateInt(7, Ord(ntNoNulls));
3138  Result.UpdateBoolean(8, False);
3139  Result.UpdateBoolean(9, False);
3140  Result.UpdateBoolean(11, False);
3141  Result.UpdateBoolean(12, False);
3142  Result.UpdateInt(18, 10);
3143  Result.InsertRow;
3144  end;
3145  Close;
3146  end;
3147 end;
3148 
3149 {**
3150  Gets a description of a table's indices and statistics. They are
3151  ordered by NON_UNIQUE, TYPE, INDEX_NAME, and ORDINAL_POSITION.
3152 
3153  <P>Each index column description has the following columns:
3154  <OL>
3155  <LI><B>TABLE_CAT</B> String => table catalog (may be null)
3156  <LI><B>TABLE_SCHEM</B> String => table schema (may be null)
3157  <LI><B>TABLE_NAME</B> String => table name
3158  <LI><B>NON_UNIQUE</B> Boolean => Can index values be non-unique?
3159  false when TYPE is tableIndexStatistic
3160  <LI><B>INDEX_QUALIFIER</B> String => index catalog (may be null);
3161  null when TYPE is tableIndexStatistic
3162  <LI><B>INDEX_NAME</B> String => index name; null when TYPE is
3163  tableIndexStatistic
3164  <LI><B>TYPE</B> short => index type:
3165  <UL>
3166  <LI> tableIndexStatistic - this identifies table statistics that are
3167  returned in conjuction with a table's index descriptions
3168  <LI> tableIndexClustered - this is a clustered index
3169  <LI> tableIndexHashed - this is a hashed index
3170  <LI> tableIndexOther - this is some other style of index
3171  </UL>
3172  <LI><B>ORDINAL_POSITION</B> short => column sequence number
3173  within index; zero when TYPE is tableIndexStatistic
3174  <LI><B>COLUMN_NAME</B> String => column name; null when TYPE is
3175  tableIndexStatistic
3176  <LI><B>ASC_OR_DESC</B> String => column sort sequence, "A" => ascending,
3177  "D" => descending, may be null if sort sequence is not supported;
3178  null when TYPE is tableIndexStatistic
3179  <LI><B>CARDINALITY</B> int => When TYPE is tableIndexStatistic, then
3180  this is the number of rows in the table; otherwise, it is the
3181  number of unique values in the index.
3182  <LI><B>PAGES</B> int => When TYPE is tableIndexStatisic then
3183  this is the number of pages used for the table, otherwise it
3184  is the number of pages used for the current index.
3185  <LI><B>FILTER_CONDITION</B> String => Filter condition, if any.
3186  (may be null)
3187  </OL>
3188 
3189  @param catalog a catalog name; "" retrieves those without a
3190  catalog; null means drop catalog name from the selection criteria
3191  @param schema a schema name; "" retrieves those without a schema
3192  @param table a table name
3193  @param unique when true, return only indices for unique values;
3194  when false, return indices regardless of whether unique or not
3195  @param approximate when true, result is allowed to reflect approximate
3196  or out of data values; when false, results are requested to be
3197  accurate
3198  @return <code>ResultSet</code> - each row is an index column description
3199 }
3200 function TZPostgreSQLDatabaseMetadata.UncachedGetIndexInfo(const Catalog: string;
3201  const Schema: string; const Table: string; Unique: Boolean;
3202  Approximate: Boolean): IZResultSet;
3203 var
3204  SQL, Select, From, Where: string;
3205 begin
3206  if (GetDatabaseInfo as IZPostgreDBInfo).HasMinimumServerVersion(7, 3) then
3207  begin
3208  Select := 'SELECT NULL AS TABLE_CAT, n.nspname AS TABLE_SCHEM,';
3209  From := ' FROM pg_catalog.pg_namespace n, pg_catalog.pg_class ct,'
3210  + ' pg_catalog.pg_class ci, pg_catalog.pg_index i,'
3211  + ' pg_catalog.pg_attribute a, pg_catalog.pg_am am';
3212  Where := ' AND n.oid = ct.relnamespace';
3213  if Schema <> '' then
3214  Where := Where + ' AND n.nspname = ' + EscapeString(Schema);
3215  end
3216  else
3217  begin
3218  Select := 'SELECT NULL AS TABLE_CAT, NULL AS TABLE_SCHEM,';
3219  From := ' FROM pg_class ct, pg_class ci, pg_index i, pg_attribute a,'
3220  + ' pg_am am';
3221  end;
3222 
3223  SQL := Select + ' ct.relname AS TABLE_NAME, NOT i.indisunique'
3224  + ' AS NON_UNIQUE, NULL AS INDEX_QUALIFIER, ci.relname AS INDEX_NAME,'
3225  + ' CASE i.indisclustered WHEN true THEN ' + IntToStr(Ord(tiClustered))
3226  + ' ELSE CASE am.amname WHEN ''hash'' THEN ' + IntToStr(Ord(tiHashed))
3227  + ' ELSE ' + IntToStr(Ord(tiOther)) + ' END END AS TYPE,'
3228  + ' a.attnum AS ORDINAL_POSITION, a.attname AS COLUMN_NAME,'
3229  + ' NULL AS ASC_OR_DESC, ci.reltuples AS CARDINALITY,'
3230  + ' ci.relpages AS PAGES, NULL AS FILTER_CONDITION'
3231  + From
3232  + ' WHERE ct.oid=i.indrelid AND ci.oid=i.indexrelid'
3233  + ' AND a.attrelid=ci.oid AND ci.relam=am.oid' + Where
3234  + ' AND ct.relname = ' + EscapeString(Table);
3235 
3236  if Unique then
3237  SQL := SQL + ' AND i.indisunique';
3238  SQL := SQL + ' ORDER BY NON_UNIQUE, TYPE, INDEX_NAME, ORDINAL_POSITION';
3239 
3240  Result := CopyToVirtualResultSet(
3241  GetConnection.CreateStatement.ExecuteQuery(SQL),
3242  ConstructVirtualResultSet(IndexInfoColumnsDynArray));
3243 end;
3244 
3245 function TZPostgreSQLDatabaseMetadata.UncachedGetSequences(const Catalog, SchemaPattern,
3246  SequenceNamePattern: string): IZResultSet;
3247 var
3248  SQL: string;
3249 begin
3250  Result:=inherited UncachedGetSequences(Catalog, SchemaPattern, SequenceNamePattern);
3251 
3252  SQL := ' SELECT nspname, relname ' +
3253  'FROM pg_catalog.pg_namespace n, pg_catalog.pg_class ct ' +
3254  'WHERE relkind = ''S'' ' +
3255  'AND n.oid = ct.relnamespace';
3256 
3257  if SequenceNamePattern <> '' then
3258  SQL := SQL + ' AND ' + Format('relname = ''%s''', [SequenceNamePattern]);
3259  if SchemaPattern <> '' then
3260  SQL := SQL + ' AND ' + Format('nspname = ''%s''', [SchemaPattern]);
3261 
3262  with GetConnection.CreateStatement.ExecuteQuery(SQL) do
3263  begin
3264  while Next do
3265  begin
3266  Result.MoveToInsertRow;
3267  Result.UpdateNull(1);
3268  Result.UpdateString(2, GetStringByName('nspname'));
3269  Result.UpdateString(3, GetStringByName('relname'));
3270  Result.InsertRow;
3271  end;
3272  Close;
3273  end;
3274 end;
3275 
3276 function TZPostgreSQLDatabaseMetadata.GetPostgreSQLType(Oid: Integer): string;
3277 begin
3278  Result := (GetConnection as IZPostgreSQLConnection).GetTypeNameByOid(Oid);
3279 end;
3280 
3281 function TZPostgreSQLDatabaseMetadata.GetSQLTypeByOid(Oid: Integer): TZSQLType;
3282 var
3283  PostgreSQLConnection: IZPostgreSQLConnection;
3284 begin
3285  PostgreSQLConnection := GetConnection as IZPostgreSQLConnection;
3286  Result := PostgreSQLToSQLType(PostgreSQLConnection,
3287  PostgreSQLConnection.GetTypeNameByOid(Oid));
3288 end;
3289 
3290 function TZPostgreSQLDatabaseMetadata.GetSQLTypeByName(
3291  TypeName: string): TZSQLType;
3292 begin
3293  Result := PostgreSQLToSQLType(
3294  GetConnection as IZPostgreSQLConnection, TypeName);
3295 end;
3296 
3297 function TZPostgreSQLDatabaseMetadata.TableTypeSQLExpression(
3298  TableType: string; UseSchemas: Boolean): string;
3299 begin
3300  if UseSchemas then
3301  begin
3302  if TableType = 'TABLE' then
3303  Result := ' c.relkind = ''r'' AND n.nspname NOT LIKE ''pg\\_%'' '
3304  else if TableType = 'VIEW' then
3305  Result := ' c.relkind = ''v'' AND n.nspname <> ''pg_catalog'' '
3306  else if TableType = 'INDEX' then
3307  Result := ' c.relkind = ''i'' AND n.nspname NOT LIKE ''pg\\_%'' '
3308  else if TableType = 'SEQUENCE' then
3309  Result := ' c.relkind = ''S'' '
3310  else if TableType = 'SYSTEM TABLE' then
3311  Result := ' c.relkind = ''r'' AND n.nspname = ''pg_catalog'' '
3312  else if TableType = 'SYSTEM TOAST TABLE' then
3313  Result := ' c.relkind = ''r'' AND n.nspname = ''pg_toast'' '
3314  else if TableType = 'SYSTEM TOAST INDEX' then
3315  Result := ' c.relkind = ''i'' AND n.nspname = ''pg_toast'' '
3316  else if TableType = 'SYSTEM VIEW' then
3317  Result := ' c.relkind = ''v'' AND n.nspname = ''pg_catalog'' '
3318  else if TableType = 'SYSTEM INDEX' then
3319  Result := ' c.relkind = ''i'' AND n.nspname = ''pg_catalog'' '
3320  else if TableType = 'TEMPORARY TABLE' then
3321  Result := ' c.relkind = ''r'' AND n.nspname LIKE ''pg\\_temp\\_%'' '
3322  else if TableType = 'TEMPORARY INDEX' then
3323  Result := 'c.relkind = ''i'' AND n.nspname LIKE ''pg\\_temp\\_%'' ';
3324  end
3325  else
3326  begin
3327  if TableType = 'TABLE' then
3328  Result := ' c.relkind = ''r'' AND c.relname NOT LIKE ''pg\\_%'' '
3329  else if TableType = 'VIEW' then
3330  Result := ' c.relkind = ''v'' AND c.relname NOT LIKE ''pg\\_%'' '
3331  else if TableType = 'INDEX' then
3332  Result := ' c.relkind = ''i'' AND c.relname NOT LIKE ''pg\\_%'' '
3333  else if TableType = 'SEQUENCE' then
3334  Result := ' c.relkind = ''S'' '
3335  else if TableType = 'SYSTEM TABLE' then
3336  Result := ' c.relkind = ''r'' AND c.relname LIKE ''pg\\_%'' AND c.relname '+
3337  'NOT LIKE ''pg\\_toast\\_%'' AND c.relname NOT LIKE ''pg\\_temp\\_%'' '
3338  else if TableType = 'SYSTEM TOAST TABLE' then
3339  Result := ' c.relkind = ''r'' AND c.relname LIKE ''pg\\_toast\\_%'' '
3340  else if TableType = 'SYSTEM TOAST INDEX' then
3341  Result := ' c.relkind = ''i'' AND c.relname LIKE ''pg\\_toast\\_%'' '
3342  else if TableType = 'SYSTEM VIEW' then
3343  Result := 'c.relkind = ''v'' AND c.relname LIKE ''pg\\_%'''
3344  else if TableType = 'SYSTEM INDEX' then
3345  begin
3346  Result := ' c.relkind = ''v'' AND c.relname LIKE ''pg\\_%'' AND '+
3347  'c.relname NOT LIKE ''pg\\_toast\\_%'' AND c.relname '+
3348  'NOT LIKE ''pg\\_temp\\_%'' '
3349  end
3350  else if TableType = 'TEMPORARY TABLE' then
3351  Result := ' c.relkind = ''r'' AND c.relname LIKE ''pg\\_temp\\_%'' '
3352  else if TableType = 'TEMPORARY INDEX' then
3353  Result := ' c.relkind = ''i'' AND c.relname LIKE ''pg\\_temp\\_%'' '
3354  end;
3355 end;
3356 
3357 procedure TZPostgreSQLDatabaseMetadata.ParseACLArray(
3358  List: TStrings; AclString: string);
3359 var
3360  PrevChar: Char;
3361  InQuotes: Boolean;
3362  I, BeginIndex: Integer;
3363 begin
3364  if AclString = '' then Exit;
3365  InQuotes := False;
3366  PrevChar := ' ';
3367  BeginIndex := 2;
3368  for I := BeginIndex to Length(AclString) do
3369  begin
3370  if (AclString[I] = '"') and (PrevChar <> '\' ) then
3371  InQuotes := not InQuotes
3372  else if (AclString[I] = ',') and not InQuotes then
3373  begin
3374  List.Add(Copy(AclString, BeginIndex, I - BeginIndex));
3375  BeginIndex := I+1;
3376  end;
3377  PrevChar := AclString[I];
3378  end;
3379 
3380  // add last element removing the trailing "}"
3381  List.Add(Copy(AclString, BeginIndex, Length(AclString) - BeginIndex));
3382 
3383  // Strip out enclosing quotes, if any.
3384  for I := 0 to List.Count-1 do
3385  begin
3386  if (List.Strings[i][1] = '"')
3387  and (List.Strings[i][Length(List.Strings[i])] = '"') then
3388  List.Strings[i] := Copy(List.Strings[i], 2, Length(List.Strings[i])-2);
3389  end;
3390 end;
3391 
3392 function TZPostgreSQLDatabaseMetadata.GetPrivilegeName(Permission: Char): string;
3393 begin
3394  case Permission of
3395  'a': Result := 'INSERT';
3396  'r': Result := 'SELECT';
3397  'w': Result := 'UPDATE';
3398  'd': Result := 'DELETE';
3399  'R': Result := 'RULE';
3400  'x': Result := 'REFERENCES';
3401  't': Result := 'TRIGGER';
3402  'X': Result := 'EXECUTE';
3403  'U': Result := 'USAGE';
3404  'C': Result := 'CREATE';
3405  'T': Result := 'CREATE TEMP';
3406  else Result := 'UNKNOWN';
3407  end;
3408 end;
3409 
3410 function TZPostgreSQLDatabaseMetadata.GetIdentifierConvertor: IZIdentifierConvertor;
3411 begin
3412  Result:=TZPostgreSQLIdentifierConvertor.Create(Self);
3413 end;
3414 
3415 {**
3416  Gets the all supported CharacterSets:
3417  @return <code>ResultSet</code> - each row is a CharacterSetName and it's ID
3418 }
3419 function TZPostgreSQLDatabaseMetadata.UncachedGetCharacterSets: IZResultSet; //EgonHugeist
3420 begin
3421  Self.GetConnection.CreateStatement.Execute(
3422  ' CREATE OR REPLACE FUNCTION get_encodings() RETURNS INTEGER AS '''+
3423  ' DECLARE '+
3424  ' enc INTEGER := 0; '+
3425  ' name VARCHAR; '+
3426  ' BEGIN '+
3427  ' CREATE TEMP TABLE encodings ( enc_code int, enc_name text ); '+
3428  ' LOOP '+
3429  ' SELECT INTO name pg_encoding_to_char( enc ); '+
3430  ' IF( name = '''''''' ) THEN '+
3431  ' EXIT; '+
3432  ' ELSE '+
3433  ' INSERT INTO encodings VALUES( enc, name ); '+
3434  ' END IF; '+
3435  ' enc := enc + 1; '+
3436  ' END LOOP; '+
3437  ' RETURN enc; '+
3438  ' END; '+
3439  ''' LANGUAGE ''plpgsql'';');
3440  Self.GetConnection.CreateStatement.ExecuteQuery('select get_encodings();').Close;
3441 
3442  Result:=inherited UncachedGetCharacterSets;
3443 
3444  with Self.GetConnection.CreateStatement.ExecuteQuery(
3445  'select * from encodings;') do
3446  begin
3447  while Next do
3448  begin
3449  Result.MoveToInsertRow;
3450  Result.UpdateString(1, GetString(2)); //CHARACTER_SET_NAME
3451  Result.UpdateShort(2, GetShort(1)); //CHARACTER_SET_ID
3452  Result.InsertRow;
3453  end;
3454  CLose;
3455  end;
3456 end;
3457 
3458 { TZPostgresIdentifierConvertor }
3459 
3460 function TZPostgreSQLIdentifierConvertor.ExtractQuote(
3461  const Value: string): string;
3462 var
3463  QuoteDelim: string;
3464 begin
3465  QuoteDelim := Metadata.GetDatabaseInfo.GetIdentifierQuoteString;
3466  Result := Value;
3467  if (QuoteDelim <> '') and (Value <> '') then
3468  if (Value[1]=QuoteDelim[1]) and
3469  (Value[PLongInt(NativeUInt(Value) - 4)^{fast Length()}]=QuoteDelim[1]) then
3470  begin
3471  Result:=copy(Value,2,length(Value)-2);
3472  Result:=StringReplace(Result,QuoteDelim+QuoteDelim,QuoteDelim,[rfReplaceAll]);
3473  end
3474  else
3475  Result := AnsiLowerCase(Value);
3476 
3477 end;
3478 
3479 function TZPostgreSQLIdentifierConvertor.IsQuoted(const Value: string): Boolean;
3480 var
3481  QuoteDelim: string;
3482 begin
3483  QuoteDelim := Metadata.GetDatabaseInfo.GetIdentifierQuoteString;
3484  Result := (QuoteDelim <> '') and (Value <> '') and
3485  (Value[1]=QuoteDelim[1]) and
3486  (Value[PLongInt(NativeUInt(Value) - 4)^{fast Length()}]=QuoteDelim[1]);
3487 end;
3488 
3489 function TZPostgreSQLIdentifierConvertor.IsSpecialCase(
3490  const Value: string): Boolean;
3491 var
3492  I: Integer;
3493 begin
3494  Result := False;
3495  if not CharInSet(Value[1], ['a'..'z','_']) then
3496  begin
3497  Result := True;
3498  Exit;
3499  end;
3500  for I := 1 to Length(Value) do
3501  begin
3502  if not CharInSet(Value[I], ['A'..'Z','a'..'z','0'..'9','_']) then
3503  begin
3504  Result := True;
3505  Break;
3506  end;
3507  end;
3508 end;
3509 
3510 function TZPostgreSQLIdentifierConvertor.Quote(const Value: string): string;
3511 var
3512  QuoteDelim: string;
3513 begin
3514  Result := Value;
3515  if IsCaseSensitive(Value) then
3516  begin
3517  QuoteDelim := Metadata.GetDatabaseInfo.GetIdentifierQuoteString;
3518  Result := QuoteDelim +
3519  StringReplace(Result,QuoteDelim,QuoteDelim+QuoteDelim,[rfReplaceAll]) +
3520  QuoteDelim;
3521  end;
3522 end;
3523 
3524 end.