1 {*********************************************************}
3 { Zeos Database Objects }
4 { SQLite 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 ZDbcSqLiteResultSet;
59 {$IFDEF WITH_TOBJECTLIST_INLINE}System.Types, System.Contnrs{$ELSE}Types, Contnrs{$ENDIF},
60 Classes, {$IFDEF MSEgui}mclasses,{$ENDIF} SysUtils,
61 ZSysUtils, ZDbcIntfs, ZDbcResultSet, ZDbcResultSetMetadata, ZPlainSqLiteDriver,
62 ZCompatibility, ZDbcCache, ZDbcCachedResultSet, ZDbcGenericResolver;
66 {** Implements SQLite ResultSet Metadata. }
67 TZSQLiteResultSetMetadata = class(TZAbstractResultSetMetadata)
69 // function IsAutoIncrement(Column: Integer): Boolean; override;
70 function IsNullable(Column: Integer): TZColumnNullableType; override;
73 {** Implements SQLite ResultSet. }
74 TZSQLiteResultSet = class(TZAbstractResultSet)
76 FFetchingReady: Boolean;
78 FStmtHandle: Psqlite_vm;
79 FColumnCount: Integer;
80 FColumnNames: PPAnsiChar;
81 FColumnValues: PPAnsiChar;
82 FPlainDriver: IZSQLitePlainDriver;
85 procedure Open; override;
87 function InternalGetString(ColumnIndex: Integer): RawByteString; override;
89 constructor Create(PlainDriver: IZSQLitePlainDriver; Statement: IZStatement;
90 SQL: string; Handle: Psqlite; StmtHandle: Psqlite_vm;
91 ColumnCount: Integer; ColumnNames: PPAnsiChar; ColumnValues: PPAnsiChar;
92 AllowFreeHandle: Boolean = True);
93 destructor Destroy; override;
95 procedure Close; override;
97 function IsNull(ColumnIndex: Integer): Boolean; override;
98 function GetPChar(ColumnIndex: Integer): PChar; override;
99 function GetBoolean(ColumnIndex: Integer): Boolean; override;
100 function GetByte(ColumnIndex: Integer): Byte; override;
101 function GetShort(ColumnIndex: Integer): SmallInt; override;
102 function GetInt(ColumnIndex: Integer): Integer; override;
103 function GetLong(ColumnIndex: Integer): Int64; override;
104 function GetFloat(ColumnIndex: Integer): Single; override;
105 function GetDouble(ColumnIndex: Integer): Double; override;
106 function GetBigDecimal(ColumnIndex: Integer): Extended; override;
107 function GetBytes(ColumnIndex: Integer): TByteDynArray; override;
108 function GetDate(ColumnIndex: Integer): TDateTime; override;
109 function GetTime(ColumnIndex: Integer): TDateTime; override;
110 function GetTimestamp(ColumnIndex: Integer): TDateTime; override;
111 function GetBlob(ColumnIndex: Integer): IZBlob; override;
113 function Next: Boolean; override;
116 {** Implements a cached resolver with SQLite specific functionality. }
117 TZSQLiteCachedResolver = class (TZGenericCachedResolver, IZCachedResolver)
120 FPlainDriver: IZSQLitePlainDriver;
121 FAutoColumnIndex: Integer;
123 constructor Create(PlainDriver: IZSQLitePlainDriver; Handle: Psqlite;
124 Statement: IZStatement; Metadata: IZResultSetMetadata);
126 procedure PostUpdates(Sender: IZCachedResultSet; UpdateType: TZRowUpdateType;
127 OldRowAccessor, NewRowAccessor: TZRowAccessor); override;
129 function FormCalculateStatement(Columns: TObjectList): string; override;
131 procedure UpdateAutoIncrementFields(Sender: IZCachedResultSet; UpdateType: TZRowUpdateType;
132 OldRowAccessor, NewRowAccessor: TZRowAccessor; Resolver: IZCachedResolver); override;
138 ZMessages, ZDbcSqLite, ZDbcSQLiteUtils, ZMatchPattern, ZEncoding,
139 ZDbcLogging, ZDbcSqLiteStatement;
141 { TZSQLiteResultSetMetadata }
144 Indicates whether the designated column is automatically numbered, thus read-only.
145 @param column the first column is 1, the second is 2, ...
146 @return <code>true</code> if so; <code>false</code> otherwise
149 function TZSQLiteResultSetMetadata.IsAutoIncrement(Column: Integer): Boolean;
151 Result := TZColumnInfo(ResultSet.ColumnsInfo[Column - 1]).AutoIncrement;
156 Indicates the nullability of values in the designated column.
157 @param column the first column is 1, the second is 2, ...
158 @return the nullability status of the given column; one of <code>columnNoNulls</code>,
159 <code>columnNullable</code> or <code>columnNullableUnknown</code>
161 function TZSQLiteResultSetMetadata.IsNullable(Column: Integer):
162 TZColumnNullableType;
164 if IsAutoIncrement(Column) then
167 Result := inherited IsNullable(Column);
170 { TZSQLiteResultSet }
173 Constructs this object, assignes main properties and
174 opens the record set.
175 @param PlainDriver a native SQLite plain driver.
176 @param Statement a related SQL statement object.
177 @param Handle a SQLite specific query handle.
178 @param UseResult <code>True</code> to use results,
179 <code>False</code> to store result.
181 constructor TZSQLiteResultSet.Create(PlainDriver: IZSQLitePlainDriver;
182 Statement: IZStatement; SQL: string; Handle: Psqlite;
183 StmtHandle: Psqlite_vm; ColumnCount: Integer; ColumnNames: PPAnsiChar;
184 ColumnValues: PPAnsiChar; AllowFreeHandle: Boolean = True);
186 inherited Create(Statement, SQL, TZSQLiteResultSetMetadata.Create(
187 Statement.GetConnection.GetMetadata, SQL, Self),
188 Statement.GetConnection.GetConSettings);
191 FStmtHandle := StmtHandle;
192 FPlainDriver := PlainDriver;
193 ResultSetConcurrency := rcReadOnly;
194 FColumnCount := ColumnCount;
195 FColumnNames := ColumnNames;
196 FColumnValues := ColumnValues;
197 FFreeHandle := AllowFreeHandle;
198 FFetchingReady := False;
204 Destroys this object and cleanups the memory.
206 destructor TZSQLiteResultSet.Destroy;
208 //ZPlainSQLLiteDriver.Step : AllocMem(SizeOf(PPAnsiChar)*(pN+1)); // Leak, if not freed ! [HD, 05.10.2007]
209 if FColumnValues <> nil then
210 FreeMem(FColumnValues, Sizeof(PPAnsiChar) * (fColumnCount + 1));
211 FColumnValues := nil;
213 //ZPlainSQLLiteDriver.Step : AllocMem(SizeOf(PPAnsiChar)*(pN+1)*2); // Leak, if not freed ! [HD, 05.10.2007]
214 if FColumnNames <> nil then
215 FreeMem(FColumnNames, Sizeof(PPAnsiChar) * (fColumnCount + 1) * 2);
222 Opens this recordset.
224 procedure TZSQLiteResultSet.Open;
227 ColumnInfo: TZColumnInfo;
228 FieldName: PPAnsiChar;
229 FieldPrecision: Integer;
230 FieldDecimals: Integer;
231 TypeName: PPAnsiChar;
233 if ResultSetConcurrency = rcUpdatable then
234 raise EZSQLException.Create(SLiveResultSetsAreNotSupported);
238 { Fills the column info. }
240 FieldName := FColumnNames;
241 TypeName := FColumnNames;
242 Inc(TypeName, FColumnCount);
243 for I := 1 to FColumnCount do
245 ColumnInfo := TZColumnInfo.Create;
248 ColumnLabel := ZDbcString(FieldName^);
252 if TypeName^ <> nil then
254 ColumnType := ConvertSQLiteTypeToSQLType(ZDbcString(TypeName^),
255 FieldPrecision, FieldDecimals, ConSettings.CPType);
260 ColumnType := ConvertSQLiteTypeToSQLType(ZDbcString(FPlainDriver.column_decltype(FStmtHandle,I-1)),
261 FieldPrecision, FieldDecimals, ConSettings.CPType);
263 if ColumnType = stString then
264 if Zencoding.ZDefaultSystemCodePage = zCP_UTF8 then
265 ColumnDisplaySize := FieldPrecision div 4
267 ColumnDisplaySize := FieldPrecision div 2;
269 if ColumnType = stUnicodeString then
270 ColumnDisplaySize := FieldPrecision div 2;
272 AutoIncrement := False;
273 Precision := FieldPrecision;
274 Scale := FieldDecimals;
276 Nullable := ntNullable;
279 ColumnsInfo.Add(ColumnInfo);
286 Frees statement handle.
288 procedure TZSQLiteResultSet.FreeHandle;
294 if Assigned(FStmtHandle) then
295 ErrorCode := FPlainDriver.Finalize(FStmtHandle)
297 ErrorCode := SQLITE_OK;
299 CheckSQLiteError(FPlainDriver, FStmtHandle, ErrorCode, nil,
300 lcOther, 'FINALIZE SQLite VM');
303 if FStmtHandle <> nil then
305 ErrorCode := FPlainDriver.reset(FStmtHandle);
307 CheckSQLiteError(FPlainDriver, FStmtHandle, ErrorCode, nil, lcBindPrepStmt, 'Reset Prepared Stmt');
308 FFetchingReady := True;
313 Releases this <code>ResultSet</code> object's database and
314 JDBC resources immediately instead of waiting for
315 this to happen when it is automatically closed.
317 <P><B>Note:</B> A <code>ResultSet</code> object
318 is automatically closed by the
319 <code>Statement</code> object that generated it when
320 that <code>Statement</code> object is closed,
321 re-executed, or is used to retrieve the next result from a
322 sequence of multiple results. A <code>ResultSet</code> object
323 is also automatically closed when it is garbage collected.
325 procedure TZSQLiteResultSet.Close;
326 var stmt: IZSQLiteCAPIPreparedStatement;
328 if Assigned(Statement) and Supports(Statement, IZSQLiteCAPIPreparedStatement, stmt) then
335 Indicates if the value of the designated column in the current row
336 of this <code>ResultSet</code> object is Null.
338 @param columnIndex the first column is 1, the second is 2, ...
339 @return if the value is SQL <code>NULL</code>, the
340 value returned is <code>true</code>. <code>false</code> otherwise.
342 function TZSQLiteResultSet.IsNull(ColumnIndex: Integer): Boolean;
346 {$IFNDEF DISABLE_CHECKING}
348 if (LastRowNo = 0) or (FColumnValues = nil) then
349 raise EZSQLException.Create(SRowDataIsNotAvailable);
352 Temp := FColumnValues;
353 Inc(Temp, ColumnIndex - 1);
354 Result := (Temp^ = nil);
358 Gets the value of the designated column in the current row
359 of this <code>ResultSet</code> object as
360 a <code>PAnsiChar</code> in the Delphi programming language.
362 @param columnIndex the first column is 1, the second is 2, ...
363 @return the column value; if the value is SQL <code>NULL</code>, the
364 value returned is <code>null</code>
366 function TZSQLiteResultSet.GetPChar(ColumnIndex: Integer): PChar;
370 {$IFNDEF DISABLE_CHECKING}
372 if (LastRowNo = 0) or (FColumnValues = nil) then
373 raise EZSQLException.Create(SRowDataIsNotAvailable);
376 TempStr := GetString(ColumnIndex);
377 Result := PChar(TempStr);
378 LastWasNull := Result = nil;
382 Gets the value of the designated column in the current row
383 of this <code>ResultSet</code> object as
384 a <code>String</code> in the Java programming language.
386 @param columnIndex the first column is 1, the second is 2, ...
387 @return the column value; if the value is SQL <code>NULL</code>, the
388 value returned is <code>null</code>
390 function TZSQLiteResultSet.InternalGetString(ColumnIndex: Integer): RawByteString;
394 {$IFNDEF DISABLE_CHECKING}
396 if (LastRowNo = 0) or (FColumnValues = nil) then
397 raise EZSQLException.Create(SRowDataIsNotAvailable);
400 Temp := FColumnValues;
401 Inc(Temp, ColumnIndex - 1);
403 LastWasNull := Temp^ = nil;
407 Gets the value of the designated column in the current row
408 of this <code>ResultSet</code> object as
409 a <code>boolean</code> in the Java programming language.
411 @param columnIndex the first column is 1, the second is 2, ...
412 @return the column value; if the value is SQL <code>NULL</code>, the
413 value returned is <code>false</code>
415 function TZSQLiteResultSet.GetBoolean(ColumnIndex: Integer): Boolean;
419 {$IFNDEF DISABLE_CHECKING}
420 CheckColumnConvertion(ColumnIndex, stBoolean);
422 Temp := UpperCase(String(InternalGetString(ColumnIndex)));
423 Result := (Temp = 'Y') or (Temp = 'YES') or (Temp = 'T') or
424 (Temp = 'TRUE') or (StrToIntDef(Temp, 0) <> 0);
428 Gets the value of the designated column in the current row
429 of this <code>ResultSet</code> object as
430 a <code>byte</code> in the Java programming language.
432 @param columnIndex the first column is 1, the second is 2, ...
433 @return the column value; if the value is SQL <code>NULL</code>, the
434 value returned is <code>0</code>
436 function TZSQLiteResultSet.GetByte(ColumnIndex: Integer): Byte;
438 {$IFNDEF DISABLE_CHECKING}
439 CheckColumnConvertion(ColumnIndex, stByte);
441 Result := Byte(StrToIntDef(String(InternalGetString(ColumnIndex)), 0));
445 Gets the value of the designated column in the current row
446 of this <code>ResultSet</code> object as
447 a <code>short</code> in the Java programming language.
449 @param columnIndex the first column is 1, the second is 2, ...
450 @return the column value; if the value is SQL <code>NULL</code>, the
451 value returned is <code>0</code>
453 function TZSQLiteResultSet.GetShort(ColumnIndex: Integer): SmallInt;
455 {$IFNDEF DISABLE_CHECKING}
456 CheckColumnConvertion(ColumnIndex, stShort);
458 Result := SmallInt(StrToIntDef(String(InternalGetString(ColumnIndex)), 0));
462 Gets the value of the designated column in the current row
463 of this <code>ResultSet</code> object as
464 an <code>int</code> in the Java programming language.
466 @param columnIndex the first column is 1, the second is 2, ...
467 @return the column value; if the value is SQL <code>NULL</code>, the
468 value returned is <code>0</code>
470 function TZSQLiteResultSet.GetInt(ColumnIndex: Integer): Integer;
472 {$IFNDEF DISABLE_CHECKING}
473 CheckColumnConvertion(ColumnIndex, stInteger);
475 Result := StrToIntDef(String(InternalGetString(ColumnIndex)), 0);
479 Gets the value of the designated column in the current row
480 of this <code>ResultSet</code> object as
481 a <code>long</code> in the Java programming language.
483 @param columnIndex the first column is 1, the second is 2, ...
484 @return the column value; if the value is SQL <code>NULL</code>, the
485 value returned is <code>0</code>
487 function TZSQLiteResultSet.GetLong(ColumnIndex: Integer): Int64;
489 {$IFNDEF DISABLE_CHECKING}
490 CheckColumnConvertion(ColumnIndex, stLong);
492 Result := StrToInt64Def(String(InternalGetString(ColumnIndex)), 0);
496 Gets the value of the designated column in the current row
497 of this <code>ResultSet</code> object as
498 a <code>float</code> in the Java programming language.
500 @param columnIndex the first column is 1, the second is 2, ...
501 @return the column value; if the value is SQL <code>NULL</code>, the
502 value returned is <code>0</code>
504 function TZSQLiteResultSet.GetFloat(ColumnIndex: Integer): Single;
506 {$IFNDEF DISABLE_CHECKING}
507 CheckColumnConvertion(ColumnIndex, stFloat);
509 Result := SQLStrToFloatDef(InternalGetString(ColumnIndex), 0);
513 Gets the value of the designated column in the current row
514 of this <code>ResultSet</code> object as
515 a <code>double</code> in the Java programming language.
517 @param columnIndex the first column is 1, the second is 2, ...
518 @return the column value; if the value is SQL <code>NULL</code>, the
519 value returned is <code>0</code>
521 function TZSQLiteResultSet.GetDouble(ColumnIndex: Integer): Double;
523 {$IFNDEF DISABLE_CHECKING}
524 CheckColumnConvertion(ColumnIndex, stDouble);
526 Result := SQLStrToFloatDef(InternalGetString(ColumnIndex), 0);
530 Gets the value of the designated column in the current row
531 of this <code>ResultSet</code> object as
532 a <code>java.sql.BigDecimal</code> in the Java programming language.
534 @param columnIndex the first column is 1, the second is 2, ...
535 @param scale the number of digits to the right of the decimal point
536 @return the column value; if the value is SQL <code>NULL</code>, the
537 value returned is <code>null</code>
539 function TZSQLiteResultSet.GetBigDecimal(ColumnIndex: Integer): Extended;
541 {$IFNDEF DISABLE_CHECKING}
542 CheckColumnConvertion(ColumnIndex, stBigDecimal);
544 Result := SQLStrToFloatDef(InternalGetString(ColumnIndex), 0);
548 Gets the value of the designated column in the current row
549 of this <code>ResultSet</code> object as
550 a <code>byte</code> array in the Java programming language.
551 The bytes represent the raw values returned by the driver.
553 @param columnIndex the first column is 1, the second is 2, ...
554 @return the column value; if the value is SQL <code>NULL</code>, the
555 value returned is <code>null</code>
557 function TZSQLiteResultSet.GetBytes(ColumnIndex: Integer): TByteDynArray;
559 {$IFNDEF DISABLE_CHECKING}
560 CheckColumnConvertion(ColumnIndex, stBytes);
562 Result := StrToBytes(DecodeString(InternalGetString(ColumnIndex)));
566 Gets the value of the designated column in the current row
567 of this <code>ResultSet</code> object as
568 a <code>java.sql.Date</code> object in the Java programming language.
570 @param columnIndex the first column is 1, the second is 2, ...
571 @return the column value; if the value is SQL <code>NULL</code>, the
572 value returned is <code>null</code>
574 function TZSQLiteResultSet.GetDate(ColumnIndex: Integer): TDateTime;
579 {$IFNDEF DISABLE_CHECKING}
580 CheckColumnConvertion(ColumnIndex, stDate);
582 Value := String(InternalGetString(ColumnIndex));
583 if IsMatch('????-??-??*', Value) then
584 Result := Trunc(AnsiSQLDateToDateTime(Value))
587 TempDate := Trunc(SQLStrToFloatDef(Value, 0));
588 Result := Trunc(TimestampStrToDateTime(Value));
589 if ( Result = 0 ) and not ( TempDate = 0 ) then
592 LastWasNull := Result = 0;
596 Gets the value of the designated column in the current row
597 of this <code>ResultSet</code> object as
598 a <code>java.sql.Time</code> object in the Java programming language.
600 @param columnIndex the first column is 1, the second is 2, ...
601 @return the column value; if the value is SQL <code>NULL</code>, the
602 value returned is <code>null</code>
604 function TZSQLiteResultSet.GetTime(ColumnIndex: Integer): TDateTime;
609 {$IFNDEF DISABLE_CHECKING}
610 CheckColumnConvertion(ColumnIndex, stTime);
612 Value := String(InternalGetString(ColumnIndex));
613 if IsMatch('*??:??:??*', Value) then
614 Result := Frac(AnsiSQLDateToDateTime(Value))
617 TempTime := Frac(SQLStrToFloatDef(Value, 0));
618 Result := Frac(TimestampStrToDateTime(Value));
619 if ( Result = 0 ) and not ( TempTime = 0 ) then
622 LastWasNull := Result = 0;
626 Gets the value of the designated column in the current row
627 of this <code>ResultSet</code> object as
628 a <code>java.sql.Timestamp</code> object in the Java programming language.
630 @param columnIndex the first column is 1, the second is 2, ...
631 @return the column value; if the value is SQL <code>NULL</code>, the
632 value returned is <code>null</code>
633 @exception SQLException if a database access error occurs
635 function TZSQLiteResultSet.GetTimestamp(ColumnIndex: Integer): TDateTime;
638 TempTimeStamp: TDateTime;
640 {$IFNDEF DISABLE_CHECKING}
641 CheckColumnConvertion(ColumnIndex, stTimestamp);
643 Value := String(InternalGetString(ColumnIndex));
644 if IsMatch('????-??-??*', Value) then
645 Result := AnsiSQLDateToDateTime(Value)
648 TempTimeStamp := SQLStrToFloatDef(Value, 0);
649 Result := TimestampStrToDateTime(Value);
650 if ( Result = 0 ) and not ( TempTimeStamp = 0 ) then
651 Result := TempTimeStamp;
653 LastWasNull := Result = 0;
657 Returns the value of the designated column in the current row
658 of this <code>ResultSet</code> object as a <code>Blob</code> object
659 in the Java programming language.
661 @param ColumnIndex the first column is 1, the second is 2, ...
662 @return a <code>Blob</code> object representing the SQL <code>BLOB</code> value in
665 function TZSQLiteResultSet.GetBlob(ColumnIndex: Integer): IZBlob;
668 AnsiTemp: RawByteString;
671 {$IFNDEF DISABLE_CHECKING}
672 CheckBlobColumn(ColumnIndex);
674 LastWasNull := IsNull(ColumnIndex);
680 if not LastWasNull then
682 case GetMetadata.GetColumnType(ColumnIndex) of
684 if ConSettings.AutoEncode then
685 Stream := TStringStream.Create(GetValidatedAnsiString(InternalGetString(ColumnIndex), ConSettings, True))
687 Stream := TStringStream.Create(InternalGetString(ColumnIndex));
690 AnsiTemp := InternalGetString(ColumnIndex);
691 if Length(AnsiTemp) = 0 then
692 Stream := TMemoryStream.Create
694 Stream := GetValidatedUnicodeStream(InternalGetString(ColumnIndex), ConSettings, True);
697 {introduced the old Zeos6 blob-encoding cause of compatibility reasons}
698 if (Statement.GetConnection as IZSQLiteConnection).UseOldBlobEncoding then
699 Stream := TStringStream.Create(DecodeString(InternalGetString(ColumnIndex)))
701 Stream := FPlaindriver.column_blob(FStmtHandle,columnIndex);
703 Result := TZAbstractBlob.CreateWithStream(Stream, GetStatement.GetConnection, GetMetadata.GetColumnType(ColumnIndex) = stUnicodeStream);
706 Result := TZAbstractBlob.CreateWithStream(nil, GetStatement.GetConnection);
708 if Assigned(Stream) then
714 Moves the cursor down one row from its current position.
715 A <code>ResultSet</code> cursor is initially positioned
716 before the first row; the first call to the method
717 <code>next</code> makes the first row the current row; the
718 second call makes the second row the current row, and so on.
720 <P>If an input stream is open for the current row, a call
721 to the method <code>next</code> will
722 implicitly close it. A <code>ResultSet</code> object's
723 warning chain is cleared when a new row is read.
725 @return <code>true</code> if the new current row is valid;
726 <code>false</code> if there are no more rows
728 function TZSQLiteResultSet.Next: Boolean;
732 { Checks for maximum row. }
735 if (MaxRows > 0) and (RowNo >= MaxRows) then
738 if LastRowNo = 0 then
740 Result := FColumnValues <> nil;
743 LastRowNo := LastRowNo + 1;
748 if RowNo <= LastRowNo then
749 RowNo := LastRowNo + 1;
754 //ZPlainSQLLiteDriver.Step : AllocMem(SizeOf(PPAnsiChar)*(pN+1)); // Leak, if not freed ! [HD, 05.10.2007]
755 if FColumnValues <> nil then
756 FreeMem(FColumnValues, Sizeof(PPAnsiChar) * (fColumnCount + 1));
757 FColumnValues := nil;
758 if Assigned(FStmtHandle) and not FFetchingReady then
760 //ZPlainSQLLiteDriver.Step : AllocMem(SizeOf(PPAnsiChar)*(pN+1)*2); // Leak, if not freed [HD, 05.10.2007]
761 if FColumnNames <> nil then
762 FreeMem(FColumnNames, Sizeof(PPAnsiChar) * (fColumnCount + 1) * 2);
764 ErrorCode := FPlainDriver.Step(FStmtHandle, FColumnCount,
765 FColumnValues, FColumnNames);
766 CheckSQLiteError(FPlainDriver, FStmtHandle, ErrorCode, nil, lcOther, 'FETCH');
769 if FColumnValues <> nil then
772 if LastRowNo < RowNo then
778 if RowNo <= LastRowNo then
779 RowNo := LastRowNo + 1;
784 { Frees handle when reads to the end. }
785 if not Result and Assigned(FStmtHandle) then
789 { TZSQLiteCachedResolver }
792 Creates a SQLite specific cached resolver object.
793 @param PlainDriver a native SQLite plain driver.
794 @param Handle a SQLite specific query handle.
795 @param Statement a related SQL statement object.
796 @param Metadata a resultset metadata reference.
798 constructor TZSQLiteCachedResolver.Create(PlainDriver: IZSQLitePlainDriver;
799 Handle: Psqlite; Statement: IZStatement; Metadata: IZResultSetMetadata);
803 inherited Create(Statement, Metadata);
804 FPlainDriver := PlainDriver;
807 { Defines an index of autoincrement field. }
808 FAutoColumnIndex := 0;
809 for I := 1 to Metadata.GetColumnCount do
811 if Metadata.IsAutoIncrement(I) and
812 (Metadata.GetColumnType(I) in [stByte, stShort, stInteger, stLong]) then
814 FAutoColumnIndex := I;
821 Posts updates to database.
822 @param Sender a cached result set object.
823 @param UpdateType a type of updates.
824 @param OldRowAccessor an accessor object to old column values.
825 @param NewRowAccessor an accessor object to new column values.
827 procedure TZSQLiteCachedResolver.PostUpdates(Sender: IZCachedResultSet;
828 UpdateType: TZRowUpdateType; OldRowAccessor, NewRowAccessor: TZRowAccessor);
830 inherited PostUpdates(Sender, UpdateType, OldRowAccessor, NewRowAccessor);
832 if (UpdateType = utInserted) then
833 UpdateAutoIncrementFields(Sender, UpdateType, OldRowAccessor, NewRowAccessor, Self);
837 Do Tasks after Post updates to database.
838 @param Sender a cached result set object.
839 @param UpdateType a type of updates.
840 @param OldRowAccessor an accessor object to old column values.
841 @param NewRowAccessor an accessor object to new column values.
843 procedure TZSQLiteCachedResolver.UpdateAutoIncrementFields(
844 Sender: IZCachedResultSet; UpdateType: TZRowUpdateType; OldRowAccessor,
845 NewRowAccessor: TZRowAccessor; Resolver: IZCachedResolver);
847 PlainDriver: IZSQLitePlainDriver;
851 if (FAutoColumnIndex > 0) and
852 (OldRowAccessor.IsNull(FAutoColumnIndex) or (OldRowAccessor.GetValue(FAutoColumnIndex).VInteger = 0)) then
854 PlainDriver := (Connection as IZSQLiteConnection).GetPlainDriver;
856 NewRowAccessor.SetLong(FAutoColumnIndex, PlainDriver.LastInsertRowId(FHandle));
860 // --> ms, 02/11/2005
862 Forms a where clause for SELECT statements to calculate default values.
863 @param Columns a collection of key columns.
864 @param OldRowAccessor an accessor object to old column values.
866 function TZSQLiteCachedResolver.FormCalculateStatement(
867 Columns: TObjectList): string;
870 Current: TZResolverParameter;
873 if Columns.Count = 0 then
876 for I := 0 to Columns.Count - 1 do
878 Current := TZResolverParameter(Columns[I]);
880 Result := Result + ',';
881 if Current.DefaultValue <> '' then
882 Result := Result + Current.DefaultValue
884 Result := Result + 'NULL';
886 Result := 'SELECT ' + Result;