00001 {*********************************************************}
00002 { }
00003 { Zeos Database Objects }
00004 { SQLite 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 ZDbcSqLiteResultSet;
00055
00056 interface
00057
00058 {$I ZDbc.inc}
00059
00060 uses
00061 Classes, SysUtils, ZSysUtils, ZDbcIntfs,
00062 Contnrs, ZDbcResultSet, ZDbcResultSetMetadata, ZPlainSqLiteDriver,
00063 ZCompatibility, ZDbcCache, ZDbcCachedResultSet, ZDbcGenericResolver;
00064
00065 type
00066
00067 {** Implements SQLite ResultSet Metadata. }
00068 TZSQLiteResultSetMetadata = class(TZAbstractResultSetMetadata)
00069 public
00070
00071 function IsNullable(Column: Integer): TZColumnNullableType; override;
00072 end;
00073
00074 {** Implements SQLite ResultSet. }
00075 TZSQLiteResultSet = class(TZAbstractResultSet)
00076 private
00077 FHandle: Psqlite;
00078 FStmtHandle: Psqlite_vm;
00079 FColumnCount: Integer;
00080 FColumnNames: PPChar;
00081 FColumnValues: PPChar;
00082 FPlainDriver: IZSQLitePlainDriver;
00083 protected
00084 procedure Open; override;
00085 procedure FreeHandle;
00086 public
00087 constructor Create(PlainDriver: IZSQLitePlainDriver; Statement: IZStatement;
00088 SQL: string; Handle: Psqlite; StmtHandle: Psqlite_vm;
00089 ColumnCount: Integer; ColumnNames: PPChar; ColumnValues: PPChar);
00090 destructor Destroy; override;
00091
00092 procedure Close; override;
00093
00094 function IsNull(ColumnIndex: Integer): Boolean; override;
00095 function GetPChar(ColumnIndex: Integer): PChar; override;
00096 function GetString(ColumnIndex: Integer): string; override;
00097 function GetBoolean(ColumnIndex: Integer): Boolean; override;
00098 function GetByte(ColumnIndex: Integer): ShortInt; override;
00099 function GetShort(ColumnIndex: Integer): SmallInt; override;
00100 function GetInt(ColumnIndex: Integer): Integer; override;
00101 function GetLong(ColumnIndex: Integer): Int64; override;
00102 function GetFloat(ColumnIndex: Integer): Single; override;
00103 function GetDouble(ColumnIndex: Integer): Double; override;
00104 function GetBigDecimal(ColumnIndex: Integer): Extended; override;
00105 function GetBytes(ColumnIndex: Integer): TByteDynArray; override;
00106 function GetDate(ColumnIndex: Integer): TDateTime; override;
00107 function GetTime(ColumnIndex: Integer): TDateTime; override;
00108 function GetTimestamp(ColumnIndex: Integer): TDateTime; override;
00109 function GetAsciiStream(ColumnIndex: Integer): TStream; override;
00110 function GetUnicodeStream(ColumnIndex: Integer): TStream; override;
00111 function GetBinaryStream(ColumnIndex: Integer): TStream; override;
00112 function GetBlob(ColumnIndex: Integer): IZBlob; override;
00113
00114 function Next: Boolean; override;
00115 end;
00116
00117 {** Implements a cached resolver with SQLite specific functionality. }
00118 TZSQLiteCachedResolver = class (TZGenericCachedResolver, IZCachedResolver)
00119 private
00120 FHandle: Psqlite;
00121 FPlainDriver: IZSQLitePlainDriver;
00122 FAutoColumnIndex: Integer;
00123 public
00124 constructor Create(PlainDriver: IZSQLitePlainDriver; Handle: Psqlite;
00125 Statement: IZStatement; Metadata: IZResultSetMetadata);
00126
00127 procedure PostUpdates(Sender: IZCachedResultSet; UpdateType: TZRowUpdateType;
00128 OldRowAccessor, NewRowAccessor: TZRowAccessor); override;
00129
00130 function FormCalculateStatement(Columns: TObjectList): string; override;
00131
00132 end;
00133
00134 implementation
00135
00136 uses
00137 ZMessages, ZDbcSQLiteUtils, ZMatchPattern,
00138 ZDbcLogging;
00139
00140 { TZSQLiteResultSetMetadata }
00141
00142 {**
00143 Indicates whether the designated column is automatically numbered, thus read-only.
00144 @param column the first column is 1, the second is 2, ...
00145 @return <code>true</code> if so; <code>false</code> otherwise
00146 }
00147 {
00148 function TZSQLiteResultSetMetadata.IsAutoIncrement(Column: Integer): Boolean;
00149 begin
00150 Result := TZColumnInfo(ResultSet.ColumnsInfo[Column - 1]).AutoIncrement;
00151 end;
00152 }
00153
00154 {**
00155 Indicates the nullability of values in the designated column.
00156 @param column the first column is 1, the second is 2, ...
00157 @return the nullability status of the given column; one of <code>columnNoNulls</code>,
00158 <code>columnNullable</code> or <code>columnNullableUnknown</code>
00159 }
00160 function TZSQLiteResultSetMetadata.IsNullable(Column: Integer):
00161 TZColumnNullableType;
00162 begin
00163 if IsAutoIncrement(Column) then
00164 Result := ntNullable
00165 else Result := inherited IsNullable(Column);
00166 end;
00167
00168 { TZSQLiteResultSet }
00169
00170 {**
00171 Constructs this object, assignes main properties and
00172 opens the record set.
00173 @param PlainDriver a native SQLite plain driver.
00174 @param Statement a related SQL statement object.
00175 @param Handle a SQLite specific query handle.
00176 @param UseResult <code>True</code> to use results,
00177 <code>False</code> to store result.
00178 }
00179 constructor TZSQLiteResultSet.Create(PlainDriver: IZSQLitePlainDriver;
00180 Statement: IZStatement; SQL: string; Handle: Psqlite;
00181 StmtHandle: Psqlite_vm; ColumnCount: Integer; ColumnNames: PPChar;
00182 ColumnValues: PPChar);
00183 begin
00184 inherited Create(Statement, SQL, TZSQLiteResultSetMetadata.Create(
00185 Statement.GetConnection.GetMetadata, SQL, Self));
00186
00187 FHandle := Handle;
00188 FStmtHandle := StmtHandle;
00189 FPlainDriver := PlainDriver;
00190 ResultSetConcurrency := rcReadOnly;
00191 FColumnCount := ColumnCount;
00192 FColumnNames := ColumnNames;
00193 FColumnValues := ColumnValues;
00194
00195 Open;
00196 end;
00197
00198 {**
00199 Destroys this object and cleanups the memory.
00200 }
00201 destructor TZSQLiteResultSet.Destroy;
00202 begin
00203
00204 if FColumnValues <> nil then
00205 FreeMem(FColumnValues,Sizeof(PPChar)*(fColumnCount+1));
00206 FColumnValues := nil;
00207
00208
00209 if FColumnNames <> nil then
00210 FreeMem(FColumnNames,Sizeof(PPChar)*(fColumnCount+1)*2);
00211 FColumnNames := nil;
00212
00213 inherited Destroy;
00214 end;
00215
00216 {**
00217 Opens this recordset.
00218 }
00219 procedure TZSQLiteResultSet.Open;
00220 var
00221 I: Integer;
00222 ColumnInfo: TZColumnInfo;
00223 FieldName: PPChar;
00224 FieldPrecision: Integer;
00225 FieldDecimals: Integer;
00226 TypeName: PPChar;
00227 begin
00228 if ResultSetConcurrency = rcUpdatable then
00229 raise EZSQLException.Create(SLiveResultSetsAreNotSupported);
00230
00231 LastRowNo := 0;
00232
00233 { Fills the column info. }
00234 ColumnsInfo.Clear;
00235 FieldName := FColumnNames;
00236 TypeName := FColumnNames;
00237 Inc(TypeName, FColumnCount);
00238 for I := 1 to FColumnCount do
00239 begin
00240 ColumnInfo := TZColumnInfo.Create;
00241 with ColumnInfo do
00242 begin
00243 ColumnLabel := StrPas(FieldName^);
00244 Inc(FieldName);
00245 TableName := '';
00246 ReadOnly := False;
00247 if TypeName^ <> nil then
00248 begin
00249 ColumnType := ConvertSQLiteTypeToSQLType(TypeName^,
00250 FieldPrecision, FieldDecimals);
00251 Inc(TypeName);
00252 end
00253 else
00254 begin
00255 ColumnType := ConvertSQLiteTypeToSQLType('',
00256 FieldPrecision, FieldDecimals);
00257 end;
00258 ColumnDisplaySize := FieldPrecision;
00259 AutoIncrement := False;
00260 Precision := FieldPrecision;
00261 Scale := FieldDecimals;
00262 Signed := True;
00263 Nullable := ntNullable;
00264 end;
00265
00266 ColumnsInfo.Add(ColumnInfo);
00267 end;
00268
00269 inherited Open;
00270 end;
00271
00272 {**
00273 Frees statement handle.
00274 }
00275 procedure TZSQLiteResultSet.FreeHandle;
00276 var
00277 ErrorCode: Integer;
00278 ErrorMessage: PChar;
00279 begin
00280 ErrorMessage := nil;
00281 if Assigned(FStmtHandle) then
00282 ErrorCode := FPlainDriver.Finalize(FStmtHandle, ErrorMessage)
00283 else ErrorCode := SQLITE_OK;
00284 FStmtHandle := nil;
00285 CheckSQLiteError(FPlainDriver, ErrorCode, ErrorMessage,
00286 lcOther, 'FINALIZE SQLite VM');
00287 end;
00288
00289 {**
00290 Releases this <code>ResultSet</code> object's database and
00291 JDBC resources immediately instead of waiting for
00292 this to happen when it is automatically closed.
00293
00294 <P><B>Note:</B> A <code>ResultSet</code> object
00295 is automatically closed by the
00296 <code>Statement</code> object that generated it when
00297 that <code>Statement</code> object is closed,
00298 re-executed, or is used to retrieve the next result from a
00299 sequence of multiple results. A <code>ResultSet</code> object
00300 is also automatically closed when it is garbage collected.
00301 }
00302 procedure TZSQLiteResultSet.Close;
00303 begin
00304 inherited Close;
00305 FreeHandle;
00306 end;
00307
00308 {**
00309 Indicates if the value of the designated column in the current row
00310 of this <code>ResultSet</code> object is Null.
00311
00312 @param columnIndex the first column is 1, the second is 2, ...
00313 @return if the value is SQL <code>NULL</code>, the
00314 value returned is <code>true</code>. <code>false</code> otherwise.
00315 }
00316 function TZSQLiteResultSet.IsNull(ColumnIndex: Integer): Boolean;
00317 var
00318 Temp: PPChar;
00319 begin
00320 {$IFNDEF DISABLE_CHECKING}
00321 CheckClosed;
00322 if (LastRowNo = 0) or (FColumnValues = nil) then
00323 raise EZSQLException.Create(SRowDataIsNotAvailable);
00324 {$ENDIF}
00325
00326 Temp := FColumnValues;
00327 Inc(Temp, ColumnIndex - 1);
00328 Result := (Temp^ = nil);
00329 end;
00330
00331 {**
00332 Gets the value of the designated column in the current row
00333 of this <code>ResultSet</code> object as
00334 a <code>PChar</code> in the Delphi programming language.
00335
00336 @param columnIndex the first column is 1, the second is 2, ...
00337 @return the column value; if the value is SQL <code>NULL</code>, the
00338 value returned is <code>null</code>
00339 }
00340 function TZSQLiteResultSet.GetPChar(ColumnIndex: Integer): PChar;
00341 var
00342 Temp: PPChar;
00343 begin
00344 {$IFNDEF DISABLE_CHECKING}
00345 CheckClosed;
00346 if (LastRowNo = 0) or (FColumnValues = nil) then
00347 raise EZSQLException.Create(SRowDataIsNotAvailable);
00348 {$ENDIF}
00349
00350 Temp := FColumnValues;
00351 Inc(Temp, ColumnIndex - 1);
00352 Result := Temp^;
00353 LastWasNull := Result = nil;
00354 end;
00355
00356 {**
00357 Gets the value of the designated column in the current row
00358 of this <code>ResultSet</code> object as
00359 a <code>String</code> in the Java programming language.
00360
00361 @param columnIndex the first column is 1, the second is 2, ...
00362 @return the column value; if the value is SQL <code>NULL</code>, the
00363 value returned is <code>null</code>
00364 }
00365 function TZSQLiteResultSet.GetString(ColumnIndex: Integer): string;
00366 var
00367 Buffer: PChar;
00368 begin
00369 Buffer := GetPChar(ColumnIndex);
00370 if Buffer <> nil then
00371 Result := StrPas(Buffer)
00372 else Result := '';
00373 end;
00374
00375 {**
00376 Gets the value of the designated column in the current row
00377 of this <code>ResultSet</code> object as
00378 a <code>boolean</code> in the Java programming language.
00379
00380 @param columnIndex the first column is 1, the second is 2, ...
00381 @return the column value; if the value is SQL <code>NULL</code>, the
00382 value returned is <code>false</code>
00383 }
00384 function TZSQLiteResultSet.GetBoolean(ColumnIndex: Integer): Boolean;
00385 var
00386 Temp: string;
00387 begin
00388 {$IFNDEF DISABLE_CHECKING}
00389 CheckColumnConvertion(ColumnIndex, stBoolean);
00390 {$ENDIF}
00391 Temp := UpperCase(GetPChar(ColumnIndex));
00392 Result := (Temp = 'Y') or (Temp = 'YES') or (Temp = 'T') or
00393 (Temp = 'TRUE') or (StrToIntDef(Temp, 0) <> 0);
00394 end;
00395
00396 {**
00397 Gets the value of the designated column in the current row
00398 of this <code>ResultSet</code> object as
00399 a <code>byte</code> in the Java programming language.
00400
00401 @param columnIndex the first column is 1, the second is 2, ...
00402 @return the column value; if the value is SQL <code>NULL</code>, the
00403 value returned is <code>0</code>
00404 }
00405 function TZSQLiteResultSet.GetByte(ColumnIndex: Integer): ShortInt;
00406 begin
00407 {$IFNDEF DISABLE_CHECKING}
00408 CheckColumnConvertion(ColumnIndex, stByte);
00409 {$ENDIF}
00410 Result := ShortInt(StrToIntDef(GetPChar(ColumnIndex), 0));
00411 end;
00412
00413 {**
00414 Gets the value of the designated column in the current row
00415 of this <code>ResultSet</code> object as
00416 a <code>short</code> in the Java programming language.
00417
00418 @param columnIndex the first column is 1, the second is 2, ...
00419 @return the column value; if the value is SQL <code>NULL</code>, the
00420 value returned is <code>0</code>
00421 }
00422 function TZSQLiteResultSet.GetShort(ColumnIndex: Integer): SmallInt;
00423 begin
00424 {$IFNDEF DISABLE_CHECKING}
00425 CheckColumnConvertion(ColumnIndex, stShort);
00426 {$ENDIF}
00427 Result := SmallInt(StrToIntDef(GetPChar(ColumnIndex), 0));
00428 end;
00429
00430 {**
00431 Gets the value of the designated column in the current row
00432 of this <code>ResultSet</code> object as
00433 an <code>int</code> in the Java programming language.
00434
00435 @param columnIndex the first column is 1, the second is 2, ...
00436 @return the column value; if the value is SQL <code>NULL</code>, the
00437 value returned is <code>0</code>
00438 }
00439 function TZSQLiteResultSet.GetInt(ColumnIndex: Integer): Integer;
00440 begin
00441 {$IFNDEF DISABLE_CHECKING}
00442 CheckColumnConvertion(ColumnIndex, stInteger);
00443 {$ENDIF}
00444 Result := StrToIntDef(GetPChar(ColumnIndex), 0);
00445 end;
00446
00447 {**
00448 Gets the value of the designated column in the current row
00449 of this <code>ResultSet</code> object as
00450 a <code>long</code> in the Java programming language.
00451
00452 @param columnIndex the first column is 1, the second is 2, ...
00453 @return the column value; if the value is SQL <code>NULL</code>, the
00454 value returned is <code>0</code>
00455 }
00456 function TZSQLiteResultSet.GetLong(ColumnIndex: Integer): Int64;
00457 begin
00458 {$IFNDEF DISABLE_CHECKING}
00459 CheckColumnConvertion(ColumnIndex, stLong);
00460 {$ENDIF}
00461 Result := StrToInt64Def(GetPChar(ColumnIndex), 0);
00462 end;
00463
00464 {**
00465 Gets the value of the designated column in the current row
00466 of this <code>ResultSet</code> object as
00467 a <code>float</code> in the Java programming language.
00468
00469 @param columnIndex the first column is 1, the second is 2, ...
00470 @return the column value; if the value is SQL <code>NULL</code>, the
00471 value returned is <code>0</code>
00472 }
00473 function TZSQLiteResultSet.GetFloat(ColumnIndex: Integer): Single;
00474 begin
00475 {$IFNDEF DISABLE_CHECKING}
00476 CheckColumnConvertion(ColumnIndex, stFloat);
00477 {$ENDIF}
00478 Result := SQLStrToFloatDef(GetPChar(ColumnIndex), 0);
00479 end;
00480
00481 {**
00482 Gets the value of the designated column in the current row
00483 of this <code>ResultSet</code> object as
00484 a <code>double</code> in the Java programming language.
00485
00486 @param columnIndex the first column is 1, the second is 2, ...
00487 @return the column value; if the value is SQL <code>NULL</code>, the
00488 value returned is <code>0</code>
00489 }
00490 function TZSQLiteResultSet.GetDouble(ColumnIndex: Integer): Double;
00491 begin
00492 {$IFNDEF DISABLE_CHECKING}
00493 CheckColumnConvertion(ColumnIndex, stDouble);
00494 {$ENDIF}
00495 Result := SQLStrToFloatDef(GetPChar(ColumnIndex), 0);
00496 end;
00497
00498 {**
00499 Gets the value of the designated column in the current row
00500 of this <code>ResultSet</code> object as
00501 a <code>java.sql.BigDecimal</code> in the Java programming language.
00502
00503 @param columnIndex the first column is 1, the second is 2, ...
00504 @param scale the number of digits to the right of the decimal point
00505 @return the column value; if the value is SQL <code>NULL</code>, the
00506 value returned is <code>null</code>
00507 }
00508 function TZSQLiteResultSet.GetBigDecimal(ColumnIndex: Integer): Extended;
00509 begin
00510 {$IFNDEF DISABLE_CHECKING}
00511 CheckColumnConvertion(ColumnIndex, stBigDecimal);
00512 {$ENDIF}
00513 Result := SQLStrToFloatDef(GetPChar(ColumnIndex), 0);
00514 end;
00515
00516 {**
00517 Gets the value of the designated column in the current row
00518 of this <code>ResultSet</code> object as
00519 a <code>byte</code> array in the Java programming language.
00520 The bytes represent the raw values returned by the driver.
00521
00522 @param columnIndex the first column is 1, the second is 2, ...
00523 @return the column value; if the value is SQL <code>NULL</code>, the
00524 value returned is <code>null</code>
00525 }
00526 function TZSQLiteResultSet.GetBytes(ColumnIndex: Integer): TByteDynArray;
00527 begin
00528 {$IFNDEF DISABLE_CHECKING}
00529 CheckColumnConvertion(ColumnIndex, stBytes);
00530 {$ENDIF}
00531 Result := StrToBytes(GetString(ColumnIndex));
00532 end;
00533
00534 {**
00535 Gets the value of the designated column in the current row
00536 of this <code>ResultSet</code> object as
00537 a <code>java.sql.Date</code> object in the Java programming language.
00538
00539 @param columnIndex the first column is 1, the second is 2, ...
00540 @return the column value; if the value is SQL <code>NULL</code>, the
00541 value returned is <code>null</code>
00542 }
00543 function TZSQLiteResultSet.GetDate(ColumnIndex: Integer): TDateTime;
00544 var
00545 Value: string;
00546 begin
00547 {$IFNDEF DISABLE_CHECKING}
00548 CheckColumnConvertion(ColumnIndex, stDate);
00549 {$ENDIF}
00550 Value := GetPChar(ColumnIndex);
00551 if IsMatch('????-??-??*', Value) then
00552 Result := Trunc(AnsiSQLDateToDateTime(Value))
00553 else Result := Trunc(TimestampStrToDateTime(Value));
00554 LastWasNull := Result = 0;
00555 end;
00556
00557 {**
00558 Gets the value of the designated column in the current row
00559 of this <code>ResultSet</code> object as
00560 a <code>java.sql.Time</code> object in the Java programming language.
00561
00562 @param columnIndex the first column is 1, the second is 2, ...
00563 @return the column value; if the value is SQL <code>NULL</code>, the
00564 value returned is <code>null</code>
00565 }
00566 function TZSQLiteResultSet.GetTime(ColumnIndex: Integer): TDateTime;
00567 var
00568 Value: string;
00569 begin
00570 {$IFNDEF DISABLE_CHECKING}
00571 CheckColumnConvertion(ColumnIndex, stTime);
00572 {$ENDIF}
00573 Value := GetPChar(ColumnIndex);
00574 if IsMatch('*??:??:??*', Value) then
00575 Result := Frac(AnsiSQLDateToDateTime(Value))
00576 else Result := Frac(TimestampStrToDateTime(Value));
00577 end;
00578
00579 {**
00580 Gets the value of the designated column in the current row
00581 of this <code>ResultSet</code> object as
00582 a <code>java.sql.Timestamp</code> object in the Java programming language.
00583
00584 @param columnIndex the first column is 1, the second is 2, ...
00585 @return the column value; if the value is SQL <code>NULL</code>, the
00586 value returned is <code>null</code>
00587 @exception SQLException if a database access error occurs
00588 }
00589 function TZSQLiteResultSet.GetTimestamp(ColumnIndex: Integer): TDateTime;
00590 var
00591 Temp: string;
00592 begin
00593 {$IFNDEF DISABLE_CHECKING}
00594 CheckColumnConvertion(ColumnIndex, stTimestamp);
00595 {$ENDIF}
00596 Temp := GetPChar(ColumnIndex);
00597 if IsMatch('????-??-??*', Temp) then
00598 Result := AnsiSQLDateToDateTime(Temp)
00599 else Result := TimestampStrToDateTime(Temp);
00600 LastWasNull := Result = 0;
00601 end;
00602
00603 {**
00604 Gets the value of the designated column in the current row
00605 of this <code>ResultSet</code> object as
00606 a stream of ASCII characters. The value can then be read in chunks from the
00607 stream. This method is particularly
00608 suitable for retrieving large <char>LONGVARCHAR</char> values.
00609 The JDBC driver will
00610 do any necessary conversion from the database format into ASCII.
00611
00612 <P><B>Note:</B> All the data in the returned stream must be
00613 read prior to getting the value of any other column. The next
00614 call to a <code>getXXX</code> method implicitly closes the stream. Also, a
00615 stream may return <code>0</code> when the method
00616 <code>InputStream.available</code>
00617 is called whether there is data available or not.
00618
00619 @param columnIndex the first column is 1, the second is 2, ...
00620 @return a Java input stream that delivers the database column value
00621 as a stream of one-byte ASCII characters; if the value is SQL
00622 <code>NULL</code>, the value returned is <code>null</code>
00623 }
00624 function TZSQLiteResultSet.GetAsciiStream(ColumnIndex: Integer): TStream;
00625 begin
00626 {$IFNDEF DISABLE_CHECKING}
00627 CheckColumnConvertion(ColumnIndex, stAsciiStream);
00628 {$ENDIF}
00629 Result := TStringStream.Create(GetString(ColumnIndex));
00630 end;
00631
00632 {**
00633 Gets the value of a column in the current row as a stream of
00634 Gets the value of the designated column in the current row
00635 of this <code>ResultSet</code> object as
00636 as a stream of Unicode characters.
00637 The value can then be read in chunks from the
00638 stream. This method is particularly
00639 suitable for retrieving large<code>LONGVARCHAR</code>values. The JDBC driver will
00640 do any necessary conversion from the database format into Unicode.
00641 The byte format of the Unicode stream must be Java UTF-8,
00642 as specified in the Java virtual machine specification.
00643
00644 <P><B>Note:</B> All the data in the returned stream must be
00645 read prior to getting the value of any other column. The next
00646 call to a <code>getXXX</code> method implicitly closes the stream. Also, a
00647 stream may return <code>0</code> when the method
00648 <code>InputStream.available</code>
00649 is called whether there is data available or not.
00650
00651 @param columnIndex the first column is 1, the second is 2, ...
00652 @return a Java input stream that delivers the database column value
00653 as a stream in Java UTF-8 byte format; if the value is SQL
00654 <code>NULL</code>, the value returned is <code>null</code>
00655 }
00656 function TZSQLiteResultSet.GetUnicodeStream(ColumnIndex: Integer): TStream;
00657 begin
00658 {$IFNDEF DISABLE_CHECKING}
00659 CheckColumnConvertion(ColumnIndex, stUnicodeStream);
00660 {$ENDIF}
00661 Result := nil;
00662 end;
00663
00664 {**
00665 Gets the value of a column in the current row as a stream of
00666 Gets the value of the designated column in the current row
00667 of this <code>ResultSet</code> object as a binary stream of
00668 uninterpreted bytes. The value can then be read in chunks from the
00669 stream. This method is particularly
00670 suitable for retrieving large <code>LONGVARBINARY</code> values.
00671
00672 <P><B>Note:</B> All the data in the returned stream must be
00673 read prior to getting the value of any other column. The next
00674 call to a <code>getXXX</code> method implicitly closes the stream. Also, a
00675 stream may return <code>0</code> when the method
00676 <code>InputStream.available</code>
00677 is called whether there is data available or not.
00678
00679 @param columnIndex the first column is 1, the second is 2, ...
00680 @return a Java input stream that delivers the database column value
00681 as a stream of uninterpreted bytes;
00682 if the value is SQL <code>NULL</code>, the value returned is <code>null</code>
00683 }
00684 function TZSQLiteResultSet.GetBinaryStream(ColumnIndex: Integer): TStream;
00685 begin
00686 {$IFNDEF DISABLE_CHECKING}
00687 CheckColumnConvertion(ColumnIndex, stBinaryStream);
00688 {$ENDIF}
00689 Result := TStringStream.Create(DecodeString(GetString(ColumnIndex)));
00690 end;
00691
00692 {**
00693 Returns the value of the designated column in the current row
00694 of this <code>ResultSet</code> object as a <code>Blob</code> object
00695 in the Java programming language.
00696
00697 @param ColumnIndex the first column is 1, the second is 2, ...
00698 @return a <code>Blob</code> object representing the SQL <code>BLOB</code> value in
00699 the specified column
00700 }
00701 function TZSQLiteResultSet.GetBlob(ColumnIndex: Integer): IZBlob;
00702 var
00703 Stream: TStream;
00704 begin
00705 {$IFNDEF DISABLE_CHECKING}
00706 CheckBlobColumn(ColumnIndex);
00707 {$ENDIF}
00708 Stream := nil;
00709 try
00710 if not IsNull(ColumnIndex) then
00711 begin
00712 if TZAbstractResultSetMetadata(Metadata).GetColumnType(ColumnIndex)
00713 <> stBinaryStream then
00714 Stream := TStringStream.Create(GetString(ColumnIndex))
00715 else Stream := TStringStream.Create(DecodeString(GetString(ColumnIndex)));
00716 Result := TZAbstractBlob.CreateWithStream(Stream)
00717 end else
00718 Result := TZAbstractBlob.CreateWithStream(nil);
00719 finally
00720 if Assigned(Stream) then
00721 Stream.Free;
00722 end;
00723 end;
00724
00725 {**
00726 Moves the cursor down one row from its current position.
00727 A <code>ResultSet</code> cursor is initially positioned
00728 before the first row; the first call to the method
00729 <code>next</code> makes the first row the current row; the
00730 second call makes the second row the current row, and so on.
00731
00732 <P>If an input stream is open for the current row, a call
00733 to the method <code>next</code> will
00734 implicitly close it. A <code>ResultSet</code> object's
00735 warning chain is cleared when a new row is read.
00736
00737 @return <code>true</code> if the new current row is valid;
00738 <code>false</code> if there are no more rows
00739 }
00740 function TZSQLiteResultSet.Next: Boolean;
00741 var
00742 ErrorCode: Integer;
00743 begin
00744 { Checks for maximum row. }
00745 Result := False;
00746
00747 if (MaxRows > 0) and (RowNo >= MaxRows) then
00748 Exit;
00749
00750 if LastRowNo = 0 then
00751 begin
00752 Result := FColumnValues <> nil;
00753 if Result then
00754 begin
00755 LastRowNo := LastRowNo + 1;
00756 RowNo := RowNo + 1;
00757 end
00758 else
00759 begin
00760 if RowNo <= LastRowNo then
00761 RowNo := LastRowNo + 1;
00762 end;
00763 end
00764 else
00765 begin
00766
00767 if FColumnValues <> nil then
00768 FreeMem(FColumnValues,Sizeof(PPChar)*(fColumnCount+1));
00769 FColumnValues := nil;
00770 if Assigned(FStmtHandle) then
00771 begin
00772
00773 if FColumnNames <> nil then
00774 FreeMem(FColumnNames,Sizeof(PPChar)*(fColumnCount+1)*2);
00775 FColumnNames := nil;
00776 ErrorCode := FPlainDriver.Step(FStmtHandle, FColumnCount,
00777 FColumnValues, FColumnNames);
00778 CheckSQLiteError(FPlainDriver, ErrorCode, nil, lcOther, 'FETCH');
00779 end;
00780
00781 if FColumnValues <> nil then
00782 begin
00783 RowNo := RowNo + 1;
00784 if LastRowNo < RowNo then
00785 LastRowNo := RowNo;
00786 Result := True;
00787 end
00788 else
00789 begin
00790 if RowNo <= LastRowNo then
00791 RowNo := LastRowNo + 1;
00792 Result := False;
00793 end;
00794 end;
00795
00796 { Frees handle when reads to the end. }
00797 if not Result and Assigned(FStmtHandle) then
00798 FreeHandle;
00799 end;
00800
00801 { TZSQLiteCachedResolver }
00802
00803 {**
00804 Creates a SQLite specific cached resolver object.
00805 @param PlainDriver a native SQLite plain driver.
00806 @param Handle a SQLite specific query handle.
00807 @param Statement a related SQL statement object.
00808 @param Metadata a resultset metadata reference.
00809 }
00810 constructor TZSQLiteCachedResolver.Create(PlainDriver: IZSQLitePlainDriver;
00811 Handle: Psqlite; Statement: IZStatement; Metadata: IZResultSetMetadata);
00812 var
00813 I: Integer;
00814 begin
00815 inherited Create(Statement, Metadata);
00816 FPlainDriver := PlainDriver;
00817 FHandle := Handle;
00818
00819 { Defines an index of autoincrement field. }
00820 FAutoColumnIndex := 0;
00821 for I := 1 to Metadata.GetColumnCount do
00822 begin
00823 if Metadata.IsAutoIncrement(I) and
00824 (Metadata.GetColumnType(I) in [stByte, stShort, stInteger, stLong]) then
00825 begin
00826 FAutoColumnIndex := I;
00827 Break;
00828 end;
00829 end;
00830 end;
00831
00832 {**
00833 Posts updates to database.
00834 @param Sender a cached result set object.
00835 @param UpdateType a type of updates.
00836 @param OldRowAccessor an accessor object to old column values.
00837 @param NewRowAccessor an accessor object to new column values.
00838 }
00839 procedure TZSQLiteCachedResolver.PostUpdates(Sender: IZCachedResultSet;
00840 UpdateType: TZRowUpdateType; OldRowAccessor, NewRowAccessor: TZRowAccessor);
00841 var
00842 Statement: IZStatement;
00843 ResultSet: IZResultSet;
00844 begin
00845 inherited PostUpdates(Sender, UpdateType, OldRowAccessor, NewRowAccessor);
00846
00847 if (UpdateType = utInserted) and (FAutoColumnIndex > 0)
00848 and OldRowAccessor.IsNull(FAutoColumnIndex) then
00849 begin
00850 Statement := Connection.CreateStatement;
00851 ResultSet := Statement.ExecuteQuery('SELECT LAST_INSERT_ROWID()');
00852 try
00853 if ResultSet.Next then
00854 NewRowAccessor.SetLong(FAutoColumnIndex, ResultSet.GetLong(1));
00855 finally
00856 ResultSet.Close;
00857 Statement.Close;
00858 end;
00859 end;
00860 end;
00861
00862
00863 {**
00864 Forms a where clause for SELECT statements to calculate default values.
00865 @param Columns a collection of key columns.
00866 @param OldRowAccessor an accessor object to old column values.
00867 }
00868 function TZSQLiteCachedResolver.FormCalculateStatement(
00869 Columns: TObjectList): string;
00870 var
00871 I: Integer;
00872 Current: TZResolverParameter;
00873 begin
00874 Result := '';
00875 if Columns.Count = 0 then Exit;
00876
00877 for I := 0 to Columns.Count - 1 do
00878 begin
00879 Current := TZResolverParameter(Columns[I]);
00880 if Result <> '' then
00881 Result := Result + ',';
00882 if Current.DefaultValue <> '' then
00883 Result := Result + Current.DefaultValue
00884 else Result := Result + 'NULL';
00885 end;
00886 Result := 'SELECT ' + Result;
00887 end;
00888
00889
00890 end.