zeoslib  UNKNOWN
 All Files
ZDbcSqLiteResultSet.pas
Go to the documentation of this file.
1 {*********************************************************}
2 { }
3 { Zeos Database Objects }
4 { SQLite Database Connectivity Classes }
5 { }
6 { Originally written by Sergey Seroukhov }
7 { }
8 {*********************************************************}
9 
10 {@********************************************************}
11 { Copyright (c) 1999-2012 Zeos Development Group }
12 { }
13 { License Agreement: }
14 { }
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. }
20 { }
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. }
39 { }
40 { }
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) }
45 { }
46 { http://www.sourceforge.net/projects/zeoslib. }
47 { }
48 { }
49 { Zeos Development Group. }
50 {********************************************************@}
51 
52 unit ZDbcSqLiteResultSet;
53 
54 interface
55 
56 {$I ZDbc.inc}
57 
58 uses
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;
63 
64 type
65 
66  {** Implements SQLite ResultSet Metadata. }
67  TZSQLiteResultSetMetadata = class(TZAbstractResultSetMetadata)
68  public
69 // function IsAutoIncrement(Column: Integer): Boolean; override;
70  function IsNullable(Column: Integer): TZColumnNullableType; override;
71  end;
72 
73  {** Implements SQLite ResultSet. }
74  TZSQLiteResultSet = class(TZAbstractResultSet)
75  private
76  FFetchingReady: Boolean;
77  FHandle: Psqlite;
78  FStmtHandle: Psqlite_vm;
79  FColumnCount: Integer;
80  FColumnNames: PPAnsiChar;
81  FColumnValues: PPAnsiChar;
82  FPlainDriver: IZSQLitePlainDriver;
83  FFreeHandle: Boolean;
84  protected
85  procedure Open; override;
86  procedure FreeHandle;
87  function InternalGetString(ColumnIndex: Integer): RawByteString; override;
88  public
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;
94 
95  procedure Close; override;
96 
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;
112 
113  function Next: Boolean; override;
114  end;
115 
116  {** Implements a cached resolver with SQLite specific functionality. }
117  TZSQLiteCachedResolver = class (TZGenericCachedResolver, IZCachedResolver)
118  private
119  FHandle: Psqlite;
120  FPlainDriver: IZSQLitePlainDriver;
121  FAutoColumnIndex: Integer;
122  public
123  constructor Create(PlainDriver: IZSQLitePlainDriver; Handle: Psqlite;
124  Statement: IZStatement; Metadata: IZResultSetMetadata);
125 
126  procedure PostUpdates(Sender: IZCachedResultSet; UpdateType: TZRowUpdateType;
127  OldRowAccessor, NewRowAccessor: TZRowAccessor); override;
128 
129  function FormCalculateStatement(Columns: TObjectList): string; override;
130 
131  procedure UpdateAutoIncrementFields(Sender: IZCachedResultSet; UpdateType: TZRowUpdateType;
132  OldRowAccessor, NewRowAccessor: TZRowAccessor; Resolver: IZCachedResolver); override;
133  end;
134 
135 implementation
136 
137 uses
138  ZMessages, ZDbcSqLite, ZDbcSQLiteUtils, ZMatchPattern, ZEncoding,
139  ZDbcLogging, ZDbcSqLiteStatement;
140 
141 { TZSQLiteResultSetMetadata }
142 
143 {**
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
147 }
148 {
149 function TZSQLiteResultSetMetadata.IsAutoIncrement(Column: Integer): Boolean;
150 begin
151  Result := TZColumnInfo(ResultSet.ColumnsInfo[Column - 1]).AutoIncrement;
152 end;
153 }
154 
155 {**
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>
160 }
161 function TZSQLiteResultSetMetadata.IsNullable(Column: Integer):
162  TZColumnNullableType;
163 begin
164  if IsAutoIncrement(Column) then
165  Result := ntNullable
166  else
167  Result := inherited IsNullable(Column);
168 end;
169 
170 { TZSQLiteResultSet }
171 
172 {**
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.
180 }
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);
185 begin
186  inherited Create(Statement, SQL, TZSQLiteResultSetMetadata.Create(
187  Statement.GetConnection.GetMetadata, SQL, Self),
188  Statement.GetConnection.GetConSettings);
189 
190  FHandle := Handle;
191  FStmtHandle := StmtHandle;
192  FPlainDriver := PlainDriver;
193  ResultSetConcurrency := rcReadOnly;
194  FColumnCount := ColumnCount;
195  FColumnNames := ColumnNames;
196  FColumnValues := ColumnValues;
197  FFreeHandle := AllowFreeHandle;
198  FFetchingReady := False;
199 
200  Open;
201 end;
202 
203 {**
204  Destroys this object and cleanups the memory.
205 }
206 destructor TZSQLiteResultSet.Destroy;
207 begin
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;
212 
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);
216  FColumnNames := nil;
217 
218  inherited Destroy;
219 end;
220 
221 {**
222  Opens this recordset.
223 }
224 procedure TZSQLiteResultSet.Open;
225 var
226  I: Integer;
227  ColumnInfo: TZColumnInfo;
228  FieldName: PPAnsiChar;
229  FieldPrecision: Integer;
230  FieldDecimals: Integer;
231  TypeName: PPAnsiChar;
232 begin
233  if ResultSetConcurrency = rcUpdatable then
234  raise EZSQLException.Create(SLiveResultSetsAreNotSupported);
235 
236  LastRowNo := 0;
237 
238  { Fills the column info. }
239  ColumnsInfo.Clear;
240  FieldName := FColumnNames;
241  TypeName := FColumnNames;
242  Inc(TypeName, FColumnCount);
243  for I := 1 to FColumnCount do
244  begin
245  ColumnInfo := TZColumnInfo.Create;
246  with ColumnInfo do
247  begin
248  ColumnLabel := ZDbcString(FieldName^);
249  Inc(FieldName);
250  TableName := '';
251  ReadOnly := False;
252  if TypeName^ <> nil then
253  begin
254  ColumnType := ConvertSQLiteTypeToSQLType(ZDbcString(TypeName^),
255  FieldPrecision, FieldDecimals, ConSettings.CPType);
256  Inc(TypeName);
257  end
258  else
259  begin
260  ColumnType := ConvertSQLiteTypeToSQLType(ZDbcString(FPlainDriver.column_decltype(FStmtHandle,I-1)),
261  FieldPrecision, FieldDecimals, ConSettings.CPType);
262  end;
263  if ColumnType = stString then
264  if Zencoding.ZDefaultSystemCodePage = zCP_UTF8 then
265  ColumnDisplaySize := FieldPrecision div 4
266  else
267  ColumnDisplaySize := FieldPrecision div 2;
268 
269  if ColumnType = stUnicodeString then
270  ColumnDisplaySize := FieldPrecision div 2;
271 
272  AutoIncrement := False;
273  Precision := FieldPrecision;
274  Scale := FieldDecimals;
275  Signed := True;
276  Nullable := ntNullable;
277  end;
278 
279  ColumnsInfo.Add(ColumnInfo);
280  end;
281 
282  inherited Open;
283 end;
284 
285 {**
286  Frees statement handle.
287 }
288 procedure TZSQLiteResultSet.FreeHandle;
289 var
290  ErrorCode: Integer;
291 begin
292  if FFreeHandle then
293  begin
294  if Assigned(FStmtHandle) then
295  ErrorCode := FPlainDriver.Finalize(FStmtHandle)
296  else
297  ErrorCode := SQLITE_OK;
298  FStmtHandle := nil;
299  CheckSQLiteError(FPlainDriver, FStmtHandle, ErrorCode, nil,
300  lcOther, 'FINALIZE SQLite VM');
301  end
302  else
303  if FStmtHandle <> nil then
304  begin
305  ErrorCode := FPlainDriver.reset(FStmtHandle);
306  FStmtHandle := nil;
307  CheckSQLiteError(FPlainDriver, FStmtHandle, ErrorCode, nil, lcBindPrepStmt, 'Reset Prepared Stmt');
308  FFetchingReady := True;
309  end;
310 end;
311 
312 {**
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.
316 
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.
324 }
325 procedure TZSQLiteResultSet.Close;
326 var stmt: IZSQLiteCAPIPreparedStatement;
327 begin
328  if Assigned(Statement) and Supports(Statement, IZSQLiteCAPIPreparedStatement, stmt) then
329  stmt.FreeReference;
330  inherited Close;
331  FreeHandle;
332 end;
333 
334 {**
335  Indicates if the value of the designated column in the current row
336  of this <code>ResultSet</code> object is Null.
337 
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.
341 }
342 function TZSQLiteResultSet.IsNull(ColumnIndex: Integer): Boolean;
343 var
344  Temp: PPAnsiChar;
345 begin
346 {$IFNDEF DISABLE_CHECKING}
347  CheckClosed;
348  if (LastRowNo = 0) or (FColumnValues = nil) then
349  raise EZSQLException.Create(SRowDataIsNotAvailable);
350 {$ENDIF}
351 
352  Temp := FColumnValues;
353  Inc(Temp, ColumnIndex - 1);
354  Result := (Temp^ = nil);
355 end;
356 
357 {**
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.
361 
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>
365 }
366 function TZSQLiteResultSet.GetPChar(ColumnIndex: Integer): PChar;
367 var
368  TempStr: String;
369 begin
370 {$IFNDEF DISABLE_CHECKING}
371  CheckClosed;
372  if (LastRowNo = 0) or (FColumnValues = nil) then
373  raise EZSQLException.Create(SRowDataIsNotAvailable);
374 {$ENDIF}
375 
376  TempStr := GetString(ColumnIndex);
377  Result := PChar(TempStr);
378  LastWasNull := Result = nil;
379 end;
380 
381 {**
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.
385 
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>
389 }
390 function TZSQLiteResultSet.InternalGetString(ColumnIndex: Integer): RawByteString;
391 var
392  Temp: PPAnsiChar;
393 begin
394 {$IFNDEF DISABLE_CHECKING}
395  CheckClosed;
396  if (LastRowNo = 0) or (FColumnValues = nil) then
397  raise EZSQLException.Create(SRowDataIsNotAvailable);
398 {$ENDIF}
399 
400  Temp := FColumnValues;
401  Inc(Temp, ColumnIndex - 1);
402  Result := Temp^;
403  LastWasNull := Temp^ = nil;
404 end;
405 
406 {**
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.
410 
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>
414 }
415 function TZSQLiteResultSet.GetBoolean(ColumnIndex: Integer): Boolean;
416 var
417  Temp: string;
418 begin
419 {$IFNDEF DISABLE_CHECKING}
420  CheckColumnConvertion(ColumnIndex, stBoolean);
421 {$ENDIF}
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);
425 end;
426 
427 {**
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.
431 
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>
435 }
436 function TZSQLiteResultSet.GetByte(ColumnIndex: Integer): Byte;
437 begin
438 {$IFNDEF DISABLE_CHECKING}
439  CheckColumnConvertion(ColumnIndex, stByte);
440 {$ENDIF}
441  Result := Byte(StrToIntDef(String(InternalGetString(ColumnIndex)), 0));
442 end;
443 
444 {**
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.
448 
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>
452 }
453 function TZSQLiteResultSet.GetShort(ColumnIndex: Integer): SmallInt;
454 begin
455 {$IFNDEF DISABLE_CHECKING}
456  CheckColumnConvertion(ColumnIndex, stShort);
457 {$ENDIF}
458  Result := SmallInt(StrToIntDef(String(InternalGetString(ColumnIndex)), 0));
459 end;
460 
461 {**
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.
465 
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>
469 }
470 function TZSQLiteResultSet.GetInt(ColumnIndex: Integer): Integer;
471 begin
472 {$IFNDEF DISABLE_CHECKING}
473  CheckColumnConvertion(ColumnIndex, stInteger);
474 {$ENDIF}
475  Result := StrToIntDef(String(InternalGetString(ColumnIndex)), 0);
476 end;
477 
478 {**
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.
482 
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>
486 }
487 function TZSQLiteResultSet.GetLong(ColumnIndex: Integer): Int64;
488 begin
489 {$IFNDEF DISABLE_CHECKING}
490  CheckColumnConvertion(ColumnIndex, stLong);
491 {$ENDIF}
492  Result := StrToInt64Def(String(InternalGetString(ColumnIndex)), 0);
493 end;
494 
495 {**
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.
499 
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>
503 }
504 function TZSQLiteResultSet.GetFloat(ColumnIndex: Integer): Single;
505 begin
506 {$IFNDEF DISABLE_CHECKING}
507  CheckColumnConvertion(ColumnIndex, stFloat);
508 {$ENDIF}
509  Result := SQLStrToFloatDef(InternalGetString(ColumnIndex), 0);
510 end;
511 
512 {**
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.
516 
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>
520 }
521 function TZSQLiteResultSet.GetDouble(ColumnIndex: Integer): Double;
522 begin
523 {$IFNDEF DISABLE_CHECKING}
524  CheckColumnConvertion(ColumnIndex, stDouble);
525 {$ENDIF}
526  Result := SQLStrToFloatDef(InternalGetString(ColumnIndex), 0);
527 end;
528 
529 {**
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.
533 
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>
538 }
539 function TZSQLiteResultSet.GetBigDecimal(ColumnIndex: Integer): Extended;
540 begin
541 {$IFNDEF DISABLE_CHECKING}
542  CheckColumnConvertion(ColumnIndex, stBigDecimal);
543 {$ENDIF}
544  Result := SQLStrToFloatDef(InternalGetString(ColumnIndex), 0);
545 end;
546 
547 {**
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.
552 
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>
556 }
557 function TZSQLiteResultSet.GetBytes(ColumnIndex: Integer): TByteDynArray;
558 begin
559 {$IFNDEF DISABLE_CHECKING}
560  CheckColumnConvertion(ColumnIndex, stBytes);
561 {$ENDIF}
562  Result := StrToBytes(DecodeString(InternalGetString(ColumnIndex)));
563 end;
564 
565 {**
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.
569 
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>
573 }
574 function TZSQLiteResultSet.GetDate(ColumnIndex: Integer): TDateTime;
575 var
576  Value: string;
577  TempDate: TDateTime;
578 begin
579 {$IFNDEF DISABLE_CHECKING}
580  CheckColumnConvertion(ColumnIndex, stDate);
581 {$ENDIF}
582  Value := String(InternalGetString(ColumnIndex));
583  if IsMatch('????-??-??*', Value) then
584  Result := Trunc(AnsiSQLDateToDateTime(Value))
585  else
586  begin
587  TempDate := Trunc(SQLStrToFloatDef(Value, 0));
588  Result := Trunc(TimestampStrToDateTime(Value));
589  if ( Result = 0 ) and not ( TempDate = 0 ) then
590  Result := TempDate;
591  end;
592  LastWasNull := Result = 0;
593 end;
594 
595 {**
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.
599 
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>
603 }
604 function TZSQLiteResultSet.GetTime(ColumnIndex: Integer): TDateTime;
605 var
606  Value: string;
607  TempTime: TDateTime;
608 begin
609 {$IFNDEF DISABLE_CHECKING}
610  CheckColumnConvertion(ColumnIndex, stTime);
611 {$ENDIF}
612  Value := String(InternalGetString(ColumnIndex));
613  if IsMatch('*??:??:??*', Value) then
614  Result := Frac(AnsiSQLDateToDateTime(Value))
615  else
616  begin
617  TempTime := Frac(SQLStrToFloatDef(Value, 0));
618  Result := Frac(TimestampStrToDateTime(Value));
619  if ( Result = 0 ) and not ( TempTime = 0 ) then
620  Result := TempTime;
621  end;
622  LastWasNull := Result = 0;
623 end;
624 
625 {**
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.
629 
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
634 }
635 function TZSQLiteResultSet.GetTimestamp(ColumnIndex: Integer): TDateTime;
636 var
637  Value: string;
638  TempTimeStamp: TDateTime;
639 begin
640 {$IFNDEF DISABLE_CHECKING}
641  CheckColumnConvertion(ColumnIndex, stTimestamp);
642 {$ENDIF}
643  Value := String(InternalGetString(ColumnIndex));
644  if IsMatch('????-??-??*', Value) then
645  Result := AnsiSQLDateToDateTime(Value)
646  else
647  begin
648  TempTimeStamp := SQLStrToFloatDef(Value, 0);
649  Result := TimestampStrToDateTime(Value);
650  if ( Result = 0 ) and not ( TempTimeStamp = 0 ) then
651  Result := TempTimeStamp;
652  end;
653  LastWasNull := Result = 0;
654 end;
655 
656 {**
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.
660 
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
663  the specified column
664 }
665 function TZSQLiteResultSet.GetBlob(ColumnIndex: Integer): IZBlob;
666 var
667  Stream: TStream;
668  AnsiTemp: RawByteString;
669 begin
670  Result := nil;
671 {$IFNDEF DISABLE_CHECKING}
672  CheckBlobColumn(ColumnIndex);
673 {$ENDIF}
674  LastWasNull := IsNull(ColumnIndex);
675  if LastWasNull then
676  Exit;
677 
678  Stream := nil;
679  try
680  if not LastWasNull then
681  begin
682  case GetMetadata.GetColumnType(ColumnIndex) of
683  stAsciiStream:
684  if ConSettings.AutoEncode then
685  Stream := TStringStream.Create(GetValidatedAnsiString(InternalGetString(ColumnIndex), ConSettings, True))
686  else
687  Stream := TStringStream.Create(InternalGetString(ColumnIndex));
688  stUnicodeStream:
689  begin
690  AnsiTemp := InternalGetString(ColumnIndex);
691  if Length(AnsiTemp) = 0 then
692  Stream := TMemoryStream.Create
693  else
694  Stream := GetValidatedUnicodeStream(InternalGetString(ColumnIndex), ConSettings, True);
695  end;
696  stBinaryStream:
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)))
700  else
701  Stream := FPlaindriver.column_blob(FStmtHandle,columnIndex);
702  end;
703  Result := TZAbstractBlob.CreateWithStream(Stream, GetStatement.GetConnection, GetMetadata.GetColumnType(ColumnIndex) = stUnicodeStream);
704  end
705  else
706  Result := TZAbstractBlob.CreateWithStream(nil, GetStatement.GetConnection);
707  finally
708  if Assigned(Stream) then
709  Stream.Free;
710  end;
711 end;
712 
713 {**
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.
719 
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.
724 
725  @return <code>true</code> if the new current row is valid;
726  <code>false</code> if there are no more rows
727 }
728 function TZSQLiteResultSet.Next: Boolean;
729 var
730  ErrorCode: Integer;
731 begin
732  { Checks for maximum row. }
733  Result := False;
734 
735  if (MaxRows > 0) and (RowNo >= MaxRows) then
736  Exit;
737 
738  if LastRowNo = 0 then
739  begin
740  Result := FColumnValues <> nil;
741  if Result then
742  begin
743  LastRowNo := LastRowNo + 1;
744  RowNo := RowNo + 1;
745  end
746  else
747  begin
748  if RowNo <= LastRowNo then
749  RowNo := LastRowNo + 1;
750  end;
751  end
752  else
753  begin
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
759  begin
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);
763  FColumnNames := nil;
764  ErrorCode := FPlainDriver.Step(FStmtHandle, FColumnCount,
765  FColumnValues, FColumnNames);
766  CheckSQLiteError(FPlainDriver, FStmtHandle, ErrorCode, nil, lcOther, 'FETCH');
767  end;
768 
769  if FColumnValues <> nil then
770  begin
771  RowNo := RowNo + 1;
772  if LastRowNo < RowNo then
773  LastRowNo := RowNo;
774  Result := True;
775  end
776  else
777  begin
778  if RowNo <= LastRowNo then
779  RowNo := LastRowNo + 1;
780  Result := False;
781  end;
782  end;
783 
784  { Frees handle when reads to the end. }
785  if not Result and Assigned(FStmtHandle) then
786  FreeHandle;
787 end;
788 
789 { TZSQLiteCachedResolver }
790 
791 {**
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.
797 }
798 constructor TZSQLiteCachedResolver.Create(PlainDriver: IZSQLitePlainDriver;
799  Handle: Psqlite; Statement: IZStatement; Metadata: IZResultSetMetadata);
800 var
801  I: Integer;
802 begin
803  inherited Create(Statement, Metadata);
804  FPlainDriver := PlainDriver;
805  FHandle := Handle;
806 
807  { Defines an index of autoincrement field. }
808  FAutoColumnIndex := 0;
809  for I := 1 to Metadata.GetColumnCount do
810  begin
811  if Metadata.IsAutoIncrement(I) and
812  (Metadata.GetColumnType(I) in [stByte, stShort, stInteger, stLong]) then
813  begin
814  FAutoColumnIndex := I;
815  Break;
816  end;
817  end;
818 end;
819 
820 {**
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.
826 }
827 procedure TZSQLiteCachedResolver.PostUpdates(Sender: IZCachedResultSet;
828  UpdateType: TZRowUpdateType; OldRowAccessor, NewRowAccessor: TZRowAccessor);
829 begin
830  inherited PostUpdates(Sender, UpdateType, OldRowAccessor, NewRowAccessor);
831 
832  if (UpdateType = utInserted) then
833  UpdateAutoIncrementFields(Sender, UpdateType, OldRowAccessor, NewRowAccessor, Self);
834 end;
835 
836 {**
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.
842 }
843 procedure TZSQLiteCachedResolver.UpdateAutoIncrementFields(
844  Sender: IZCachedResultSet; UpdateType: TZRowUpdateType; OldRowAccessor,
845  NewRowAccessor: TZRowAccessor; Resolver: IZCachedResolver);
846 var
847  PlainDriver: IZSQLitePlainDriver;
848 begin
849  inherited;
850 
851  if (FAutoColumnIndex > 0) and
852  (OldRowAccessor.IsNull(FAutoColumnIndex) or (OldRowAccessor.GetValue(FAutoColumnIndex).VInteger = 0)) then
853  begin
854  PlainDriver := (Connection as IZSQLiteConnection).GetPlainDriver;
855 
856  NewRowAccessor.SetLong(FAutoColumnIndex, PlainDriver.LastInsertRowId(FHandle));
857  end;
858 end;
859 
860 // --> ms, 02/11/2005
861 {**
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.
865 }
866 function TZSQLiteCachedResolver.FormCalculateStatement(
867  Columns: TObjectList): string;
868 var
869  I: Integer;
870  Current: TZResolverParameter;
871 begin
872  Result := '';
873  if Columns.Count = 0 then
874  Exit;
875 
876  for I := 0 to Columns.Count - 1 do
877  begin
878  Current := TZResolverParameter(Columns[I]);
879  if Result <> '' then
880  Result := Result + ',';
881  if Current.DefaultValue <> '' then
882  Result := Result + Current.DefaultValue
883  else
884  Result := Result + 'NULL';
885  end;
886  Result := 'SELECT ' + Result;
887 end;
888 // <-- ms
889 
890 end.