source: oup/releases/0.17a/SQLiteTable3.pas @ 409

Last change on this file since 409 was 21, checked in by alloc, 17 years ago
File size: 22.8 KB
Line 
1unit SQLiteTable3;
2
3{
4  Simple classes for using SQLite's exec and get_table.
5
6  TSQLiteDatabase wraps the calls to open and close an SQLite database.
7  It also wraps SQLite_exec for queries that do not return a result set
8
9  TSQLiteTable wraps sqlite_get_table.
10  It allows accessing fields by name as well as index and can step through a
11  result set with the Next procedure.
12
13  Adapted by Tim Anderson (tim@itwriting.com)
14  Originally created by Pablo Pissanetzky (pablo@myhtpc.net)
15}
16
17interface
18
19uses
20  Windows, SQLite3, Classes, Sysutils;
21
22const
23  dtStr = 0;
24  dtInt = 1;
25  dtBool = 2;
26  dtNumeric = 3;
27  dtBlob = 4;
28
29type
30
31  ESQLiteException = class(Exception)
32  private
33  public
34  end;
35
36  TSQLiteTable = class;
37
38  TSQLiteDatabase = class
39  private
40    fDB: TSQLiteDB;
41    fInTrans: Boolean;
42    procedure RaiseError(s: string; SQL: string);
43
44  public
45    constructor Create(const FileName: string);
46    destructor Destroy; override;
47    function GetTable(const SQL: string): TSQLiteTable;
48    procedure ExecSQL(const SQL: string);
49    procedure UpdateBlob(const SQL: string; BlobData: TStream);
50    procedure BeginTransaction;
51    procedure Commit;
52    procedure Rollback;
53    function TableExists(TableName: string): boolean;
54    function GetLastInsertRowID: int64;
55
56  published
57    property isTransactionOpen: boolean read fInTrans;
58
59  end;
60
61  TSQLiteTable = class
62  private
63    fResults: TList;
64    fRowCount: Cardinal;
65    fColCount: Cardinal;
66    fCols: TStringList;
67    fColTypes: TList;
68    fRow: Cardinal;
69
70    function GetFields(I: Integer): string;
71    function GetEOF: Boolean;
72    function GetBOF: Boolean;
73    function GetColumns(I: Integer): string;
74    function GetFieldByName(FieldName: string): string;
75    function GetFieldIndex(FieldName: string): integer;
76    function GetCount: Integer;
77    function GetCountResult: Integer;
78
79
80  public
81    constructor Create(DB: TSQLiteDatabase; const SQL: string);
82    destructor Destroy; override;
83    function FieldAsInteger(FieldName: string): integer;
84    function FieldAsBool(FieldName: string): boolean;
85    function FieldAsBlob(FieldName: string): TMemoryStream;
86    function FieldAsBlobText(FieldName: string): string;
87    function FieldIsNull(FieldName: string): boolean;
88    function FieldAsString(FieldName: string): string;
89    function FieldAsDouble(FieldName: string): double;
90{    function FieldAsInteger(I: integer): integer;
91    function FieldAsBool(I: integer): boolean;
92    function FieldAsBlob(I: Integer): TMemoryStream;
93    function FieldAsBlobText(I: Integer): string;
94    function FieldIsNull(I: integer): boolean;
95    function FieldAsString(I: Integer): string;
96    function FieldAsDouble(I: Integer): double;
97}    function Next: Boolean;
98    function Previous: Boolean;
99    property EOF: Boolean read GetEOF;
100    property BOF: Boolean read GetBOF;
101    property Fields[I: Integer]: string read GetFields;
102    property FieldByName[FieldName: string]: string read GetFieldByName;
103    property FieldIndex[FieldName: string]: integer read GetFieldIndex;
104    property Columns[I: Integer]: string read GetColumns;
105    property ColCount: Cardinal read fColCount;
106    property RowCount: Cardinal read fRowCount;
107    property Row: Cardinal read fRow;
108    function MoveFirst: boolean;
109    function MoveLast: boolean;
110
111
112    property Count: Integer read GetCount;
113
114    // The property CountResult is used when you execute count(*) queries.
115    // It returns 0 if the result set is empty or the value of the
116    // first field as an integer.
117    property CountResult: Integer read GetCountResult;
118  end;
119
120
121procedure DisposePointer(ptr: pointer); cdecl;
122
123implementation
124
125uses
126  strutils;
127
128
129procedure DisposePointer(ptr: pointer); cdecl;
130begin
131
132  if assigned(ptr) then
133  begin freemem(ptr) end;
134
135end;
136
137//------------------------------------------------------------------------------
138// TSQLiteDatabase
139//------------------------------------------------------------------------------
140
141constructor TSQLiteDatabase.Create(const FileName: string);
142var
143  Msg: pchar;
144  iResult: integer;
145begin
146  inherited Create;
147
148  self.fInTrans := false;
149
150  Msg := nil;
151  try
152    iResult := SQLite3_Open(PChar(FileName), Fdb);
153
154    if iResult <> SQLITE_OK then
155    begin
156      if Assigned(Fdb) then
157      begin
158        Msg := Sqlite3_ErrMsg(Fdb);
159        raise ESqliteException.CreateFmt('Failed to open database "%s" : %s', [FileName, Msg]);
160      end
161      else
162      begin raise ESqliteException.CreateFmt('Failed to open database "%s" : unknown error', [FileName]) end;
163    end;
164
165    //set a few configs
166    self.ExecSQL('PRAGMA SYNCHRONOUS=NORMAL;');
167
168    //this pragma not recommended and may disappear in future
169    //sqlite versions
170    //self.ExecSQL('PRAGMA full_column_names = 1;');
171
172  finally
173    if Assigned(Msg) then
174    begin SQLite3_Free(Msg) end;
175  end;
176
177
178end;
179
180
181//..............................................................................
182
183destructor TSQLiteDatabase.Destroy;
184begin
185
186  if self.fInTrans then
187  begin self.ExecSQL('ROLLBACK;') end; //assume rollback
188
189  if Assigned(fDB) then
190  begin SQLite3_Close(fDB) end;
191
192  inherited;
193end;
194
195function TSQLiteDatabase.GetLastInsertRowID: int64;
196begin
197  result := Sqlite3_LastInsertRowID(self.fDB);
198end;
199
200//..............................................................................
201
202procedure TSQLiteDatabase.RaiseError(s: string; SQL: string);
203//look up last error and raise and exception with an appropriate message
204var
205  Msg: PChar;
206begin
207
208  Msg := nil;
209
210  if sqlite3_errcode(self.fDB) <> SQLITE_OK then
211    Msg := sqlite3_errmsg(self.fDB);
212
213  if Msg <> nil then
214    raise ESqliteException.CreateFmt(s + ' "%s" : %s', [SQL, Msg])
215  else
216    raise ESqliteException.CreateFmt(s, [SQL, 'No message']);
217
218end;
219
220procedure TSQLiteDatabase.ExecSQL(const SQL: string);
221var
222  Stmt: TSQLiteStmt;
223  NextSQLStatement: Pchar;
224  iStepResult: integer;
225begin
226  try
227
228    if Sqlite3_Prepare(self.fDB, PChar(SQL), -1, Stmt, NextSQLStatement) <> SQLITE_OK then
229    begin RaiseError('Error executing SQL', SQL) end;
230
231    if (Stmt = nil) then
232    begin RaiseError('Could not prepare SQL statement', SQL) end;
233
234    iStepResult := Sqlite3_step(Stmt);
235
236    if (iStepResult <> SQLITE_DONE) then
237    begin RaiseError('Error executing SQL statement', SQL) end;
238
239  finally
240
241    if Assigned(Stmt) then
242    begin Sqlite3_Finalize(stmt) end;
243
244  end;
245end;
246
247procedure TSQLiteDatabase.UpdateBlob(const SQL: string; BlobData: TStream);
248var
249  iSize: integer;
250  ptr: pointer;
251  Stmt: TSQLiteStmt;
252  Msg: Pchar;
253  NextSQLStatement: Pchar;
254  iStepResult: integer;
255  iBindResult: integer;
256begin
257//expects SQL of the form 'UPDATE MYTABLE SET MYFIELD = ? WHERE MYKEY = 1'
258
259  if pos('?', SQL) = 0 then
260  begin RaiseError('SQL must include a ? parameter', SQL) end;
261
262  Msg := nil;
263  try
264
265    if Sqlite3_Prepare(self.fDB, PChar(SQL), -1, Stmt, NextSQLStatement) <> SQLITE_OK then
266    begin RaiseError('Could not prepare SQL statement', SQL) end;
267
268    if (Stmt = nil) then
269    begin RaiseError('Could not prepare SQL statement', SQL) end;
270
271//now bind the blob data
272    iSize := BlobData.size;
273
274    GetMem(ptr, iSize);
275
276    if (ptr = nil) then
277    begin raise ESqliteException.CreateFmt('Error getting memory to save blob', [SQL, 'Error']) end;
278
279    BlobData.position := 0;
280    BlobData.Read(ptr^, iSize);
281
282    iBindResult := SQLite3_BindBlob(stmt, 1, ptr, iSize, @DisposePointer);
283
284    if iBindResult <> SQLITE_OK then
285    begin RaiseError('Error binding blob to database', SQL) end;
286
287    iStepResult := Sqlite3_step(Stmt);
288
289    if (iStepResult <> SQLITE_DONE) then
290    begin RaiseError('Error executing SQL statement', SQL) end;
291
292  finally
293
294    if Assigned(Stmt) then
295    begin Sqlite3_Finalize(stmt) end;
296
297    if Assigned(Msg) then
298    begin SQLite3_Free(Msg) end;
299  end;
300
301end;
302
303//..............................................................................
304
305function TSQLiteDatabase.GetTable(const SQL: string): TSQLiteTable;
306begin
307  Result := TSQLiteTable.Create(Self, SQL);
308end;
309
310procedure TSQLiteDatabase.BeginTransaction;
311begin
312  if not self.fInTrans then
313  begin
314    self.ExecSQL('BEGIN TRANSACTION;');
315    self.fInTrans := true;
316  end
317  else
318  begin raise ESqliteException.Create('Transaction already open') end;
319end;
320
321procedure TSQLiteDatabase.Commit;
322begin
323  self.ExecSQL('COMMIT;');
324  self.fInTrans := false;
325end;
326
327procedure TSQLiteDatabase.Rollback;
328begin
329  self.ExecSQL('ROLLBACK;');
330  self.fInTrans := false;
331end;
332
333function TSQLiteDatabase.TableExists(TableName: string): boolean;
334var
335  sql: string;
336  ds: TSqliteTable;
337begin
338//returns true if table exists in the database
339  sql := 'select [sql] from sqlite_master where [type] = ''table'' and lower(name) = ''' + lowercase(TableName) + ''' ';
340
341  try
342
343    ds := self.GetTable(sql);
344
345    result := (ds.Count > 0);
346
347  finally
348
349    freeandnil(ds);
350
351  end;
352
353end;
354
355
356//------------------------------------------------------------------------------
357// TSQLiteTable
358//------------------------------------------------------------------------------
359
360constructor TSQLiteTable.Create(DB: TSQLiteDatabase; const SQL: string);
361var
362  Stmt: TSQLiteStmt;
363  NextSQLStatement: Pchar;
364  iStepResult: integer;
365
366  ptr: pointer;
367  iNumBytes: integer;
368  thisBlobValue: TMemoryStream;
369  thisStringValue: pstring;
370  thisBoolValue: pBoolean;
371  thisDoubleValue: pDouble;
372  thisIntValue: pInteger;
373  thisColType: pInteger;
374  i: integer;
375  DeclaredColType: Pchar;
376  ActualColType: integer;
377  ptrValue: Pchar;
378
379begin
380
381  try
382
383    self.fRowCount := 0;
384    self.fColCount := 0;
385
386//if there are several SQL statements in SQL, NextSQLStatment points to the
387//beginning of the next one. Prepare only prepares the first SQL statement.
388
389    if Sqlite3_Prepare(Db.fDB, PChar(SQL), -1, Stmt, NextSQLStatement) <> SQLITE_OK then
390    begin Db.RaiseError('Error executing SQL', SQL) end;
391
392    if (Stmt = nil) then
393    begin Db.RaiseError('Could not prepare SQL statement', SQL) end;
394
395    iStepResult := Sqlite3_step(Stmt);
396
397    while (iStepResult <> SQLITE_DONE) do
398    begin
399
400      case iStepResult of
401        SQLITE_ROW:
402          begin
403
404            inc(fRowCount);
405
406            if (fRowCount = 1) then
407            begin
408     //get data types
409              fCols := TStringList.Create;
410              fCols.CaseSensitive := False;
411              fColTypes := TList.Create;
412
413              fColCount := SQLite3_ColumnCount(stmt);
414
415              for i := 0 to Pred(fColCount) do
416              begin
417                fCols.Add(Sqlite3_ColumnName(stmt, i));
418              end;
419
420              for i := 0 to Pred(fColCount) do
421              begin
422
423                new(thisColType);
424                DeclaredColType := Sqlite3_ColumnDeclType(stmt, i);
425
426                if DeclaredColType = nil then begin
427                //use the actual column type instead
428                //seems to be needed for last_insert_rowid
429                  thisColType^ := Sqlite3_ColumnType(stmt, i);
430                end else begin
431                  DeclaredColType := strupper(DeclaredColType);
432                 
433                  if DeclaredColType = 'INTEGER' then
434                  begin thisColType^ := dtInt end
435                  else
436                    if DeclaredColType = 'BOOLEAN' then
437                    begin thisColType^ := dtBool end
438                    else
439                      if (DeclaredColType = 'NUMERIC') or (DeclaredColType = 'FLOAT') or (DeclaredColType = 'DOUBLE') then
440                      begin thisColType^ := dtNumeric end
441                      else
442                        if DeclaredColType = 'BLOB' then
443                        begin thisColType^ := dtBlob end
444                        else
445                        begin thisColType^ := dtStr end;
446                  end;
447
448                fColTypes.Add(thiscoltype);
449              end;
450
451              fResults := TList.Create;
452
453            end;
454
455     //get column values
456            for i := 0 to Pred(ColCount) do
457            begin
458
459              ActualColType := Sqlite3_ColumnType(stmt, i);
460              if (ActualColType = SQLITE_NULL) then
461              begin fResults.Add(nil) end
462              else
463              begin
464                if pInteger(fColTypes[i])^ = dtInt then
465                begin
466                  new(thisintvalue);
467                  thisintvalue^ := Sqlite3_ColumnInt(stmt, i);
468                  fResults.Add(thisintvalue);
469                end
470                else
471                  if pInteger(fColTypes[i])^ = dtBool then
472                  begin
473                    new(thisboolvalue);
474                    thisboolvalue^ := not (Sqlite3_ColumnInt(stmt, i) = 0);
475                    fResults.Add(thisboolvalue);
476                  end
477                  else
478                    if pInteger(fColTypes[i])^ = dtNumeric then
479                    begin
480                      new(thisdoublevalue);
481                      thisdoublevalue^ := Sqlite3_ColumnDouble(stmt, i);
482                      fResults.Add(thisdoublevalue);
483                    end
484                    else
485                      if pInteger(fColTypes[i])^ = dtBlob then
486                      begin
487                        iNumBytes := Sqlite3_ColumnBytes(stmt, i);
488
489                        if iNumBytes = 0 then
490                        begin thisblobvalue := nil end
491                        else
492                        begin
493                          thisblobvalue := TMemoryStream.Create;
494                          thisblobvalue.position := 0;
495                          ptr := Sqlite3_ColumnBlob(stmt, i);
496                          thisblobvalue.writebuffer(ptr^, iNumBytes);
497                        end;
498                        fResults.Add(thisblobvalue);
499
500                      end
501                      else
502                      begin
503                        new(thisstringvalue);
504                        ptrValue := Sqlite3_ColumnText(stmt, i);
505                        setstring(thisstringvalue^, ptrvalue, strlen(ptrvalue));
506                        fResults.Add(thisstringvalue);
507                      end;
508              end;
509
510            end;
511
512
513
514          end;
515
516        SQLITE_BUSY:
517          begin raise ESqliteException.CreateFmt('Could not prepare SQL statement', [SQL, 'SQLite is Busy']) end;
518      else
519        begin Db.RaiseError('Could not retrieve data', SQL) end;
520      end;
521
522      iStepResult := Sqlite3_step(Stmt);
523
524    end;
525
526    fRow := 0;
527
528  finally
529    if Assigned(Stmt) then
530    begin Sqlite3_Finalize(stmt) end;
531  end;
532
533end;
534
535//..............................................................................
536
537destructor TSQLiteTable.Destroy;
538var i: integer;
539  iColNo: integer;
540begin
541
542
543  if Assigned(fResults) then
544  begin for i := 0 to fResults.Count - 1 do
545    begin
546    //check for blob type
547      iColNo := (i mod fColCount);
548      case pInteger(self.fColTypes[iColNo])^ of
549      dtBlob:
550          begin
551          TMemoryStream(fResults[i]).free;
552          end;
553      dtStr:
554          begin
555           if fResults[i] <> nil then
556           begin
557               setstring(string(fResults[i]^), nil, 0);
558               dispose(fResults[i]);
559           end;
560          end;
561      else
562        begin
563        dispose(fResults[i])
564        end;
565      end;
566    end;
567    fResults.Free;
568   end;
569
570    if Assigned(fCols) then
571    begin fCols.Free end;
572
573    if Assigned(fColTypes) then
574    begin for i := 0 to fColTypes.Count - 1 do
575      begin
576        dispose(fColTypes[i]);
577      end end;
578    fColTypes.Free;
579    inherited;
580  end;
581
582//..............................................................................
583
584function TSQLiteTable.GetColumns(I: Integer): string;
585begin
586  Result := fCols[I];
587end;
588
589//..............................................................................
590
591function TSQLiteTable.GetCountResult: Integer;
592begin
593  if not EOF then
594  begin Result := StrToInt(Fields[0]) end
595  else
596  begin Result := 0 end;
597end;
598
599function TSQLiteTable.GetCount: Integer;
600begin
601  Result := FRowCount;
602end;
603
604//..............................................................................
605
606function TSQLiteTable.GetEOF: Boolean;
607begin
608  Result := fRow >= fRowCount;
609end;
610
611function TSQLiteTable.GetBOF: Boolean;
612begin
613  Result := fRow <= 0;
614end;
615
616//..............................................................................
617
618function TSQLiteTable.GetFieldByName(FieldName: string): string;
619begin
620  Result := GetFields(self.GetFieldIndex(FieldName));
621end;
622
623function TSQLiteTable.GetFieldIndex(FieldName: string): integer;
624begin
625
626  if (fCols = nil) then
627  begin
628    raise ESqliteException.Create('Field ' + fieldname + ' Not found. Empty dataset');
629    exit;
630  end;
631
632  if (fCols.count = 0) then
633  begin
634    raise ESqliteException.Create('Field ' + fieldname + ' Not found. Empty dataset');
635    exit;
636  end;
637
638  result := fCols.IndexOf(FieldName);
639
640  if (result < 0) then
641  begin raise ESqliteException.Create('Field not found in dataset: ' + fieldname) end;
642
643end;
644
645//..............................................................................
646
647function TSQLiteTable.GetFields(I: Integer): string;
648var
649  thisvalue: pstring;
650  ptr: pointer;
651  thisboolvalue: pBoolean;
652  thistype: integer;
653begin
654  Result := '';
655
656  if EOF then
657  begin raise ESqliteException.Create('Table is at End of File') end;
658
659//integer and boolean types are not stored in the resultset
660//as strings, so they should be retrieved using the type-specific
661//methods
662
663  thistype := pInteger(self.fColTypes[I])^;
664
665  if (thistype = dtInt) or (thistype = dtNumeric) or (thistype = dtBlob) then
666  begin
667    ptr := self.fResults[(self.frow * self.fColCount) + I];
668
669    if ptr <> nil then
670    begin
671      raise ESqliteException.Create('Use the specific methods for integer, numeric or blob fields');
672    end;
673
674  end
675  else
676    if pInteger(self.fColTypes[I])^ = dtBool then
677    begin
678      thisboolvalue := self.fResults[(self.frow * self.fColCount) + I];
679      if thisboolvalue <> nil then
680      begin if thisboolvalue^ then
681        begin result := '1' end
682        else
683        begin result := '0' end end;
684    end
685
686    else
687
688    begin
689
690      thisvalue := self.fResults[(self.frow * self.fColCount) + I];
691      if (thisvalue <> nil) then
692      begin Result := thisvalue^ end
693      else
694      begin Result := '' end; //return empty string
695    end;
696
697end;
698
699function TSqliteTable.FieldAsBlob(FieldName: string): TMemoryStream;
700var
701  i: Integer;
702begin
703
704  if EOF then
705  begin raise ESqliteException.Create('Table is at End of File') end;
706
707  i:=Self.FieldIndex[FieldName];
708
709  if (self.fResults[(self.frow * self.fColCount) + I] = nil) then
710  begin result := nil end
711  else
712    if pInteger(self.fColTypes[I])^ = dtBlob then
713    begin result := TMemoryStream(self.fResults[(self.frow * self.fColCount) + I]) end
714    else
715    begin raise ESqliteException.Create('Not a Blob field') end;
716end;
717
718function TSqliteTable.FieldAsBlobText(FieldName: string): string;
719var
720  MemStream: TMemoryStream;
721  Buffer: PChar;
722begin
723  result := '';
724
725  MemStream := self.FieldAsBlob(FieldName);
726
727  if MemStream <> nil then
728  begin if MemStream.Size > 0 then
729    begin
730      MemStream.position := 0;
731
732      Buffer := stralloc(MemStream.Size + 1);
733      MemStream.readbuffer(Buffer[0], MemStream.Size);
734      (Buffer + MemStream.Size)^ := chr(0);
735      SetString(Result, Buffer, MemStream.size);
736      strdispose(Buffer);
737    end end;
738
739end;
740
741
742function TSqliteTable.FieldAsInteger(FieldName: string): integer;
743var
744  i: Integer;
745begin
746
747  if EOF then
748  begin raise ESqliteException.Create('Table is at End of File') end;
749
750  i:=Self.FieldIndex[FieldName];
751
752  if (self.fResults[(self.frow * self.fColCount) + I] = nil) then
753  begin result := 0 end
754  else
755    if pInteger(self.fColTypes[I])^ = dtInt then
756    begin result := pInteger(self.fResults[(self.frow * self.fColCount) + I])^ end
757    else
758      if pInteger(self.fColTypes[I])^ = dtNumeric then
759      begin result := trunc(strtofloat(pString(self.fResults[(self.frow * self.fColCount) + I])^)) end
760      else
761      begin raise ESqliteException.Create('Not an integer or numeric field') end;
762
763end;
764
765function TSqliteTable.FieldAsDouble(FieldName: string): double;
766var
767  i: Integer;
768begin
769
770  if EOF then
771  begin raise ESqliteException.Create('Table is at End of File') end;
772
773  i:=Self.FieldIndex[FieldName];
774
775  if (self.fResults[(self.frow * self.fColCount) + I] = nil) then
776  begin result := 0 end
777  else
778    if pInteger(self.fColTypes[I])^ = dtInt then
779    begin result := pInteger(self.fResults[(self.frow * self.fColCount) + I])^ end
780    else
781      if pInteger(self.fColTypes[I])^ = dtNumeric then
782      begin result := pDouble(self.fResults[(self.frow * self.fColCount) + I])^ end
783      else
784      begin raise ESqliteException.Create('Not an integer or numeric field') end;
785
786end;
787
788function TSqliteTable.FieldAsBool(FieldName: string): boolean;
789var
790  i: Integer;
791begin
792
793  if EOF then
794  begin raise ESqliteException.Create('Table is at End of File') end;
795
796  i:=Self.FieldIndex[FieldName];
797
798  if (self.fResults[(self.frow * self.fColCount) + I] = nil) then
799  begin result := false end
800  else
801    if pInteger(self.fColTypes[I])^ = dtBool then
802    begin result := pBoolean(self.fResults[(self.frow * self.fColCount) + I])^ end
803    else
804    begin raise ESqliteException.Create('Not a boolean field') end;
805end;
806
807function TSqliteTable.FieldAsString(FieldName: string): string;
808var
809  i: Integer;
810begin
811
812  if EOF then
813  begin raise ESqliteException.Create('Table is at End of File') end;
814
815  i:=Self.FieldIndex[FieldName];
816
817  if (self.fResults[(self.frow * self.fColCount) + I] = nil) then
818  begin result := '' end
819  else
820  begin result := self.GetFields(I) end;
821
822end;
823
824function TSqliteTable.FieldIsNull(FieldName: string): boolean;
825var
826  thisvalue: pointer;
827  i: Integer;
828begin
829
830  if EOF then
831  begin raise ESqliteException.Create('Table is at End of File') end;
832
833  i:=Self.FieldIndex[FieldName];
834
835  thisvalue := self.fResults[(self.frow * self.fColCount) + I];
836  result := (thisvalue = nil);
837end;
838
839//..............................................................................
840
841function TSQLiteTable.Next: boolean;
842begin
843  result := false;
844  if not EOF then
845  begin
846    Inc(fRow);
847    result := true;
848  end;
849end;
850
851function TSQLiteTable.Previous: boolean;
852begin
853  result := false;
854  if not BOF then
855  begin
856    Dec(fRow);
857    result := true;
858  end;
859end;
860
861function TSQLiteTable.MoveFirst: boolean;
862begin
863  result := false;
864  if self.fRowCount > 0 then
865  begin
866    fRow := 0;
867    result := true;
868  end;
869end;
870
871function TSQLiteTable.MoveLast: boolean;
872begin
873  result := false;
874  if self.fRowCount > 0 then
875  begin
876    fRow := fRowCount - 1;
877    result := true;
878  end;
879end;
880
881
882end.
883
Note: See TracBrowser for help on using the repository browser.