1 {*********************************************************}
3 { Zeos Database Objects }
4 { PostgreSQL Database Connectivity Classes }
6 { Originally written by Sergey Seroukhov }
8 {*********************************************************}
10 {@********************************************************}
11 { Copyright (c) 1999-2012 Zeos Development Group }
13 { License Agreement: }
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. }
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. }
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) }
46 { http://www.sourceforge.net/projects/zeoslib. }
49 { Zeos Development Group. }
50 {********************************************************@}
52 unit ZDbcPostgreSqlResultSet;
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;
65 {** Implements PostgreSQL ResultSet. }
66 TZPostgreSQLResultSet = class(TZAbstractResultSet)
68 FHandle: PZPostgreSQLConnect;
69 FQueryHandle: PZPostgreSQLResult;
70 FPlainDriver: IZPostgreSQLPlainDriver;
72 FUndefinedVarcharAsStringLength: Integer;
74 function InternalGetString(ColumnIndex: Integer): RawByteString; override;
75 procedure Open; override;
76 procedure DefinePostgreSQLToSQLType(ColumnInfo: TZColumnInfo; const TypeOid: Oid);
78 constructor Create(PlainDriver: IZPostgreSQLPlainDriver;
79 Statement: IZStatement; SQL: string; Handle: PZPostgreSQLConnect;
80 QueryHandle: PZPostgreSQLResult; Chunk_Size: Integer);
82 procedure Close; override;
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;
100 function MoveAbsolute(Row: Integer): Boolean; override;
103 {** Represents an interface, specific for PostgreSQL blobs. }
104 IZPostgreSQLBlob = interface(IZBlob)
105 ['{BDFB6B80-477D-4CB1-9508-9541FEA6CD72}']
106 function GetBlobOid: Oid;
111 {** Implements external blob wrapper object for PostgreSQL. }
112 TZPostgreSQLBlob = class(TZAbstractBlob, IZPostgreSQLBlob)
114 FHandle: PZPostgreSQLConnect;
116 FPlainDriver: IZPostgreSQLPlainDriver;
117 FChunk_Size: Integer;
119 constructor Create(PlainDriver: IZPostgreSQLPlainDriver; Data: Pointer;
120 Size: Integer; Handle: PZPostgreSQLConnect; BlobOid: Oid; Chunk_Size: Integer);
122 destructor Destroy; override;
124 function GetBlobOid: Oid;
128 function IsEmpty: Boolean; override;
129 function Clone: IZBlob; override;
131 function GetStream: TStream; override;
137 Math, ZMessages, ZMatchPattern, ZDbcPostgreSql, ZDbcUtils, ZEncoding,
138 ZDbcPostgreSqlUtils{$IFDEF WITH_UNITANSISTRINGS}, AnsiStrings{$ENDIF};
140 { TZPostgreSQLResultSet }
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.
150 constructor TZPostgreSQLResultSet.Create(PlainDriver: IZPostgreSQLPlainDriver;
151 Statement: IZStatement; SQL: string; Handle: PZPostgreSQLConnect;
152 QueryHandle: PZPostgreSQLResult; Chunk_Size: Integer);
154 inherited Create(Statement, SQL, nil, Statement.GetConnection.GetConSettings);
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;
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.
173 procedure TZPostgreSQLResultSet.DefinePostgreSQLToSQLType(
174 ColumnInfo: TZColumnInfo; const TypeOid: Oid);
177 Connection: IZPostgreSQLConnection;
179 Connection := Statement.GetConnection as IZPostgreSQLConnection;
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
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
194 if Connection.IsOidAsBlob then
195 ColumnInfo.Precision := 256;
198 SQLType := PostgreSQLToSQLType(ConSettings, Connection.IsOidAsBlob, TypeOid);
200 if SQLType <> stUnknown then
201 ColumnInfo.ColumnType := SQLType
204 ColumnInfo.ColumnType := stString;
205 ColumnInfo.Precision := 255;
206 ColumnInfo.ReadOnly := True;
211 Opens this recordset.
213 procedure TZPostgreSQLResultSet.Open;
215 I, FieldCount: Integer;
216 ColumnInfo: TZColumnInfo;
217 FieldMode, FieldSize, FieldType: Integer;
218 TableInfo: PZPGTableInfo;
219 Connection: IZPostgreSQLConnection;
221 if ResultSetConcurrency = rcUpdatable then
222 raise EZSQLException.Create(SLiveResultSetsAreNotSupported);
224 if not Assigned(FQueryHandle) then
225 raise EZSQLException.Create(SCanNotRetrieveResultSetData);
227 Connection := Statement.GetConnection as IZPostgreSQLConnection;
229 LastRowNo := FPlainDriver.GetRowCount(FQueryHandle);
231 { Fills the column info. }
233 FieldCount := FPlainDriver.GetFieldCount(FQueryHandle);
234 for I := 0 to FieldCount - 1 do
236 ColumnInfo := TZColumnInfo.Create;
239 if Statement.GetResultSetConcurrency = rcUpdatable then //exclude system-tables and if no updates happen -> useless
240 TableInfo := Connection.GetTableInfo(FPlainDriver.GetFieldTableOID(FQueryHandle, I),FieldCount)
243 if TableInfo = nil then
251 SchemaName := TableInfo^.Schema;
252 TableName := TableInfo^.Name;
253 ColumnName := TableInfo^.ColNames[FplainDriver.GetFieldTableColIdx(FQueryHandle, I) - 1];
255 ColumnLabel := ZDbcString(FPlainDriver.GetFieldName(FQueryHandle, I));
256 ColumnDisplaySize := 0;
260 AutoIncrement := False;
262 Nullable := ntNullable;
264 FieldType := FPlainDriver.GetFieldType(FQueryHandle, I);
265 DefinePostgreSQLToSQLType(ColumnInfo, FieldType);
267 if Precision = 0 then
269 FieldMode := FPlainDriver.GetFieldMode(FQueryHandle, I);
270 FieldSize := FPlainDriver.GetFieldSize(FQueryHandle, I);
271 Precision := Max(Max(FieldMode - 4, FieldSize), 0);
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)
281 DefinePostgreSQLToSQLType(ColumnInfo, 25) //assume text instead!
283 if ( (ColumnLabel = 'expr') or ( Precision = 0 ) ) then
284 Precision := GetFieldSize(ColumnType, ConSettings, 255,
285 ConSettings.ClientCodePage^.CharWidth, nil, True)
287 Precision := GetFieldSize(ColumnType, ConSettings, Precision,
288 ConSettings.ClientCodePage^.CharWidth, @ColumnDisplaySize);
292 ColumnsInfo.Add(ColumnInfo);
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.
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.
311 procedure TZPostgreSQLResultSet.Close;
313 if FQueryHandle <> nil then
314 FPlainDriver.Clear(FQueryHandle);
321 Indicates if the value of the designated column in the current row
322 of this <code>ResultSet</code> object is Null.
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.
328 function TZPostgreSQLResultSet.IsNull(ColumnIndex: Integer): Boolean;
330 {$IFNDEF DISABLE_CHECKING}
332 if (RowNo < 1) or (RowNo > LastRowNo) then
333 raise EZSQLException.Create(SRowDataIsNotAvailable);
336 Result := FPlainDriver.GetIsNull(FQueryHandle, RowNo - 1,
337 ColumnIndex - 1) <> 0;
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.
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>
349 function TZPostgreSQLResultSet.InternalGetString(ColumnIndex: Integer): RawByteString;
350 {$IFDEF WITH_RAWBYTESTRING}
354 ColumnIndex := ColumnIndex - 1;
355 LastWasNull := FPlainDriver.GetIsNull(FQueryHandle, RowNo - 1,
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);
362 SetString(Result, FPlainDriver.GetValue(FQueryHandle, RowNo - 1, ColumnIndex),
363 FPlainDriver.GetLength(FQueryHandle, RowNo - 1, ColumnIndex));
365 if FPlainDriver.GetFieldType(FQueryHandle, ColumnIndex) = 1042 then
366 Result := {$IFDEF WITH_UNITANSISTRINGS}AnsiStrings.{$ENDIF}TrimRight(Result);
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.
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>
378 function TZPostgreSQLResultSet.GetBoolean(ColumnIndex: Integer): Boolean;
382 {$IFNDEF DISABLE_CHECKING}
383 CheckColumnConvertion(ColumnIndex, stBoolean);
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);
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.
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>
399 function TZPostgreSQLResultSet.GetByte(ColumnIndex: Integer): Byte;
401 {$IFNDEF DISABLE_CHECKING}
402 CheckColumnConvertion(ColumnIndex, stByte);
404 Result := Byte(StrToIntDef(String(InternalGetString(ColumnIndex)), 0));
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.
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>
416 function TZPostgreSQLResultSet.GetShort(ColumnIndex: Integer): SmallInt;
418 {$IFNDEF DISABLE_CHECKING}
419 CheckColumnConvertion(ColumnIndex, stShort);
421 Result := SmallInt(StrToIntDef(String(InternalGetString(ColumnIndex)), 0));
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.
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>
433 function TZPostgreSQLResultSet.GetInt(ColumnIndex: Integer): Integer;
435 {$IFNDEF DISABLE_CHECKING}
436 CheckColumnConvertion(ColumnIndex, stInteger);
438 Result := StrToIntDef(String(InternalGetString(ColumnIndex)), 0);
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.
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>
450 function TZPostgreSQLResultSet.GetLong(ColumnIndex: Integer): Int64;
452 {$IFNDEF DISABLE_CHECKING}
453 CheckColumnConvertion(ColumnIndex, stLong);
455 Result := StrToInt64Def(String(InternalGetString(ColumnIndex)), 0);
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.
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>
467 function TZPostgreSQLResultSet.GetFloat(ColumnIndex: Integer): Single;
469 {$IFNDEF DISABLE_CHECKING}
470 CheckColumnConvertion(ColumnIndex, stFloat);
472 Result := SQLStrToFloatDef(InternalGetString(ColumnIndex), 0);
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.
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>
484 function TZPostgreSQLResultSet.GetDouble(ColumnIndex: Integer): Double;
486 {$IFNDEF DISABLE_CHECKING}
487 CheckColumnConvertion(ColumnIndex, stDouble);
489 Result := SQLStrToFloatDef(InternalGetString(ColumnIndex), 0);
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.
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>
502 function TZPostgreSQLResultSet.GetBigDecimal(ColumnIndex: Integer): Extended;
504 {$IFNDEF DISABLE_CHECKING}
505 CheckColumnConvertion(ColumnIndex, stBigDecimal);
507 Result := SQLStrToFloatDef(InternalGetString(ColumnIndex), 0);
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.
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>
520 function TZPostgreSQLResultSet.GetBytes(ColumnIndex: Integer): TByteDynArray;
522 {$IFNDEF DISABLE_CHECKING}
523 CheckColumnConvertion(ColumnIndex, stBytes);
525 Result := StrToBytes(DecodeString(InternalGetString(ColumnIndex)));
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.
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>
537 function TZPostgreSQLResultSet.GetDate(ColumnIndex: Integer): TDateTime;
541 {$IFNDEF DISABLE_CHECKING}
542 CheckColumnConvertion(ColumnIndex, stDate);
544 Value := String(InternalGetString(ColumnIndex));
545 if IsMatch('????-??-??*', Value) then
546 Result := Trunc(AnsiSQLDateToDateTime(Value))
548 Result := Trunc(TimestampStrToDateTime(Value));
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.
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>
560 function TZPostgreSQLResultSet.GetTime(ColumnIndex: Integer): TDateTime;
564 {$IFNDEF DISABLE_CHECKING}
565 CheckColumnConvertion(ColumnIndex, stTime);
567 Value := String(InternalGetString(ColumnIndex));
568 if IsMatch('*??:??:??*', Value) then
569 Result := Frac(AnsiSQLDateToDateTime(Value))
571 Result := Frac(TimestampStrToDateTime(Value));
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.
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
584 function TZPostgreSQLResultSet.GetTimestamp(ColumnIndex: Integer): TDateTime;
588 {$IFNDEF DISABLE_CHECKING}
589 CheckColumnConvertion(ColumnIndex, stTimestamp);
591 Value := String(InternalGetString(ColumnIndex));
592 if IsMatch('????-??-??*', Value) then
593 Result := AnsiSQLDateToDateTime(Value)
595 Result := TimestampStrToDateTime(Value);
598 function TZPostgreSQLResultSet.GetUnicodeStream(ColumnIndex: Integer): TStream;
600 {$IFNDEF DISABLE_CHECKING}
601 CheckColumnConvertion(ColumnIndex, stUnicodeStream);
603 Result := TStringStream.Create(InternalGetString(ColumnIndex));
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.
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
615 function TZPostgreSQLResultSet.GetBlob(ColumnIndex: Integer): IZBlob;
619 Connection: IZConnection;
622 {$IFNDEF DISABLE_CHECKING}
623 CheckBlobColumn(ColumnIndex);
625 if (RowNo < 1) or (RowNo > LastRowNo) then
626 raise EZSQLException.Create(SRowDataIsNotAvailable);
629 Connection := Statement.GetConnection;
630 if (GetMetadata.GetColumnType(ColumnIndex) = stBinaryStream)
631 and (Connection as IZPostgreSQLConnection).IsOidAsBlob then
633 if FPlainDriver.GetIsNull(FQueryHandle, RowNo - 1, ColumnIndex - 1) = 0 then
634 BlobOid := StrToIntDef(String(InternalGetString(ColumnIndex)), 0)
638 Result := TZPostgreSQLBlob.Create(FPlainDriver, nil, 0, FHandle, BlobOid, FChunk_Size);
642 if FPlainDriver.GetIsNull(FQueryHandle, RowNo - 1, ColumnIndex - 1) = 0 then
646 case GetMetadata.GetColumnType(ColumnIndex) of
648 Stream := TStringStream.Create(FPlainDriver.DecodeBYTEA(InternalGetString(ColumnIndex),
649 (Connection as IZPostgreSQLConnection).Is_bytea_output_hex, Self.FHandle));
651 Stream := TStringStream.Create(GetValidatedAnsiString(InternalGetString(ColumnIndex), ConSettings, True));
654 WS := ZDbcUnicodeString(InternalGetString(ColumnIndex));
655 Stream := WideStringStream(Ws);
658 Result := TZAbstractBlob.CreateWithStream(Stream, GetStatement.GetConnection,
659 GetMetadata.GetColumnType(ColumnIndex) = stUnicodeStream);
661 if Assigned(Stream) then
666 Result := TZAbstractBlob.CreateWithStream(nil, GetStatement.GetConnection);
671 Moves the cursor to the given row number in
672 this <code>ResultSet</code> object.
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
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.
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
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>.
694 @return <code>true</code> if the cursor is on the result set;
695 <code>false</code> otherwise
697 function TZPostgreSQLResultSet.MoveAbsolute(Row: Integer): Boolean;
699 {$IFNDEF DISABLE_CHECKING}
703 { Checks for maximum row. }
705 if (MaxRows > 0) and (Row > MaxRows) then
708 { Processes negative rows. }
711 Row := LastRowNo - Row + 1;
716 if (ResultSetType <> rtForwardOnly) or (Row >= RowNo) then
718 if (Row >= 0) and (Row <= LastRowNo + 1) then
721 Result := (Row >= 1) and (Row <= LastRowNo);
727 RaiseForwardOnlyException;
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.
739 constructor TZPostgreSQLBlob.Create(PlainDriver: IZPostgreSQLPlainDriver;
740 Data: Pointer; Size: Integer; Handle: PZPostgreSQLConnect; BlobOid: Oid;
741 Chunk_Size: Integer);
743 inherited CreateWithData(Data, Size, nil);
746 FPlainDriver := PlainDriver;
747 FChunk_Size := Chunk_Size;
751 Destroys this object and cleanups the memory.
753 destructor TZPostgreSQLBlob.Destroy;
759 Gets the blob handle oid.
760 @return the blob handle oid.
762 function TZPostgreSQLBlob.GetBlobOid: Oid;
768 Reads the blob by the blob handle.
770 procedure TZPostgreSQLBlob.ReadBlob;
773 Buffer: array[0..1024] of AnsiChar;
775 ReadStream: TMemoryStream;
777 if not Updated and (FBlobOid > 0) then
779 BlobHandle := FPlainDriver.OpenLargeObject(FHandle, FBlobOid, INV_READ);
780 CheckPostgreSQLError(nil, FPlainDriver, FHandle, lcOther, 'Read Large Object',nil);
782 if BlobHandle >= 0 then
784 ReadStream := TMemoryStream.Create;
786 ReadNum := FPlainDriver.ReadLargeObject(FHandle, BlobHandle,
790 ReadStream.SetSize(ReadStream.Size + ReadNum);
791 ReadStream.Write(Buffer, ReadNum);
793 until ReadNum < 1024;
794 FPlainDriver.CloseLargeObject(FHandle, BlobHandle);
795 ReadStream.Position := 0;
797 SetStream(ReadStream);
798 if ReadStream <> nil then
804 Writes the blob by the blob handle.
806 procedure TZPostgreSQLBlob.WriteBlob;
812 { Checks for empty blob. }
819 { Creates a new large object. }
822 FBlobOid := FPlainDriver.CreateLargeObject(FHandle, INV_WRITE);
823 CheckPostgreSQLError(nil, FPlainDriver, FHandle, lcOther, 'Create Large Object',nil);
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);
831 while Position < BlobSize do
833 if (BlobSize - Position) < FChunk_Size then
834 Size := BlobSize - Position
837 FPlainDriver.WriteLargeObject(FHandle, BlobHandle,
838 Pointer(NativeUInt(BlobData) + NativeUInt(Position)), Size);
839 CheckPostgreSQLError(nil, FPlainDriver, FHandle, lcOther, 'Write Large Object',nil);
843 FPlainDriver.CloseLargeObject(FHandle, BlobHandle);
844 CheckPostgreSQLError(nil, FPlainDriver, FHandle, lcOther, 'Close Large Object',nil);
848 Checks if this blob has an empty content.
849 @return <code>True</code> if this blob is empty.
851 function TZPostgreSQLBlob.IsEmpty: Boolean;
854 Result := inherited IsEmpty;
858 Clones this blob object.
859 @return a clonned blob object.
861 function TZPostgreSQLBlob.Clone: IZBlob;
863 Result := TZPostgreSQLBlob.Create(FPlainDriver, BlobData, BlobSize,
864 FHandle, FBlobOid, FChunk_Size);
868 Gets the associated stream object.
869 @return an associated or newly created stream object.
871 function TZPostgreSQLBlob.GetStream: TStream;
874 Result := inherited GetStream;