00001 {*********************************************************}
00002 { }
00003 { Zeos Database Objects }
00004 { PostgreSQL Database Connectivity Classes }
00005 { }
00006 { Originally written by Sergey Seroukhov }
00007 { }
00008 {*********************************************************}
00009
00010 {@********************************************************}
00011 { Copyright (c) 1999-2006 Zeos Development Group }
00012 { }
00013 { License Agreement: }
00014 { }
00015 { This library is distributed in the hope that it will be }
00016 { useful, but WITHOUT ANY WARRANTY; without even the }
00017 { implied warranty of MERCHANTABILITY or FITNESS FOR }
00018 { A PARTICULAR PURPOSE. See the GNU Lesser General }
00019 { Public License for more details. }
00020 { }
00021 { The source code of the ZEOS Libraries and packages are }
00022 { distributed under the Library GNU General Public }
00023 { License (see the file COPYING / COPYING.ZEOS) }
00024 { with the following modification: }
00025 { As a special exception, the copyright holders of this }
00026 { library give you permission to link this library with }
00027 { independent modules to produce an executable, }
00028 { regardless of the license terms of these independent }
00029 { modules, and to copy and distribute the resulting }
00030 { executable under terms of your choice, provided that }
00031 { you also meet, for each linked independent module, }
00032 { the terms and conditions of the license of that module. }
00033 { An independent module is a module which is not derived }
00034 { from or based on this library. If you modify this }
00035 { library, you may extend this exception to your version }
00036 { of the library, but you are not obligated to do so. }
00037 { If you do not wish to do so, delete this exception }
00038 { statement from your version. }
00039 { }
00040 { }
00041 { The project web site is located on: }
00042 { http:
00043 { http:
00044 { svn:
00045 { }
00046 { http:
00047 { http:
00048 { }
00049 { }
00050 { }
00051 { Zeos Development Group. }
00052 {********************************************************@}
00053
00054 unit ZDbcPostgreSqlResultSet;
00055
00056 interface
00057
00058 {$I ZDbc.inc}
00059
00060 uses
00061 Classes, SysUtils, ZSysUtils, ZDbcIntfs, ZDbcResultSet,
00062 ZPlainPostgreSqlDriver, ZDbcResultSetMetadata, ZDbcLogging, ZCompatibility;
00063
00064 type
00065
00066 {** Implements PostgreSQL ResultSet. }
00067 TZPostgreSQLResultSet = class(TZAbstractResultSet)
00068 private
00069 FHandle: PZPostgreSQLConnect;
00070 FQueryHandle: PZPostgreSQLResult;
00071 FPlainDriver: IZPostgreSQLPlainDriver;
00072 protected
00073 procedure Open; override;
00074 procedure DefinePostgreSQLToSQLType(ColumnIndex: Integer;
00075 ColumnInfo: TZColumnInfo; TypeOid: Oid);
00076 public
00077 constructor Create(PlainDriver: IZPostgreSQLPlainDriver;
00078 Statement: IZStatement; SQL: string; Handle: PZPostgreSQLConnect;
00079 QueryHandle: PZPostgreSQLResult);
00080 destructor Destroy; override;
00081
00082 procedure Close; override;
00083
00084 function IsNull(ColumnIndex: Integer): Boolean; override;
00085 function GetString(ColumnIndex: Integer): string; override;
00086 function GetBoolean(ColumnIndex: Integer): Boolean; override;
00087 function GetByte(ColumnIndex: Integer): ShortInt; override;
00088 function GetShort(ColumnIndex: Integer): SmallInt; override;
00089 function GetInt(ColumnIndex: Integer): Integer; override;
00090 function GetLong(ColumnIndex: Integer): Int64; override;
00091 function GetFloat(ColumnIndex: Integer): Single; override;
00092 function GetDouble(ColumnIndex: Integer): Double; override;
00093 function GetBigDecimal(ColumnIndex: Integer): Extended; override;
00094 function GetBytes(ColumnIndex: Integer): TByteDynArray; override;
00095 function GetDate(ColumnIndex: Integer): TDateTime; override;
00096 function GetTime(ColumnIndex: Integer): TDateTime; override;
00097 function GetTimestamp(ColumnIndex: Integer): TDateTime; override;
00098 function GetBlob(ColumnIndex: Integer): IZBlob; override;
00099
00100 function MoveAbsolute(Row: Integer): Boolean; override;
00101 end;
00102
00103 {** Represents an interface, specific for PostgreSQL blobs. }
00104 IZPostgreSQLBlob = interface(IZBlob)
00105 ['{BDFB6B80-477D-4CB1-9508-9541FEA6CD72}']
00106 function GetBlobOid: Oid;
00107 procedure ReadBlob;
00108 procedure WriteBlob;
00109 end;
00110
00111 {** Implements external blob wrapper object for PostgreSQL. }
00112 TZPostgreSQLBlob = class(TZAbstractBlob, IZPostgreSQLBlob)
00113 private
00114 FHandle: PZPostgreSQLConnect;
00115 FBlobOid: Oid;
00116 FPlainDriver: IZPostgreSQLPlainDriver;
00117 public
00118 constructor Create(PlainDriver: IZPostgreSQLPlainDriver; Data: Pointer;
00119 Size: Integer; Handle: PZPostgreSQLConnect; BlobOid: Oid);
00120 destructor Destroy; override;
00121
00122 function GetBlobOid: Oid;
00123 procedure ReadBlob;
00124 procedure WriteBlob;
00125
00126 function IsEmpty: Boolean; override;
00127 function Clone: IZBlob; override;
00128
00129 function GetStream: TStream; override;
00130 end;
00131
00132 implementation
00133
00134 uses
00135 Math, ZMessages, ZMatchPattern, ZDbcPostgreSql,
00136 ZDbcPostgreSqlUtils;
00137
00138 { TZPostgreSQLResultSet }
00139
00140 {**
00141 Constructs this object, assignes main properties and
00142 opens the record set.
00143 @param PlainDriver a PostgreSQL plain driver.
00144 @param Statement a related SQL statement object.
00145 @param SQL a SQL statement.
00146 @param Handle a PostgreSQL specific query handle.
00147 }
00148 constructor TZPostgreSQLResultSet.Create(PlainDriver: IZPostgreSQLPlainDriver;
00149 Statement: IZStatement; SQL: string; Handle: PZPostgreSQLConnect;
00150 QueryHandle: PZPostgreSQLResult);
00151 begin
00152 inherited Create(Statement, SQL, nil);
00153
00154 FHandle := Handle;
00155 FQueryHandle := QueryHandle;
00156 FPlainDriver := PlainDriver;
00157 ResultSetConcurrency := rcReadOnly;
00158
00159 Open;
00160 end;
00161
00162 {**
00163 Destroys this object and cleanups the memory.
00164 }
00165 destructor TZPostgreSQLResultSet.Destroy;
00166 begin
00167 inherited Destroy;
00168 end;
00169
00170 {**
00171 Converts a PostgreSQL native types into ZDBC SQL types.
00172 @param ColumnIndex a column index.
00173 @param ColumnInfo a column description object.
00174 @param TypeOid a type oid.
00175 @return a SQL undepended type.
00176 }
00177 procedure TZPostgreSQLResultSet.DefinePostgreSQLToSQLType(ColumnIndex: Integer;
00178 ColumnInfo: TZColumnInfo; TypeOid: Oid);
00179 var
00180 SQLType: TZSQLType;
00181 Connection: IZPostgreSQLConnection;
00182 begin
00183 Connection := Statement.GetConnection as IZPostgreSQLConnection;
00184
00185 case TypeOid of
00186 790: ColumnInfo.Currency := True; { money }
00187 19: ColumnInfo.Precision := 32; { name }
00188 1186: ColumnInfo.Precision := 32; { interval }
00189 24: ColumnInfo.Precision := 10; { regproc }
00190 17:{ bytea }
00191 if Connection.IsOidAsBlob then
00192 ColumnInfo.Precision := 256;
00193 end;
00194
00195 SQLType := PostgreSQLToSQLType(Connection, TypeOid);
00196
00197 if SQLType <> stUnknown then
00198 ColumnInfo.ColumnType := SQLType
00199 else
00200 begin
00201 ColumnInfo.ColumnType := stString;
00202 ColumnInfo.Precision := 255;
00203 ColumnInfo.ReadOnly := True;
00204 end;
00205 end;
00206
00207 {**
00208 Opens this recordset.
00209 }
00210 procedure TZPostgreSQLResultSet.Open;
00211 var
00212 I: Integer;
00213 ColumnInfo: TZColumnInfo;
00214 begin
00215 if ResultSetConcurrency = rcUpdatable then
00216 raise EZSQLException.Create(SLiveResultSetsAreNotSupported);
00217
00218 if not Assigned(FQueryHandle) then
00219 raise EZSQLException.Create(SCanNotRetrieveResultSetData);
00220
00221 LastRowNo := FPlainDriver.GetRowCount(FQueryHandle);
00222
00223 { Fills the column info. }
00224 ColumnsInfo.Clear;
00225 for I := 0 to FPlainDriver.GetFieldCount(FQueryHandle) - 1 do
00226 begin
00227 ColumnInfo := TZColumnInfo.Create;
00228 with ColumnInfo do
00229 begin
00230 ColumnName := '';
00231 TableName := '';
00232 ColumnLabel := StrPas(FPlainDriver.GetFieldName(FQueryHandle, I));
00233 ColumnDisplaySize := 0;
00234 Scale := 0;
00235 Precision := 0;
00236
00237 AutoIncrement := False;
00238 Signed := False;
00239 Nullable := ntNullable;
00240
00241 DefinePostgreSQLToSQLType(I, ColumnInfo,
00242 FPlainDriver.GetFieldType(FQueryHandle, I));
00243
00244 if Precision = 0 then
00245 begin
00246 Precision := Max(Max(FPlainDriver.GetFieldMode(FQueryHandle, I) - 4,
00247 FPlainDriver.GetFieldSize(FQueryHandle, I)), 0);
00248 end;
00249
00250 if ((ColumnType = stString) or (ColumnType = stUnicodeString))
00251 and ((Precision = 0) or (ColumnLabel = 'expr')) then
00252 Precision := 255;
00253 end;
00254
00255 ColumnsInfo.Add(ColumnInfo);
00256 end;
00257
00258 inherited Open;
00259 end;
00260
00261 {**
00262 Releases this <code>ResultSet</code> object's database and
00263 JDBC resources immediately instead of waiting for
00264 this to happen when it is automatically closed.
00265
00266 <P><B>Note:</B> A <code>ResultSet</code> object
00267 is automatically closed by the
00268 <code>Statement</code> object that generated it when
00269 that <code>Statement</code> object is closed,
00270 re-executed, or is used to retrieve the next result from a
00271 sequence of multiple results. A <code>ResultSet</code> object
00272 is also automatically closed when it is garbage collected.
00273 }
00274 procedure TZPostgreSQLResultSet.Close;
00275 begin
00276 if FQueryHandle <> nil then
00277 FPlainDriver.Clear(FQueryHandle);
00278 FHandle := nil;
00279 FQueryHandle := nil;
00280 inherited Close;
00281 end;
00282
00283 {**
00284 Indicates if the value of the designated column in the current row
00285 of this <code>ResultSet</code> object is Null.
00286
00287 @param columnIndex the first column is 1, the second is 2, ...
00288 @return if the value is SQL <code>NULL</code>, the
00289 value returned is <code>true</code>. <code>false</code> otherwise.
00290 }
00291 function TZPostgreSQLResultSet.IsNull(ColumnIndex: Integer): Boolean;
00292 begin
00293 {$IFNDEF DISABLE_CHECKING}
00294 CheckClosed;
00295 if (RowNo < 1) or (RowNo > LastRowNo) then
00296 raise EZSQLException.Create(SRowDataIsNotAvailable);
00297 {$ENDIF}
00298
00299 Result := FPlainDriver.GetIsNull(FQueryHandle, RowNo - 1,
00300 ColumnIndex - 1) <> 0;
00301 end;
00302
00303 {**
00304 Gets the value of the designated column in the current row
00305 of this <code>ResultSet</code> object as
00306 a <code>String</code> in the Java programming language.
00307
00308 @param columnIndex the first column is 1, the second is 2, ...
00309 @return the column value; if the value is SQL <code>NULL</code>, the
00310 value returned is <code>null</code>
00311 }
00312 function TZPostgreSQLResultSet.GetString(ColumnIndex: Integer): string;
00313 begin
00314 {$IFNDEF DISABLE_CHECKING}
00315 CheckClosed;
00316 CheckColumnConvertion(ColumnIndex, stString);
00317 if (RowNo < 1) or (RowNo > LastRowNo) then
00318 raise EZSQLException.Create(SRowDataIsNotAvailable);
00319 {$ENDIF}
00320
00321 ColumnIndex := ColumnIndex - 1;
00322 LastWasNull := FPlainDriver.GetIsNull(FQueryHandle, RowNo - 1,
00323 ColumnIndex) <> 0;
00324 SetString(Result, FPlainDriver.GetValue(FQueryHandle, RowNo - 1, ColumnIndex),
00325 FPlainDriver.GetLength(FQueryHandle, RowNo - 1, ColumnIndex));
00326 if FPlainDriver.GetFieldType(FQueryHandle, ColumnIndex) = 1042 then
00327 Result := TrimRight(Result);
00328 end;
00329
00330 {**
00331 Gets the value of the designated column in the current row
00332 of this <code>ResultSet</code> object as
00333 a <code>boolean</code> in the Java programming language.
00334
00335 @param columnIndex the first column is 1, the second is 2, ...
00336 @return the column value; if the value is SQL <code>NULL</code>, the
00337 value returned is <code>false</code>
00338 }
00339 function TZPostgreSQLResultSet.GetBoolean(ColumnIndex: Integer): Boolean;
00340 var
00341 Temp: string;
00342 begin
00343 {$IFNDEF DISABLE_CHECKING}
00344 CheckColumnConvertion(ColumnIndex, stBoolean);
00345 {$ENDIF}
00346 Temp := UpperCase(GetString(ColumnIndex));
00347 Result := (Temp = 'Y') or (Temp = 'YES') or (Temp = 'T') or
00348 (Temp = 'TRUE') or (StrToIntDef(Temp, 0) <> 0);
00349 end;
00350
00351 {**
00352 Gets the value of the designated column in the current row
00353 of this <code>ResultSet</code> object as
00354 a <code>byte</code> in the Java programming language.
00355
00356 @param columnIndex the first column is 1, the second is 2, ...
00357 @return the column value; if the value is SQL <code>NULL</code>, the
00358 value returned is <code>0</code>
00359 }
00360 function TZPostgreSQLResultSet.GetByte(ColumnIndex: Integer): ShortInt;
00361 begin
00362 {$IFNDEF DISABLE_CHECKING}
00363 CheckColumnConvertion(ColumnIndex, stByte);
00364 {$ENDIF}
00365 Result := ShortInt(StrToIntDef(GetString(ColumnIndex), 0));
00366 end;
00367
00368 {**
00369 Gets the value of the designated column in the current row
00370 of this <code>ResultSet</code> object as
00371 a <code>short</code> in the Java programming language.
00372
00373 @param columnIndex the first column is 1, the second is 2, ...
00374 @return the column value; if the value is SQL <code>NULL</code>, the
00375 value returned is <code>0</code>
00376 }
00377 function TZPostgreSQLResultSet.GetShort(ColumnIndex: Integer): SmallInt;
00378 begin
00379 {$IFNDEF DISABLE_CHECKING}
00380 CheckColumnConvertion(ColumnIndex, stShort);
00381 {$ENDIF}
00382 Result := SmallInt(StrToIntDef(GetString(ColumnIndex), 0));
00383 end;
00384
00385 {**
00386 Gets the value of the designated column in the current row
00387 of this <code>ResultSet</code> object as
00388 an <code>int</code> in the Java programming language.
00389
00390 @param columnIndex the first column is 1, the second is 2, ...
00391 @return the column value; if the value is SQL <code>NULL</code>, the
00392 value returned is <code>0</code>
00393 }
00394 function TZPostgreSQLResultSet.GetInt(ColumnIndex: Integer): Integer;
00395 begin
00396 {$IFNDEF DISABLE_CHECKING}
00397 CheckColumnConvertion(ColumnIndex, stInteger);
00398 {$ENDIF}
00399 Result := StrToIntDef(GetString(ColumnIndex), 0);
00400 end;
00401
00402 {**
00403 Gets the value of the designated column in the current row
00404 of this <code>ResultSet</code> object as
00405 a <code>long</code> in the Java programming language.
00406
00407 @param columnIndex the first column is 1, the second is 2, ...
00408 @return the column value; if the value is SQL <code>NULL</code>, the
00409 value returned is <code>0</code>
00410 }
00411 function TZPostgreSQLResultSet.GetLong(ColumnIndex: Integer): Int64;
00412 begin
00413 {$IFNDEF DISABLE_CHECKING}
00414 CheckColumnConvertion(ColumnIndex, stLong);
00415 {$ENDIF}
00416 Result := StrToInt64Def(GetString(ColumnIndex), 0);
00417 end;
00418
00419 {**
00420 Gets the value of the designated column in the current row
00421 of this <code>ResultSet</code> object as
00422 a <code>float</code> in the Java programming language.
00423
00424 @param columnIndex the first column is 1, the second is 2, ...
00425 @return the column value; if the value is SQL <code>NULL</code>, the
00426 value returned is <code>0</code>
00427 }
00428 function TZPostgreSQLResultSet.GetFloat(ColumnIndex: Integer): Single;
00429 begin
00430 {$IFNDEF DISABLE_CHECKING}
00431 CheckColumnConvertion(ColumnIndex, stFloat);
00432 {$ENDIF}
00433 Result := SQLStrToFloatDef(GetString(ColumnIndex), 0);
00434 end;
00435
00436 {**
00437 Gets the value of the designated column in the current row
00438 of this <code>ResultSet</code> object as
00439 a <code>double</code> in the Java programming language.
00440
00441 @param columnIndex the first column is 1, the second is 2, ...
00442 @return the column value; if the value is SQL <code>NULL</code>, the
00443 value returned is <code>0</code>
00444 }
00445 function TZPostgreSQLResultSet.GetDouble(ColumnIndex: Integer): Double;
00446 begin
00447 {$IFNDEF DISABLE_CHECKING}
00448 CheckColumnConvertion(ColumnIndex, stDouble);
00449 {$ENDIF}
00450 Result := SQLStrToFloatDef(GetString(ColumnIndex), 0);
00451 end;
00452
00453 {**
00454 Gets the value of the designated column in the current row
00455 of this <code>ResultSet</code> object as
00456 a <code>java.sql.BigDecimal</code> in the Java programming language.
00457
00458 @param columnIndex the first column is 1, the second is 2, ...
00459 @param scale the number of digits to the right of the decimal point
00460 @return the column value; if the value is SQL <code>NULL</code>, the
00461 value returned is <code>null</code>
00462 }
00463 function TZPostgreSQLResultSet.GetBigDecimal(ColumnIndex: Integer): Extended;
00464 begin
00465 {$IFNDEF DISABLE_CHECKING}
00466 CheckColumnConvertion(ColumnIndex, stBigDecimal);
00467 {$ENDIF}
00468 Result := SQLStrToFloatDef(GetString(ColumnIndex), 0);
00469 end;
00470
00471 {**
00472 Gets the value of the designated column in the current row
00473 of this <code>ResultSet</code> object as
00474 a <code>byte</code> array in the Java programming language.
00475 The bytes represent the raw values returned by the driver.
00476
00477 @param columnIndex the first column is 1, the second is 2, ...
00478 @return the column value; if the value is SQL <code>NULL</code>, the
00479 value returned is <code>null</code>
00480 }
00481 function TZPostgreSQLResultSet.GetBytes(ColumnIndex: Integer): TByteDynArray;
00482 begin
00483 {$IFNDEF DISABLE_CHECKING}
00484 CheckColumnConvertion(ColumnIndex, stBytes);
00485 {$ENDIF}
00486 Result := StrToBytes(DecodeString(GetString(ColumnIndex)));
00487 end;
00488
00489 {**
00490 Gets the value of the designated column in the current row
00491 of this <code>ResultSet</code> object as
00492 a <code>java.sql.Date</code> object in the Java programming language.
00493
00494 @param columnIndex the first column is 1, the second is 2, ...
00495 @return the column value; if the value is SQL <code>NULL</code>, the
00496 value returned is <code>null</code>
00497 }
00498 function TZPostgreSQLResultSet.GetDate(ColumnIndex: Integer): TDateTime;
00499 var
00500 Value: string;
00501 begin
00502 {$IFNDEF DISABLE_CHECKING}
00503 CheckColumnConvertion(ColumnIndex, stDate);
00504 {$ENDIF}
00505 Value := GetString(ColumnIndex);
00506 if IsMatch('????-??-??*', Value) then
00507 Result := Trunc(AnsiSQLDateToDateTime(Value))
00508 else Result := Trunc(TimestampStrToDateTime(Value));
00509 end;
00510
00511 {**
00512 Gets the value of the designated column in the current row
00513 of this <code>ResultSet</code> object as
00514 a <code>java.sql.Time</code> object in the Java programming language.
00515
00516 @param columnIndex the first column is 1, the second is 2, ...
00517 @return the column value; if the value is SQL <code>NULL</code>, the
00518 value returned is <code>null</code>
00519 }
00520 function TZPostgreSQLResultSet.GetTime(ColumnIndex: Integer): TDateTime;
00521 var
00522 Value: string;
00523 begin
00524 {$IFNDEF DISABLE_CHECKING}
00525 CheckColumnConvertion(ColumnIndex, stTime);
00526 {$ENDIF}
00527 Value := GetString(ColumnIndex);
00528 if IsMatch('*??:??:??*', Value) then
00529 Result := Frac(AnsiSQLDateToDateTime(Value))
00530 else Result := Frac(TimestampStrToDateTime(Value));
00531 end;
00532
00533 {**
00534 Gets the value of the designated column in the current row
00535 of this <code>ResultSet</code> object as
00536 a <code>java.sql.Timestamp</code> object in the Java programming language.
00537
00538 @param columnIndex the first column is 1, the second is 2, ...
00539 @return the column value; if the value is SQL <code>NULL</code>, the
00540 value returned is <code>null</code>
00541 @exception SQLException if a database access error occurs
00542 }
00543 function TZPostgreSQLResultSet.GetTimestamp(ColumnIndex: Integer): TDateTime;
00544 var
00545 Value: string;
00546 begin
00547 {$IFNDEF DISABLE_CHECKING}
00548 CheckColumnConvertion(ColumnIndex, stTimestamp);
00549 {$ENDIF}
00550 Value := GetString(ColumnIndex);
00551 if IsMatch('????-??-??*', Value) then
00552 Result := AnsiSQLDateToDateTime(Value)
00553 else Result := TimestampStrToDateTime(Value);
00554 end;
00555
00556 {**
00557 Returns the value of the designated column in the current row
00558 of this <code>ResultSet</code> object as a <code>Blob</code> object
00559 in the Java programming language.
00560
00561 @param ColumnIndex the first column is 1, the second is 2, ...
00562 @return a <code>Blob</code> object representing the SQL <code>BLOB</code> value in
00563 the specified column
00564 }
00565 function TZPostgreSQLResultSet.GetBlob(ColumnIndex: Integer): IZBlob;
00566 var
00567 BlobOid: Oid;
00568 Stream: TStream;
00569 begin
00570 {$IFNDEF DISABLE_CHECKING}
00571 CheckBlobColumn(ColumnIndex);
00572 CheckClosed;
00573 if (RowNo < 1) or (RowNo > LastRowNo) then
00574 raise EZSQLException.Create(SRowDataIsNotAvailable);
00575 {$ENDIF}
00576
00577 if (GetMetadata.GetColumnType(ColumnIndex) = stBinaryStream)
00578 and (Statement.GetConnection as IZPostgreSQLConnection).IsOidAsBlob then
00579 begin
00580 if FPlainDriver.GetIsNull(FQueryHandle, RowNo - 1, ColumnIndex - 1) = 0 then
00581 BlobOid := StrToIntDef(GetString(ColumnIndex), 0)
00582 else BlobOid := 0;
00583
00584 Result := TZPostgreSQLBlob.Create(FPlainDriver, nil, 0, FHandle, BlobOid);
00585 end
00586 else
00587 begin
00588 if FPlainDriver.GetIsNull(FQueryHandle, RowNo - 1, ColumnIndex - 1) = 0 then
00589 begin
00590 Stream := nil;
00591 try
00592 if GetMetadata.GetColumnType(ColumnIndex) = stBinaryStream then
00593 Stream := TStringStream.Create(FPlainDriver.DecodeBYTEA(GetString(ColumnIndex)))
00594 else
00595 Stream := TStringStream.Create(GetString(ColumnIndex));
00596 Result := TZAbstractBlob.CreateWithStream(Stream);
00597 finally
00598 if Assigned(Stream) then
00599 Stream.Free;
00600 end;
00601 end else
00602 Result := TZAbstractBlob.CreateWithStream(nil);
00603 end;
00604 end;
00605
00606 {**
00607 Moves the cursor to the given row number in
00608 this <code>ResultSet</code> object.
00609
00610 <p>If the row number is positive, the cursor moves to
00611 the given row number with respect to the
00612 beginning of the result set. The first row is row 1, the second
00613 is row 2, and so on.
00614
00615 <p>If the given row number is negative, the cursor moves to
00616 an absolute row position with respect to
00617 the end of the result set. For example, calling the method
00618 <code>absolute(-1)</code> positions the
00619 cursor on the last row; calling the method <code>absolute(-2)</code>
00620 moves the cursor to the next-to-last row, and so on.
00621
00622 <p>An attempt to position the cursor beyond the first/last row in
00623 the result set leaves the cursor before the first row or after
00624 the last row.
00625
00626 <p><B>Note:</B> Calling <code>absolute(1)</code> is the same
00627 as calling <code>first()</code>. Calling <code>absolute(-1)</code>
00628 is the same as calling <code>last()</code>.
00629
00630 @return <code>true</code> if the cursor is on the result set;
00631 <code>false</code> otherwise
00632 }
00633 function TZPostgreSQLResultSet.MoveAbsolute(Row: Integer): Boolean;
00634 begin
00635 {$IFNDEF DISABLE_CHECKING}
00636 CheckClosed;
00637 {$ENDIF}
00638
00639 { Checks for maximum row. }
00640 Result := False;
00641 if (MaxRows > 0) and (Row > MaxRows) then
00642 Exit;
00643
00644 { Processes negative rows. }
00645 if Row < 0 then
00646 begin
00647 Row := LastRowNo - Row + 1;
00648 if Row < 0 then Row := 0;
00649 end;
00650
00651 if ResultSetType <> rtForwardOnly then
00652 begin
00653 if (Row >= 0) and (Row <= LastRowNo + 1) then
00654 begin
00655 RowNo := Row;
00656 Result := (Row >= 1) and (Row <= LastRowNo);
00657 end else
00658 Result := False;
00659 end else
00660 RaiseForwardOnlyException;
00661 end;
00662
00663 { TZPostgreSQLBlob }
00664
00665 {**
00666 Constructs this class and assignes the main properties.
00667 @param PlainDriver a PostgreSQL plain driver.
00668 @param Data a pointer to the blobdata.
00669 @param Size the size of the blobdata.
00670 @param Handle a PostgreSQL connection reference.
00671 }
00672 constructor TZPostgreSQLBlob.Create(PlainDriver: IZPostgreSQLPlainDriver;
00673 Data: Pointer; Size: Integer; Handle: PZPostgreSQLConnect; BlobOid: Oid);
00674 begin
00675 inherited CreateWithData(Data, Size);
00676 FHandle := Handle;
00677 FBlobOid := BlobOid;
00678 FPlainDriver := PlainDriver;
00679 end;
00680
00681 {**
00682 Destroys this object and cleanups the memory.
00683 }
00684 destructor TZPostgreSQLBlob.Destroy;
00685 begin
00686 inherited Destroy;
00687 end;
00688
00689 {**
00690 Gets the blob handle oid.
00691 @return the blob handle oid.
00692 }
00693 function TZPostgreSQLBlob.GetBlobOid: Oid;
00694 begin
00695 Result := FBlobOid;
00696 end;
00697
00698 {**
00699 Reads the blob by the blob handle.
00700 }
00701 procedure TZPostgreSQLBlob.ReadBlob;
00702 var
00703 BlobHandle: Integer;
00704 Buffer: array[0..1024] of Char;
00705 ReadNum: Integer;
00706 ReadStream: TMemoryStream;
00707 begin
00708 if not Updated and (FBlobOid > 0) then
00709 begin
00710 BlobHandle := FPlainDriver.OpenLargeObject(FHandle, FBlobOid, INV_READ);
00711 CheckPostgreSQLError(nil, FPlainDriver, FHandle, lcOther, 'Read Large Object',nil);
00712 ReadStream := nil;
00713 if BlobHandle >= 0 then
00714 begin
00715 ReadStream := TMemoryStream.Create;
00716 repeat
00717 ReadNum := FPlainDriver.ReadLargeObject(FHandle, BlobHandle,
00718 Buffer, 1024);
00719 if ReadNum > 0 then
00720 begin
00721 ReadStream.SetSize(ReadStream.Size + ReadNum);
00722 ReadStream.Write(Buffer, ReadNum);
00723 end;
00724 until ReadNum < 1024;
00725 FPlainDriver.CloseLargeObject(FHandle, BlobHandle);
00726 ReadStream.Position := 0;
00727 end;
00728 SetStream(ReadStream);
00729 if ReadStream <> nil then
00730 ReadStream.free;
00731 end;
00732 end;
00733
00734 {**
00735 Writes the blob by the blob handle.
00736 }
00737 procedure TZPostgreSQLBlob.WriteBlob;
00738 var
00739 BlobHandle: Integer;
00740 Position: Integer;
00741 Size: Integer;
00742 begin
00743 { Checks for empty blob. }
00744 if IsEmpty then
00745 begin
00746 FBlobOid := 0;
00747 Exit;
00748 end;
00749
00750 { Creates a new large object. }
00751 if FBlobOid = 0 then
00752 begin
00753 FBlobOid := FPlainDriver.CreateLargeObject(FHandle, INV_WRITE);
00754 CheckPostgreSQLError(nil, FPlainDriver, FHandle, lcOther, 'Create Large Object',nil);
00755 end;
00756
00757 { Opens and writes a large object. }
00758 BlobHandle := FPlainDriver.OpenLargeObject(FHandle, FBlobOid, INV_WRITE);
00759 CheckPostgreSQLError(nil, FPlainDriver, FHandle, lcOther, 'Open Large Object',nil);
00760
00761 Position := 0;
00762 while Position < BlobSize do
00763 begin
00764 if (BlobSize - Position) < 1024 then
00765 Size := BlobSize - Position
00766 else Size := 1024;
00767 FPlainDriver.WriteLargeObject(FHandle, BlobHandle,
00768 Pointer(LongInt(BlobData) + Position), Size);
00769 CheckPostgreSQLError(nil, FPlainDriver, FHandle, lcOther, 'Write Large Object',nil);
00770 Inc(Position, Size);
00771 end;
00772
00773 FPlainDriver.CloseLargeObject(FHandle, BlobHandle);
00774 CheckPostgreSQLError(nil, FPlainDriver, FHandle, lcOther, 'Close Large Object',nil);
00775 end;
00776
00777 {**
00778 Checks if this blob has an empty content.
00779 @return <code>True</code> if this blob is empty.
00780 }
00781 function TZPostgreSQLBlob.IsEmpty: Boolean;
00782 begin
00783 ReadBlob;
00784 Result := inherited IsEmpty;
00785 end;
00786
00787 {**
00788 Clones this blob object.
00789 @return a clonned blob object.
00790 }
00791 function TZPostgreSQLBlob.Clone: IZBlob;
00792 begin
00793 Result := TZPostgreSQLBlob.Create(FPlainDriver, BlobData, BlobSize,
00794 FHandle, FBlobOid);
00795 end;
00796
00797 {**
00798 Gets the associated stream object.
00799 @return an associated or newly created stream object.
00800 }
00801 function TZPostgreSQLBlob.GetStream: TStream;
00802 begin
00803 ReadBlob;
00804 Result := inherited GetStream;
00805 end;
00806
00807 end.