zeoslib  UNKNOWN
 All Files
ZDbcPostgreSqlResultSet.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 { }
8 {*********************************************************}
9 
10 {@********************************************************}
11 { Copyright (c) 1999-2012 Zeos Development Group }
12 { }
13 { License Agreement: }
14 { }
15 { This library is distributed in the hope that it will be }
16 { useful, but WITHOUT ANY WARRANTY; without even the }
17 { implied warranty of MERCHANTABILITY or FITNESS FOR }
18 { A PARTICULAR PURPOSE. See the GNU Lesser General }
19 { Public License for more details. }
20 { }
21 { The source code of the ZEOS Libraries and packages are }
22 { distributed under the Library GNU General Public }
23 { License (see the file COPYING / COPYING.ZEOS) }
24 { with the following modification: }
25 { As a special exception, the copyright holders of this }
26 { library give you permission to link this library with }
27 { independent modules to produce an executable, }
28 { regardless of the license terms of these independent }
29 { modules, and to copy and distribute the resulting }
30 { executable under terms of your choice, provided that }
31 { you also meet, for each linked independent module, }
32 { the terms and conditions of the license of that module. }
33 { An independent module is a module which is not derived }
34 { from or based on this library. If you modify this }
35 { library, you may extend this exception to your version }
36 { of the library, but you are not obligated to do so. }
37 { If you do not wish to do so, delete this exception }
38 { statement from your version. }
39 { }
40 { }
41 { The project web site is located on: }
42 { http://zeos.firmos.at (FORUM) }
43 { http://sourceforge.net/p/zeoslib/tickets/ (BUGTRACKER)}
44 { svn://svn.code.sf.net/p/zeoslib/code-0/trunk (SVN) }
45 { }
46 { http://www.sourceforge.net/projects/zeoslib. }
47 { }
48 { }
49 { Zeos Development Group. }
50 {********************************************************@}
51 
52 unit ZDbcPostgreSqlResultSet;
53 
54 interface
55 
56 {$I ZDbc.inc}
57 
58 uses
59  {$IFDEF WITH_TOBJECTLIST_INLINE}System.Types, System.Contnrs{$ELSE}Types{$ENDIF},
60  Classes, {$IFDEF MSEgui}mclasses,{$ENDIF} SysUtils,
61  ZSysUtils, ZDbcIntfs, ZDbcResultSet, ZPlainPostgreSqlDriver, ZDbcLogging,
62  ZDbcResultSetMetadata, ZCompatibility;
63 
64 type
65  {** Implements PostgreSQL ResultSet. }
66  TZPostgreSQLResultSet = class(TZAbstractResultSet)
67  private
68  FHandle: PZPostgreSQLConnect;
69  FQueryHandle: PZPostgreSQLResult;
70  FPlainDriver: IZPostgreSQLPlainDriver;
71  FChunk_Size: Integer;
72  FUndefinedVarcharAsStringLength: Integer;
73  protected
74  function InternalGetString(ColumnIndex: Integer): RawByteString; override;
75  procedure Open; override;
76  procedure DefinePostgreSQLToSQLType(ColumnInfo: TZColumnInfo; const TypeOid: Oid);
77  public
78  constructor Create(PlainDriver: IZPostgreSQLPlainDriver;
79  Statement: IZStatement; SQL: string; Handle: PZPostgreSQLConnect;
80  QueryHandle: PZPostgreSQLResult; Chunk_Size: Integer);
81 
82  procedure Close; override;
83 
84  function IsNull(ColumnIndex: Integer): Boolean; override;
85  function GetUnicodeStream(ColumnIndex: Integer): TStream; override;
86  function GetBoolean(ColumnIndex: Integer): Boolean; override;
87  function GetByte(ColumnIndex: Integer): Byte; override;
88  function GetShort(ColumnIndex: Integer): SmallInt; override;
89  function GetInt(ColumnIndex: Integer): Integer; override;
90  function GetLong(ColumnIndex: Integer): Int64; override;
91  function GetFloat(ColumnIndex: Integer): Single; override;
92  function GetDouble(ColumnIndex: Integer): Double; override;
93  function GetBigDecimal(ColumnIndex: Integer): Extended; override;
94  function GetBytes(ColumnIndex: Integer): TByteDynArray; override;
95  function GetDate(ColumnIndex: Integer): TDateTime; override;
96  function GetTime(ColumnIndex: Integer): TDateTime; override;
97  function GetTimestamp(ColumnIndex: Integer): TDateTime; override;
98  function GetBlob(ColumnIndex: Integer): IZBlob; override;
99 
100  function MoveAbsolute(Row: Integer): Boolean; override;
101  end;
102 
103  {** Represents an interface, specific for PostgreSQL blobs. }
104  IZPostgreSQLBlob = interface(IZBlob)
105  ['{BDFB6B80-477D-4CB1-9508-9541FEA6CD72}']
106  function GetBlobOid: Oid;
107  procedure ReadBlob;
108  procedure WriteBlob;
109  end;
110 
111  {** Implements external blob wrapper object for PostgreSQL. }
112  TZPostgreSQLBlob = class(TZAbstractBlob, IZPostgreSQLBlob)
113  private
114  FHandle: PZPostgreSQLConnect;
115  FBlobOid: Oid;
116  FPlainDriver: IZPostgreSQLPlainDriver;
117  FChunk_Size: Integer;
118  public
119  constructor Create(PlainDriver: IZPostgreSQLPlainDriver; Data: Pointer;
120  Size: Integer; Handle: PZPostgreSQLConnect; BlobOid: Oid; Chunk_Size: Integer);
121 
122  destructor Destroy; override;
123 
124  function GetBlobOid: Oid;
125  procedure ReadBlob;
126  procedure WriteBlob;
127 
128  function IsEmpty: Boolean; override;
129  function Clone: IZBlob; override;
130 
131  function GetStream: TStream; override;
132  end;
133 
134 implementation
135 
136 uses
137  Math, ZMessages, ZMatchPattern, ZDbcPostgreSql, ZDbcUtils, ZEncoding,
138  ZDbcPostgreSqlUtils{$IFDEF WITH_UNITANSISTRINGS}, AnsiStrings{$ENDIF};
139 
140 { TZPostgreSQLResultSet }
141 
142 {**
143  Constructs this object, assignes main properties and
144  opens the record set.
145  @param PlainDriver a PostgreSQL plain driver.
146  @param Statement a related SQL statement object.
147  @param SQL a SQL statement.
148  @param Handle a PostgreSQL specific query handle.
149 }
150 constructor TZPostgreSQLResultSet.Create(PlainDriver: IZPostgreSQLPlainDriver;
151  Statement: IZStatement; SQL: string; Handle: PZPostgreSQLConnect;
152  QueryHandle: PZPostgreSQLResult; Chunk_Size: Integer);
153 begin
154  inherited Create(Statement, SQL, nil, Statement.GetConnection.GetConSettings);
155 
156  FHandle := Handle;
157  FQueryHandle := QueryHandle;
158  FPlainDriver := PlainDriver;
159  ResultSetConcurrency := rcReadOnly;
160  FChunk_Size := Chunk_Size; //size of red/write lob chunks
161  FUndefinedVarcharAsStringLength := (Statement.GetConnection as IZPostgreSQLConnection).GetUndefinedVarcharAsStringLength;
162 
163  Open;
164 end;
165 
166 {**
167  Converts a PostgreSQL native types into ZDBC SQL types.
168  @param ColumnIndex a column index.
169  @param ColumnInfo a column description object.
170  @param TypeOid a type oid.
171  @return a SQL undepended type.
172 }
173 procedure TZPostgreSQLResultSet.DefinePostgreSQLToSQLType(
174  ColumnInfo: TZColumnInfo; const TypeOid: Oid);
175 var
176  SQLType: TZSQLType;
177  Connection: IZPostgreSQLConnection;
178 begin
179  Connection := Statement.GetConnection as IZPostgreSQLConnection;
180 
181  case TypeOid of
182  790: ColumnInfo.Currency := True; { money }
183  19: if (Connection.GetServerMajorVersion < 7) or
184  ((Connection.GetServerMajorVersion = 7) and (Connection.GetServerMinorVersion < 3)) then
185  ColumnInfo.Precision := 32
186  else
187  ColumnInfo.Precision := 64; { name }
188  650: ColumnInfo.Precision := 100; { cidr }
189  869: ColumnInfo.Precision := 100; { inet }
190  829: ColumnInfo.Precision := 17; { macaddr }
191  1186: ColumnInfo.Precision := 32; { interval }
192  24: ColumnInfo.Precision := 64; { regproc } // M.A. was 10
193  17:{ bytea }
194  if Connection.IsOidAsBlob then
195  ColumnInfo.Precision := 256;
196  end;
197 
198  SQLType := PostgreSQLToSQLType(ConSettings, Connection.IsOidAsBlob, TypeOid);
199 
200  if SQLType <> stUnknown then
201  ColumnInfo.ColumnType := SQLType
202  else
203  begin
204  ColumnInfo.ColumnType := stString;
205  ColumnInfo.Precision := 255;
206  ColumnInfo.ReadOnly := True;
207  end;
208 end;
209 
210 {**
211  Opens this recordset.
212 }
213 procedure TZPostgreSQLResultSet.Open;
214 var
215  I, FieldCount: Integer;
216  ColumnInfo: TZColumnInfo;
217  FieldMode, FieldSize, FieldType: Integer;
218  TableInfo: PZPGTableInfo;
219  Connection: IZPostgreSQLConnection;
220 begin
221  if ResultSetConcurrency = rcUpdatable then
222  raise EZSQLException.Create(SLiveResultSetsAreNotSupported);
223 
224  if not Assigned(FQueryHandle) then
225  raise EZSQLException.Create(SCanNotRetrieveResultSetData);
226 
227  Connection := Statement.GetConnection as IZPostgreSQLConnection;
228 
229  LastRowNo := FPlainDriver.GetRowCount(FQueryHandle);
230 
231  { Fills the column info. }
232  ColumnsInfo.Clear;
233  FieldCount := FPlainDriver.GetFieldCount(FQueryHandle);
234  for I := 0 to FieldCount - 1 do
235  begin
236  ColumnInfo := TZColumnInfo.Create;
237  with ColumnInfo do
238  begin
239  if Statement.GetResultSetConcurrency = rcUpdatable then //exclude system-tables and if no updates happen -> useless
240  TableInfo := Connection.GetTableInfo(FPlainDriver.GetFieldTableOID(FQueryHandle, I),FieldCount)
241  else
242  TableInfo := nil;
243  if TableInfo = nil then
244  begin
245  SchemaName := '';
246  ColumnName := '';
247  TableName := '';
248  end
249  else
250  begin
251  SchemaName := TableInfo^.Schema;
252  TableName := TableInfo^.Name;
253  ColumnName := TableInfo^.ColNames[FplainDriver.GetFieldTableColIdx(FQueryHandle, I) - 1];
254  end;
255  ColumnLabel := ZDbcString(FPlainDriver.GetFieldName(FQueryHandle, I));
256  ColumnDisplaySize := 0;
257  Scale := 0;
258  Precision := 0;
259 
260  AutoIncrement := False;
261  Signed := False;
262  Nullable := ntNullable;
263 
264  FieldType := FPlainDriver.GetFieldType(FQueryHandle, I);
265  DefinePostgreSQLToSQLType(ColumnInfo, FieldType);
266 
267  if Precision = 0 then
268  begin
269  FieldMode := FPlainDriver.GetFieldMode(FQueryHandle, I);
270  FieldSize := FPlainDriver.GetFieldSize(FQueryHandle, I);
271  Precision := Max(Max(FieldMode - 4, FieldSize), 0);
272 
273  if ColumnType in [stString, stUnicodeString] then
274  {begin patch: varchar() is equal to text!}
275  if ( FieldMode = -1 ) and ( FieldSize = -1 ) and ( FieldType = 1043) then
276  if FUndefinedVarcharAsStringLength > 0 then
277  Precision := GetFieldSize(ColumnType, ConSettings,
278  FUndefinedVarcharAsStringLength,
279  ConSettings.ClientCodePage^.CharWidth, nil, False)
280  else
281  DefinePostgreSQLToSQLType(ColumnInfo, 25) //assume text instead!
282  else
283  if ( (ColumnLabel = 'expr') or ( Precision = 0 ) ) then
284  Precision := GetFieldSize(ColumnType, ConSettings, 255,
285  ConSettings.ClientCodePage^.CharWidth, nil, True)
286  else
287  Precision := GetFieldSize(ColumnType, ConSettings, Precision,
288  ConSettings.ClientCodePage^.CharWidth, @ColumnDisplaySize);
289  end;
290  end;
291 
292  ColumnsInfo.Add(ColumnInfo);
293  end;
294 
295  inherited Open;
296 end;
297 
298 {**
299  Releases this <code>ResultSet</code> object's database and
300  JDBC resources immediately instead of waiting for
301  this to happen when it is automatically closed.
302 
303  <P><B>Note:</B> A <code>ResultSet</code> object
304  is automatically closed by the
305  <code>Statement</code> object that generated it when
306  that <code>Statement</code> object is closed,
307  re-executed, or is used to retrieve the next result from a
308  sequence of multiple results. A <code>ResultSet</code> object
309  is also automatically closed when it is garbage collected.
310 }
311 procedure TZPostgreSQLResultSet.Close;
312 begin
313  if FQueryHandle <> nil then
314  FPlainDriver.Clear(FQueryHandle);
315  FHandle := nil;
316  FQueryHandle := nil;
317  inherited Close;
318 end;
319 
320 {**
321  Indicates if the value of the designated column in the current row
322  of this <code>ResultSet</code> object is Null.
323 
324  @param columnIndex the first column is 1, the second is 2, ...
325  @return if the value is SQL <code>NULL</code>, the
326  value returned is <code>true</code>. <code>false</code> otherwise.
327 }
328 function TZPostgreSQLResultSet.IsNull(ColumnIndex: Integer): Boolean;
329 begin
330 {$IFNDEF DISABLE_CHECKING}
331  CheckClosed;
332  if (RowNo < 1) or (RowNo > LastRowNo) then
333  raise EZSQLException.Create(SRowDataIsNotAvailable);
334 {$ENDIF}
335 
336  Result := FPlainDriver.GetIsNull(FQueryHandle, RowNo - 1,
337  ColumnIndex - 1) <> 0;
338 end;
339 
340 {**
341  Gets the value of the designated column in the current row
342  of this <code>ResultSet</code> object as
343  a <code>String</code> in the Java programming language.
344 
345  @param columnIndex the first column is 1, the second is 2, ...
346  @return the column value; if the value is SQL <code>NULL</code>, the
347  value returned is <code>null</code>
348 }
349 function TZPostgreSQLResultSet.InternalGetString(ColumnIndex: Integer): RawByteString;
350 {$IFDEF WITH_RAWBYTESTRING}
351 var Len: Integer;
352 {$ENDIF}
353 begin
354  ColumnIndex := ColumnIndex - 1;
355  LastWasNull := FPlainDriver.GetIsNull(FQueryHandle, RowNo - 1,
356  ColumnIndex) <> 0;
357  {$IFDEF WITH_RAWBYTESTRING}
358  Len := FPlainDriver.GetLength(FQueryHandle, RowNo - 1, ColumnIndex);
359  SetLength(Result, Len);
360  Move(FPlainDriver.GetValue(FQueryHandle, RowNo - 1, ColumnIndex)^, PAnsiChar(Result)^, Len);
361  {$ELSE}
362  SetString(Result, FPlainDriver.GetValue(FQueryHandle, RowNo - 1, ColumnIndex),
363  FPlainDriver.GetLength(FQueryHandle, RowNo - 1, ColumnIndex));
364  {$ENDIF}
365  if FPlainDriver.GetFieldType(FQueryHandle, ColumnIndex) = 1042 then
366  Result := {$IFDEF WITH_UNITANSISTRINGS}AnsiStrings.{$ENDIF}TrimRight(Result);
367 end;
368 
369 {**
370  Gets the value of the designated column in the current row
371  of this <code>ResultSet</code> object as
372  a <code>boolean</code> in the Java programming language.
373 
374  @param columnIndex the first column is 1, the second is 2, ...
375  @return the column value; if the value is SQL <code>NULL</code>, the
376  value returned is <code>false</code>
377 }
378 function TZPostgreSQLResultSet.GetBoolean(ColumnIndex: Integer): Boolean;
379 var
380  Temp: string;
381 begin
382 {$IFNDEF DISABLE_CHECKING}
383  CheckColumnConvertion(ColumnIndex, stBoolean);
384 {$ENDIF}
385  Temp := UpperCase(String(InternalGetString(ColumnIndex)));
386  Result := (Temp = 'Y') or (Temp = 'YES') or (Temp = 'T') or
387  (Temp = 'TRUE') or (StrToIntDef(String(Temp), 0) <> 0);
388 end;
389 
390 {**
391  Gets the value of the designated column in the current row
392  of this <code>ResultSet</code> object as
393  a <code>byte</code> in the Java programming language.
394 
395  @param columnIndex the first column is 1, the second is 2, ...
396  @return the column value; if the value is SQL <code>NULL</code>, the
397  value returned is <code>0</code>
398 }
399 function TZPostgreSQLResultSet.GetByte(ColumnIndex: Integer): Byte;
400 begin
401 {$IFNDEF DISABLE_CHECKING}
402  CheckColumnConvertion(ColumnIndex, stByte);
403 {$ENDIF}
404  Result := Byte(StrToIntDef(String(InternalGetString(ColumnIndex)), 0));
405 end;
406 
407 {**
408  Gets the value of the designated column in the current row
409  of this <code>ResultSet</code> object as
410  a <code>short</code> in the Java programming language.
411 
412  @param columnIndex the first column is 1, the second is 2, ...
413  @return the column value; if the value is SQL <code>NULL</code>, the
414  value returned is <code>0</code>
415 }
416 function TZPostgreSQLResultSet.GetShort(ColumnIndex: Integer): SmallInt;
417 begin
418 {$IFNDEF DISABLE_CHECKING}
419  CheckColumnConvertion(ColumnIndex, stShort);
420 {$ENDIF}
421  Result := SmallInt(StrToIntDef(String(InternalGetString(ColumnIndex)), 0));
422 end;
423 
424 {**
425  Gets the value of the designated column in the current row
426  of this <code>ResultSet</code> object as
427  an <code>int</code> in the Java programming language.
428 
429  @param columnIndex the first column is 1, the second is 2, ...
430  @return the column value; if the value is SQL <code>NULL</code>, the
431  value returned is <code>0</code>
432 }
433 function TZPostgreSQLResultSet.GetInt(ColumnIndex: Integer): Integer;
434 begin
435 {$IFNDEF DISABLE_CHECKING}
436  CheckColumnConvertion(ColumnIndex, stInteger);
437 {$ENDIF}
438  Result := StrToIntDef(String(InternalGetString(ColumnIndex)), 0);
439 end;
440 
441 {**
442  Gets the value of the designated column in the current row
443  of this <code>ResultSet</code> object as
444  a <code>long</code> in the Java programming language.
445 
446  @param columnIndex the first column is 1, the second is 2, ...
447  @return the column value; if the value is SQL <code>NULL</code>, the
448  value returned is <code>0</code>
449 }
450 function TZPostgreSQLResultSet.GetLong(ColumnIndex: Integer): Int64;
451 begin
452 {$IFNDEF DISABLE_CHECKING}
453  CheckColumnConvertion(ColumnIndex, stLong);
454 {$ENDIF}
455  Result := StrToInt64Def(String(InternalGetString(ColumnIndex)), 0);
456 end;
457 
458 {**
459  Gets the value of the designated column in the current row
460  of this <code>ResultSet</code> object as
461  a <code>float</code> in the Java programming language.
462 
463  @param columnIndex the first column is 1, the second is 2, ...
464  @return the column value; if the value is SQL <code>NULL</code>, the
465  value returned is <code>0</code>
466 }
467 function TZPostgreSQLResultSet.GetFloat(ColumnIndex: Integer): Single;
468 begin
469 {$IFNDEF DISABLE_CHECKING}
470  CheckColumnConvertion(ColumnIndex, stFloat);
471 {$ENDIF}
472  Result := SQLStrToFloatDef(InternalGetString(ColumnIndex), 0);
473 end;
474 
475 {**
476  Gets the value of the designated column in the current row
477  of this <code>ResultSet</code> object as
478  a <code>double</code> in the Java programming language.
479 
480  @param columnIndex the first column is 1, the second is 2, ...
481  @return the column value; if the value is SQL <code>NULL</code>, the
482  value returned is <code>0</code>
483 }
484 function TZPostgreSQLResultSet.GetDouble(ColumnIndex: Integer): Double;
485 begin
486 {$IFNDEF DISABLE_CHECKING}
487  CheckColumnConvertion(ColumnIndex, stDouble);
488 {$ENDIF}
489  Result := SQLStrToFloatDef(InternalGetString(ColumnIndex), 0);
490 end;
491 
492 {**
493  Gets the value of the designated column in the current row
494  of this <code>ResultSet</code> object as
495  a <code>java.sql.BigDecimal</code> in the Java programming language.
496 
497  @param columnIndex the first column is 1, the second is 2, ...
498  @param scale the number of digits to the right of the decimal point
499  @return the column value; if the value is SQL <code>NULL</code>, the
500  value returned is <code>null</code>
501 }
502 function TZPostgreSQLResultSet.GetBigDecimal(ColumnIndex: Integer): Extended;
503 begin
504 {$IFNDEF DISABLE_CHECKING}
505  CheckColumnConvertion(ColumnIndex, stBigDecimal);
506 {$ENDIF}
507  Result := SQLStrToFloatDef(InternalGetString(ColumnIndex), 0);
508 end;
509 
510 {**
511  Gets the value of the designated column in the current row
512  of this <code>ResultSet</code> object as
513  a <code>byte</code> array in the Java programming language.
514  The bytes represent the raw values returned by the driver.
515 
516  @param columnIndex the first column is 1, the second is 2, ...
517  @return the column value; if the value is SQL <code>NULL</code>, the
518  value returned is <code>null</code>
519 }
520 function TZPostgreSQLResultSet.GetBytes(ColumnIndex: Integer): TByteDynArray;
521 begin
522 {$IFNDEF DISABLE_CHECKING}
523  CheckColumnConvertion(ColumnIndex, stBytes);
524 {$ENDIF}
525  Result := StrToBytes(DecodeString(InternalGetString(ColumnIndex)));
526 end;
527 
528 {**
529  Gets the value of the designated column in the current row
530  of this <code>ResultSet</code> object as
531  a <code>java.sql.Date</code> object in the Java programming language.
532 
533  @param columnIndex the first column is 1, the second is 2, ...
534  @return the column value; if the value is SQL <code>NULL</code>, the
535  value returned is <code>null</code>
536 }
537 function TZPostgreSQLResultSet.GetDate(ColumnIndex: Integer): TDateTime;
538 var
539  Value: string;
540 begin
541 {$IFNDEF DISABLE_CHECKING}
542  CheckColumnConvertion(ColumnIndex, stDate);
543 {$ENDIF}
544  Value := String(InternalGetString(ColumnIndex));
545  if IsMatch('????-??-??*', Value) then
546  Result := Trunc(AnsiSQLDateToDateTime(Value))
547  else
548  Result := Trunc(TimestampStrToDateTime(Value));
549 end;
550 
551 {**
552  Gets the value of the designated column in the current row
553  of this <code>ResultSet</code> object as
554  a <code>java.sql.Time</code> object in the Java programming language.
555 
556  @param columnIndex the first column is 1, the second is 2, ...
557  @return the column value; if the value is SQL <code>NULL</code>, the
558  value returned is <code>null</code>
559 }
560 function TZPostgreSQLResultSet.GetTime(ColumnIndex: Integer): TDateTime;
561 var
562  Value: string;
563 begin
564 {$IFNDEF DISABLE_CHECKING}
565  CheckColumnConvertion(ColumnIndex, stTime);
566 {$ENDIF}
567  Value := String(InternalGetString(ColumnIndex));
568  if IsMatch('*??:??:??*', Value) then
569  Result := Frac(AnsiSQLDateToDateTime(Value))
570  else
571  Result := Frac(TimestampStrToDateTime(Value));
572 end;
573 
574 {**
575  Gets the value of the designated column in the current row
576  of this <code>ResultSet</code> object as
577  a <code>java.sql.Timestamp</code> object in the Java programming language.
578 
579  @param columnIndex the first column is 1, the second is 2, ...
580  @return the column value; if the value is SQL <code>NULL</code>, the
581  value returned is <code>null</code>
582  @exception SQLException if a database access error occurs
583 }
584 function TZPostgreSQLResultSet.GetTimestamp(ColumnIndex: Integer): TDateTime;
585 var
586  Value: string;
587 begin
588 {$IFNDEF DISABLE_CHECKING}
589  CheckColumnConvertion(ColumnIndex, stTimestamp);
590 {$ENDIF}
591  Value := String(InternalGetString(ColumnIndex));
592  if IsMatch('????-??-??*', Value) then
593  Result := AnsiSQLDateToDateTime(Value)
594  else
595  Result := TimestampStrToDateTime(Value);
596 end;
597 
598 function TZPostgreSQLResultSet.GetUnicodeStream(ColumnIndex: Integer): TStream;
599 begin
600 {$IFNDEF DISABLE_CHECKING}
601  CheckColumnConvertion(ColumnIndex, stUnicodeStream);
602 {$ENDIF}
603  Result := TStringStream.Create(InternalGetString(ColumnIndex));
604 end;
605 
606 {**
607  Returns the value of the designated column in the current row
608  of this <code>ResultSet</code> object as a <code>Blob</code> object
609  in the Java programming language.
610 
611  @param ColumnIndex the first column is 1, the second is 2, ...
612  @return a <code>Blob</code> object representing the SQL <code>BLOB</code> value in
613  the specified column
614 }
615 function TZPostgreSQLResultSet.GetBlob(ColumnIndex: Integer): IZBlob;
616 var
617  BlobOid: Oid;
618  Stream: TStream;
619  Connection: IZConnection;
620  WS: ZWideString;
621 begin
622 {$IFNDEF DISABLE_CHECKING}
623  CheckBlobColumn(ColumnIndex);
624  CheckClosed;
625  if (RowNo < 1) or (RowNo > LastRowNo) then
626  raise EZSQLException.Create(SRowDataIsNotAvailable);
627 {$ENDIF}
628 
629  Connection := Statement.GetConnection;
630  if (GetMetadata.GetColumnType(ColumnIndex) = stBinaryStream)
631  and (Connection as IZPostgreSQLConnection).IsOidAsBlob then
632  begin
633  if FPlainDriver.GetIsNull(FQueryHandle, RowNo - 1, ColumnIndex - 1) = 0 then
634  BlobOid := StrToIntDef(String(InternalGetString(ColumnIndex)), 0)
635  else
636  BlobOid := 0;
637 
638  Result := TZPostgreSQLBlob.Create(FPlainDriver, nil, 0, FHandle, BlobOid, FChunk_Size);
639  end
640  else
641  begin
642  if FPlainDriver.GetIsNull(FQueryHandle, RowNo - 1, ColumnIndex - 1) = 0 then
643  begin
644  Stream := nil;
645  try
646  case GetMetadata.GetColumnType(ColumnIndex) of
647  stBinaryStream:
648  Stream := TStringStream.Create(FPlainDriver.DecodeBYTEA(InternalGetString(ColumnIndex),
649  (Connection as IZPostgreSQLConnection).Is_bytea_output_hex, Self.FHandle));
650  stAsciiStream:
651  Stream := TStringStream.Create(GetValidatedAnsiString(InternalGetString(ColumnIndex), ConSettings, True));
652  stUnicodeStream:
653  begin
654  WS := ZDbcUnicodeString(InternalGetString(ColumnIndex));
655  Stream := WideStringStream(Ws);
656  end;
657  end;
658  Result := TZAbstractBlob.CreateWithStream(Stream, GetStatement.GetConnection,
659  GetMetadata.GetColumnType(ColumnIndex) = stUnicodeStream);
660  finally
661  if Assigned(Stream) then
662  Stream.Free;
663  end;
664  end
665  else
666  Result := TZAbstractBlob.CreateWithStream(nil, GetStatement.GetConnection);
667  end;
668 end;
669 
670 {**
671  Moves the cursor to the given row number in
672  this <code>ResultSet</code> object.
673 
674  <p>If the row number is positive, the cursor moves to
675  the given row number with respect to the
676  beginning of the result set. The first row is row 1, the second
677  is row 2, and so on.
678 
679  <p>If the given row number is negative, the cursor moves to
680  an absolute row position with respect to
681  the end of the result set. For example, calling the method
682  <code>absolute(-1)</code> positions the
683  cursor on the last row; calling the method <code>absolute(-2)</code>
684  moves the cursor to the next-to-last row, and so on.
685 
686  <p>An attempt to position the cursor beyond the first/last row in
687  the result set leaves the cursor before the first row or after
688  the last row.
689 
690  <p><B>Note:</B> Calling <code>absolute(1)</code> is the same
691  as calling <code>first()</code>. Calling <code>absolute(-1)</code>
692  is the same as calling <code>last()</code>.
693 
694  @return <code>true</code> if the cursor is on the result set;
695  <code>false</code> otherwise
696 }
697 function TZPostgreSQLResultSet.MoveAbsolute(Row: Integer): Boolean;
698 begin
699 {$IFNDEF DISABLE_CHECKING}
700  CheckClosed;
701 {$ENDIF}
702 
703  { Checks for maximum row. }
704  Result := False;
705  if (MaxRows > 0) and (Row > MaxRows) then
706  Exit;
707 
708  { Processes negative rows. }
709  if Row < 0 then
710  begin
711  Row := LastRowNo - Row + 1;
712  if Row < 0 then
713  Row := 0;
714  end;
715 
716  if (ResultSetType <> rtForwardOnly) or (Row >= RowNo) then
717  begin
718  if (Row >= 0) and (Row <= LastRowNo + 1) then
719  begin
720  RowNo := Row;
721  Result := (Row >= 1) and (Row <= LastRowNo);
722  end
723  else
724  Result := False;
725  end
726  else
727  RaiseForwardOnlyException;
728 end;
729 
730 { TZPostgreSQLBlob }
731 
732 {**
733  Constructs this class and assignes the main properties.
734  @param PlainDriver a PostgreSQL plain driver.
735  @param Data a pointer to the blobdata.
736  @param Size the size of the blobdata.
737  @param Handle a PostgreSQL connection reference.
738 }
739 constructor TZPostgreSQLBlob.Create(PlainDriver: IZPostgreSQLPlainDriver;
740  Data: Pointer; Size: Integer; Handle: PZPostgreSQLConnect; BlobOid: Oid;
741  Chunk_Size: Integer);
742 begin
743  inherited CreateWithData(Data, Size, nil);
744  FHandle := Handle;
745  FBlobOid := BlobOid;
746  FPlainDriver := PlainDriver;
747  FChunk_Size := Chunk_Size;
748 end;
749 
750 {**
751  Destroys this object and cleanups the memory.
752 }
753 destructor TZPostgreSQLBlob.Destroy;
754 begin
755  inherited Destroy;
756 end;
757 
758 {**
759  Gets the blob handle oid.
760  @return the blob handle oid.
761 }
762 function TZPostgreSQLBlob.GetBlobOid: Oid;
763 begin
764  Result := FBlobOid;
765 end;
766 
767 {**
768  Reads the blob by the blob handle.
769 }
770 procedure TZPostgreSQLBlob.ReadBlob;
771 var
772  BlobHandle: Integer;
773  Buffer: array[0..1024] of AnsiChar;
774  ReadNum: Integer;
775  ReadStream: TMemoryStream;
776 begin
777  if not Updated and (FBlobOid > 0) then
778  begin
779  BlobHandle := FPlainDriver.OpenLargeObject(FHandle, FBlobOid, INV_READ);
780  CheckPostgreSQLError(nil, FPlainDriver, FHandle, lcOther, 'Read Large Object',nil);
781  ReadStream := nil;
782  if BlobHandle >= 0 then
783  begin
784  ReadStream := TMemoryStream.Create;
785  repeat
786  ReadNum := FPlainDriver.ReadLargeObject(FHandle, BlobHandle,
787  Buffer, 1024);
788  if ReadNum > 0 then
789  begin
790  ReadStream.SetSize(ReadStream.Size + ReadNum);
791  ReadStream.Write(Buffer, ReadNum);
792  end;
793  until ReadNum < 1024;
794  FPlainDriver.CloseLargeObject(FHandle, BlobHandle);
795  ReadStream.Position := 0;
796  end;
797  SetStream(ReadStream);
798  if ReadStream <> nil then
799  ReadStream.free;
800  end;
801 end;
802 
803 {**
804  Writes the blob by the blob handle.
805 }
806 procedure TZPostgreSQLBlob.WriteBlob;
807 var
808  BlobHandle: Integer;
809  Position: Integer;
810  Size: Integer;
811 begin
812  { Checks for empty blob. }
813  if IsEmpty then
814  begin
815  FBlobOid := 0;
816  Exit;
817  end;
818 
819  { Creates a new large object. }
820  if FBlobOid = 0 then
821  begin
822  FBlobOid := FPlainDriver.CreateLargeObject(FHandle, INV_WRITE);
823  CheckPostgreSQLError(nil, FPlainDriver, FHandle, lcOther, 'Create Large Object',nil);
824  end;
825 
826  { Opens and writes a large object. }
827  BlobHandle := FPlainDriver.OpenLargeObject(FHandle, FBlobOid, INV_WRITE);
828  CheckPostgreSQLError(nil, FPlainDriver, FHandle, lcOther, 'Open Large Object',nil);
829 
830  Position := 0;
831  while Position < BlobSize do
832  begin
833  if (BlobSize - Position) < FChunk_Size then
834  Size := BlobSize - Position
835  else
836  Size := FChunk_Size;
837  FPlainDriver.WriteLargeObject(FHandle, BlobHandle,
838  Pointer(NativeUInt(BlobData) + NativeUInt(Position)), Size);
839  CheckPostgreSQLError(nil, FPlainDriver, FHandle, lcOther, 'Write Large Object',nil);
840  Inc(Position, Size);
841  end;
842 
843  FPlainDriver.CloseLargeObject(FHandle, BlobHandle);
844  CheckPostgreSQLError(nil, FPlainDriver, FHandle, lcOther, 'Close Large Object',nil);
845 end;
846 
847 {**
848  Checks if this blob has an empty content.
849  @return <code>True</code> if this blob is empty.
850 }
851 function TZPostgreSQLBlob.IsEmpty: Boolean;
852 begin
853  ReadBlob;
854  Result := inherited IsEmpty;
855 end;
856 
857 {**
858  Clones this blob object.
859  @return a clonned blob object.
860 }
861 function TZPostgreSQLBlob.Clone: IZBlob;
862 begin
863  Result := TZPostgreSQLBlob.Create(FPlainDriver, BlobData, BlobSize,
864  FHandle, FBlobOid, FChunk_Size);
865 end;
866 
867 {**
868  Gets the associated stream object.
869  @return an associated or newly created stream object.
870 }
871 function TZPostgreSQLBlob.GetStream: TStream;
872 begin
873  ReadBlob;
874  Result := inherited GetStream;
875 end;
876 
877 end.