zeoslib  UNKNOWN
 All Files
ZDbcSqLiteStatement.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 ZDbcSqLiteStatement;
53 
54 interface
55 
56 {$I ZDbc.inc}
57 
58 uses
59  Classes, {$IFDEF MSEgui}mclasses,{$ENDIF} SysUtils,
60  {$IFDEF WITH_WIDESTRUTILS}WideStrUtils, {$ENDIF}
61  ZDbcIntfs, ZDbcStatement, ZPlainSqLiteDriver, ZCompatibility, ZDbcLogging,
62  ZVariant;
63 
64 type
65 
66  {** Implements Generic SQLite Statement. }
67  TZSQLiteStatement = class(TZAbstractStatement)
68  private
69  FHandle: Psqlite;
70  FPlainDriver: IZSQLitePlainDriver;
71 
72  function CreateResultSet(const SQL: string; StmtHandle: Psqlite_vm;
73  ColumnCount: Integer; ColumnNames: PPAnsiChar;
74  ColumnValues: PPAnsiChar): IZResultSet;
75  public
76  constructor Create(PlainDriver: IZSQLitePlainDriver;
77  Connection: IZConnection; Info: TStrings; Handle: Psqlite);
78 
79  function ExecuteQuery(const SQL: RawByteString): IZResultSet; override;
80  function ExecuteUpdate(const SQL: RawByteString): Integer; override;
81  function Execute(const SQL: RawByteString): Boolean; override;
82  end;
83 
84  {$IFDEF ZEOS_TEST_ONLY}
85  {** Implements Prepared SQL Statement. }
86  TZSQLitePreparedStatement = class(TZEmulatedPreparedStatement)
87  private
88  FHandle: Psqlite;
89  FPlainDriver: IZSQLitePlainDriver;
90  protected
91  function CreateExecStatement: IZStatement; override;
92  function PrepareAnsiSQLParam(ParamIndex: Integer): RawByteString; override;
93  public
94  constructor Create(PlainDriver: IZSQLitePlainDriver;
95  Connection: IZConnection; const SQL: string; Info: TStrings;
96  Handle: Psqlite);
97  end;
98  {$ENDIF}
99 
100  IZSQLiteCAPIPreparedStatement = Interface(IZPreparedStatement)
101  ['{CA05874D-E817-4523-B0AF-DBCDD0CF85CA}']
102  Procedure FreeReference;
103  end;
104  {** Implements CAPI Prepared SQL Statement. }
105  TZSQLiteCAPIPreparedStatement = class(TZAbstractPreparedStatement,
106  IZSQLiteCAPIPreparedStatement)
107  private
108  FErrorCode: Integer;
109  FHandle: Psqlite;
110  FStmtHandle: Psqlite3_stmt;
111  FPlainDriver: IZSQLitePlainDriver;
112  FOpenResultSet: Pointer;
113  function CreateResultSet(const SQL: string; StmtHandle: Psqlite_vm;
114  ColumnCount: Integer; ColumnNames: PPAnsiChar;
115  ColumnValues: PPAnsiChar): IZResultSet;
116  protected
117  procedure SetASQL(const Value: RawByteString); override;
118  procedure SetWSQL(const Value: ZWideString); override;
119  procedure FreeReference;
120  protected //abstaction overrides
121  procedure PrepareInParameters; override;
122  procedure BindInParameters; override;
123  public
124  constructor Create(PlainDriver: IZSQLitePlainDriver;
125  Connection: IZConnection; const SQL: string; Info: TStrings; Handle: Psqlite);
126 
127  procedure Prepare; override;
128  procedure Unprepare; override;
129 
130  function ExecuteQueryPrepared: IZResultSet; override;
131  function ExecuteUpdatePrepared: Integer; override;
132  function ExecutePrepared: Boolean; override;
133  end;
134 
135 
136 implementation
137 
138 uses
139  Types, ZDbcSqLiteUtils, ZDbcSqLiteResultSet, ZSysUtils, ZEncoding,
140  ZMessages, ZDbcCachedResultSet{$IFDEF WITH_UNITANSISTRINGS}, AnsiStrings{$ENDIF};
141 
142 { TZSQLiteStatement }
143 
144 {**
145  Constructs this object and assignes the main properties.
146  @param PlainDriver a native SQLite plain driver.
147  @param Connection a database connection object.
148  @param Handle a connection handle pointer.
149  @param Info a statement parameters.
150 }
151 constructor TZSQLiteStatement.Create(PlainDriver: IZSQLitePlainDriver;
152  Connection: IZConnection; Info: TStrings; Handle: Psqlite);
153 begin
154  inherited Create(Connection, Info);
155  FHandle := Handle;
156  FPlainDriver := PlainDriver;
157  ResultSetType := rtScrollInsensitive;
158 end;
159 
160 {**
161  Creates a result set based on the current settings.
162  @return a created result set object.
163 }
164 
165 function TZSQLiteStatement.CreateResultSet(const SQL: string; StmtHandle: Psqlite_vm;
166  ColumnCount: Integer; ColumnNames: PPAnsiChar; ColumnValues: PPAnsiChar): IZResultSet;
167 var
168  CachedResolver: TZSQLiteCachedResolver;
169  NativeResultSet: TZSQLiteResultSet;
170  CachedResultSet: TZCachedResultSet;
171 begin
172  { Creates a native result set. }
173  NativeResultSet := TZSQLiteResultSet.Create(FPlainDriver, Self, SQL, FHandle,
174  StmtHandle, ColumnCount, ColumnNames, ColumnValues);
175  NativeResultSet.SetConcurrency(rcReadOnly);
176 
177  { Creates a cached result set. }
178  CachedResolver := TZSQLiteCachedResolver.Create(FPlainDriver, FHandle, Self,
179  NativeResultSet.GetMetaData);
180  CachedResultSet := TZCachedResultSet.Create(NativeResultSet, SQL,
181  CachedResolver,GetConnection.GetConSettings);
182 
183  { Fetches all rows to prevent blocking. }
184  CachedResultSet.SetType(rtScrollInsensitive);
185  CachedResultSet.Last;
186  CachedResultSet.BeforeFirst;
187  CachedResultSet.SetConcurrency(GetResultSetConcurrency);
188 
189  Result := CachedResultSet;
190 end;
191 
192 {**
193  Executes an SQL statement that returns a single <code>ResultSet</code> object.
194  @param sql typically this is a static SQL <code>SELECT</code> statement
195  @return a <code>ResultSet</code> object that contains the data produced by the
196  given query; never <code>null</code>
197 }
198 function TZSQLiteStatement.ExecuteQuery(const SQL: RawByteString): IZResultSet;
199 var
200  ErrorCode: Integer;
201  StmtHandle: Psqlite3_stmt;
202  ColumnCount: Integer;
203  ColumnValues: PPAnsiChar;
204  ColumnNames: PPAnsiChar;
205 begin
206  ColumnCount := 0;
207  ASQL := SQL; //preprepares SQL
208  ErrorCode := FPlainDriver.Prepare(FHandle, PAnsiChar(ASQL), Length(ASQL),
209  StmtHandle, nil);
210  CheckSQLiteError(FPlainDriver, FHandle, ErrorCode, nil, lcExecute, LogSQL);
211  DriverManager.LogMessage(lcExecute, FPlainDriver.GetProtocol, LogSQL);
212 
213  try
214  ErrorCode := FPlainDriver.Step(StmtHandle, ColumnCount,
215  ColumnValues, ColumnNames);
216  CheckSQLiteError(FPlainDriver, FHandle, ErrorCode, nil, lcOther, 'FETCH');
217  except
218  FPlainDriver.Finalize(StmtHandle);
219  raise;
220  end;
221 
222  Result := CreateResultSet(SSQL, StmtHandle, ColumnCount, ColumnNames,
223  ColumnValues);
224 end;
225 
226 {**
227  Executes an SQL <code>INSERT</code>, <code>UPDATE</code> or
228  <code>DELETE</code> statement. In addition,
229  SQL statements that return nothing, such as SQL DDL statements,
230  can be executed.
231 
232  @param sql an SQL <code>INSERT</code>, <code>UPDATE</code> or
233  <code>DELETE</code> statement or an SQL statement that returns nothing
234  @return either the row count for <code>INSERT</code>, <code>UPDATE</code>
235  or <code>DELETE</code> statements, or 0 for SQL statements that return nothing
236 }
237 function TZSQLiteStatement.ExecuteUpdate(const SQL: RawByteString): Integer;
238 var
239  ErrorCode: Integer;
240  ErrorMessage: PAnsichar;
241 begin
242  ASQL := SQL; //preprepares SQL
243  ErrorCode := FPlainDriver.Execute(FHandle, PAnsiChar(ASQL), nil, nil,ErrorMessage);
244  CheckSQLiteError(FPlainDriver, FHandle, ErrorCode, ErrorMessage, lcExecute, SSQL);
245  DriverManager.LogMessage(lcExecute, FPlainDriver.GetProtocol, SSQL);
246  Result := FPlainDriver.Changes(FHandle);
247  LastUpdateCount := Result;
248 end;
249 
250 {**
251  Executes an SQL statement that may return multiple results.
252  Under some (uncommon) situations a single SQL statement may return
253  multiple result sets and/or update counts. Normally you can ignore
254  this unless you are (1) executing a stored procedure that you know may
255  return multiple results or (2) you are dynamically executing an
256  unknown SQL string. The methods <code>execute</code>,
257  <code>getMoreResults</code>, <code>getResultSet</code>,
258  and <code>getUpdateCount</code> let you navigate through multiple results.
259 
260  The <code>execute</code> method executes an SQL statement and indicates the
261  form of the first result. You can then use the methods
262  <code>getResultSet</code> or <code>getUpdateCount</code>
263  to retrieve the result, and <code>getMoreResults</code> to
264  move to any subsequent result(s).
265 
266  @param sql any SQL statement
267  @return <code>true</code> if the next result is a <code>ResultSet</code> object;
268  <code>false</code> if it is an update count or there are no more results
269 }
270 function TZSQLiteStatement.Execute(const SQL: RawByteString): Boolean;
271 var
272  ErrorCode: Integer;
273  StmtHandle: Psqlite_vm;
274  ColumnCount: Integer;
275  ColumnValues: PPAnsiChar;
276  ColumnNames: PPAnsiChar;
277 begin
278  ColumnCount := 0;
279  ColumnValues:=nil;
280  ColumnNames:=nil;
281  ASQL := SQL; //preprapares SQL
282  ErrorCode := FPlainDriver.Prepare(FHandle, PAnsiChar(ASQL), Length(ASQL),
283  StmtHandle, nil);
284  CheckSQLiteError(FPlainDriver, FHandle, ErrorCode, nil, lcExecute, SSQL);
285  DriverManager.LogMessage(lcExecute, FPlainDriver.GetProtocol, SSQL);
286 
287  try
288  ErrorCode := FPlainDriver.Step(StmtHandle, ColumnCount,
289  ColumnValues, ColumnNames);
290  CheckSQLiteError(FPlainDriver, FHandle, ErrorCode, nil, lcOther, 'FETCH');
291  except
292  FPlainDriver.Finalize(StmtHandle);
293  raise;
294  end;
295 
296  { Process queries with result sets }
297  if ColumnCount <> 0 then
298  begin
299  Result := True;
300  LastResultSet := CreateResultSet(SSQL, StmtHandle, ColumnCount, ColumnNames,
301  ColumnValues);
302  end
303  { Processes regular query. }
304  else
305  begin
306  if assigned(ColumnValues) then
307  Freemem(ColumnValues);
308  if assigned(ColumnNames) then
309  Freemem(ColumnNames);
310  Result := False;
311  LastUpdateCount := FPlainDriver.Changes(FHandle);
312  ErrorCode := FPlainDriver.Finalize(StmtHandle);
313  CheckSQLiteError(FPlainDriver, FHandle, ErrorCode, nil, lcOther,
314  'Finalize SQLite VM');
315  end;
316 end;
317 
318 {$IFDEF ZEOS_TEST_ONLY}
319 { TZSQLitePreparedStatement }
320 
321 {**
322  Constructs this object and assignes the main properties.
323  @param PlainDriver a native SQLite Plain driver.
324  @param Connection a database connection object.
325  @param Info a statement parameters.
326  @param Handle a connection handle pointer.
327 }
328 constructor TZSQLitePreparedStatement.Create(PlainDriver: IZSQLitePlainDriver;
329  Connection: IZConnection; const SQL: string; Info: TStrings; Handle: Psqlite);
330 begin
331  inherited Create(Connection, SQL, Info);
332  FHandle := Handle;
333  FPlainDriver := PlainDriver;
334  ResultSetType := rtForwardOnly;
335  Prepare;
336 end;
337 
338 {**
339  Creates a temporary statement which executes queries.
340  @param Info a statement parameters.
341  @return a created statement object.
342 }
343 function TZSQLitePreparedStatement.CreateExecStatement: IZStatement;
344 begin
345  Result := TZSQLiteStatement.Create(FPlainDriver, Connection, Info,FHandle);
346 end;
347 
348 {**
349  Prepares an SQL parameter for the query.
350  @param ParameterIndex the first parameter is 1, the second is 2, ...
351  @return a string representation of the parameter.
352 }
353 function TZSQLitePreparedStatement.PrepareAnsiSQLParam(ParamIndex: Integer): RawByteString;
354 var
355  Value: TZVariant;
356  TempBlob: IZBlob;
357  TempBytes: TByteDynArray;
358 begin
359  if InParamCount <= ParamIndex then
360  raise EZSQLException.Create(SInvalidInputParameterCount);
361 
362  Value := InParamValues[ParamIndex];
363  if DefVarManager.IsNull(Value) then
364  Result := 'NULL'
365  else
366  begin
367  case InParamTypes[ParamIndex] of
368  stBoolean:
369  if SoftVarManager.GetAsBoolean(Value) then
370  Result := '''Y'''
371  else
372  Result := '''N''';
373  stByte, stShort, stInteger, stLong, stBigDecimal, stFloat, stDouble:
374  Result := RawByteString(SoftVarManager.GetAsString(Value));
375  stBytes:
376  begin
377  TempBytes := SoftVarManager.GetAsBytes(Value);
378  Result := EncodeString(@TempBytes, Length(TempBytes));
379  end;
380  stString:
381  Result := ZPlainString(AnsiQuotedStr(SoftVarManager.GetAsString(Value), #39));
382  stUnicodeString:
383  {$IFDEF UNICODE}
384  Result := ZPlainString(AnsiQuotedStr(SoftVarManager.GetAsUnicodeString(Value), #39));
385  {$ELSE}
386  Result := AnsiQuotedStr(ZPlainString(SoftVarManager.GetAsUnicodeString(Value)), #39);
387  {$ENDIF}
388  stDate:
389  Result := '''' + RawByteString(FormatDateTime('yyyy-mm-dd',
390  SoftVarManager.GetAsDateTime(Value))) + '''';
391  stTime:
392  Result := '''' + RawByteString(FormatDateTime('hh:mm:ss',
393  SoftVarManager.GetAsDateTime(Value))) + '''';
394  stTimestamp:
395  Result := '''' + RawByteString(FormatDateTime('yyyy-mm-dd hh:mm:ss',
396  SoftVarManager.GetAsDateTime(Value))) + '''';
397  stAsciiStream, stUnicodeStream, stBinaryStream:
398  begin
399  TempBlob := DefVarManager.GetAsInterface(Value) as IZBlob;
400  if not TempBlob.IsEmpty then
401  if InParamTypes[ParamIndex] = stBinaryStream then
402  Result := EncodeString(TempBlob.GetBuffer, TempBlob.Length)
403  else
404  Result := {$IFDEF WITH_UNITANSISTRINGS}AnsiStrings.{$ENDIF}AnsiQuotedStr(
405  GetValidatedAnsiStringFromBuffer(TempBlob.GetBuffer,
406  TempBlob.Length, TempBlob.WasDecoded, ConSettings), #39)
407  else
408  Result := 'NULL';
409  end;
410  end;
411  end;
412 end;
413 {$ENDIF}
414 
415 
416 procedure BindingDestructor(Value: PAnsiChar); cdecl;
417 begin
418  {$IFDEF WITH_STRDISPOSE_DEPRECATED}AnsiStrings.{$ENDIF}StrDispose(Value);
419 end;
420 
421 { TZSQLiteCAPIPreparedStatement }
422 
423 function TZSQLiteCAPIPreparedStatement.CreateResultSet(const SQL: string;
424  StmtHandle: Psqlite_vm; ColumnCount: Integer; ColumnNames: PPAnsiChar;
425  ColumnValues: PPAnsiChar): IZResultSet;
426 var
427  CachedResolver: TZSQLiteCachedResolver;
428  NativeResultSet: TZSQLiteResultSet;
429  CachedResultSet: TZCachedResultSet;
430 begin
431  { Creates a native result set. }
432  Self.SSQL := SQL;
433  NativeResultSet := TZSQLiteResultSet.Create(FPlainDriver, Self, SSQL, FHandle,
434  StmtHandle, ColumnCount, ColumnNames, ColumnValues, False);
435  NativeResultSet.SetConcurrency(rcReadOnly);
436 
437  { Creates a cached result set. }
438  CachedResolver := TZSQLiteCachedResolver.Create(FPlainDriver, FHandle, Self,
439  NativeResultSet.GetMetaData);
440  CachedResultSet := TZCachedResultSet.Create(NativeResultSet, SSQL,
441  CachedResolver,GetConnection.GetConSettings);
442 
443  { Fetches all rows to prevent blocking. }
444  CachedResultSet.SetType(rtScrollInsensitive);
445  CachedResultSet.Last;
446  CachedResultSet.BeforeFirst;
447  CachedResultSet.SetConcurrency(GetResultSetConcurrency);
448 
449  Result := CachedResultSet;
450  FOpenResultSet := Pointer(Result);
451 end;
452 
453 procedure TZSQLiteCAPIPreparedStatement.SetASQL(const Value: RawByteString);
454 begin
455  if ( ASQL <> Value ) and Prepared then
456  Unprepare;
457  inherited SetASQL(Value);
458 end;
459 
460 procedure TZSQLiteCAPIPreparedStatement.SetWSQL(const Value: ZWideString);
461 begin
462  if ( WSQL <> Value ) and Prepared then
463  Unprepare;
464  inherited SetWSQL(Value);
465 end;
466 
467 procedure TZSQLiteCAPIPreparedStatement.FreeReference;
468 begin
469  FOpenResultSet := nil;
470 end;
471 
472 procedure TZSQLiteCAPIPreparedStatement.PrepareInParameters;
473 begin
474  if FPlainDriver.bind_parameter_count(FStmtHandle) <> InParamCount then
475  raise Exception.Create('Invalid InParamCount');
476 end;
477 
478 procedure TZSQLiteCAPIPreparedStatement.BindInParameters;
479 var
480  Value: TZVariant;
481  TempBlob: IZBlob;
482  I, L: Integer;
483  TempAnsi: RawByteString;
484  Bts: TByteDynArray;
485 
486  Function AsPAnsiChar(Const S : RawByteString; Len: Integer) : PAnsiChar;
487  begin
488  Result := {$IFDEF UNICODE}AnsiStrAlloc{$ELSE}StrAlloc{$ENDIF}(Len);
489  System.Move(PAnsiChar(S)^, Result^, Len);
490  end;
491 
492 begin
493  FErrorcode := FPlainDriver.clear_bindings(FStmtHandle);
494  CheckSQLiteError(FPlainDriver, FStmtHandle, FErrorCode, nil, lcBindPrepStmt, SSQL);
495  for i := 1 to InParamCount do
496  begin
497  Value := InParamValues[i-1];
498  if DefVarManager.IsNull(Value) then
499  FErrorcode := FPlainDriver.bind_null(FStmtHandle, I)
500  else
501  begin
502  case InParamTypes[I-1] of
503  stBoolean:
504  if SoftVarManager.GetAsBoolean(Value) then
505  FErrorcode := FPlainDriver.bind_text(FStmtHandle, i,
506  {$IFDEF WITH_STRNEW_DEPRECATED}AnsiStrings.{$ENDIF}StrNew(PAnsiChar(AnsiString('Y'))), 1, @BindingDestructor)
507  else
508  FErrorcode := FPlainDriver.bind_text(FStmtHandle, i,
509  {$IFDEF WITH_STRNEW_DEPRECATED}AnsiStrings.{$ENDIF}StrNew(PAnsichar(AnsiString('N'))), 1, @BindingDestructor);
510  stByte, stShort, stInteger:
511  FErrorcode := FPlainDriver.bind_int(FStmtHandle, i,
512  SoftVarManager.GetAsInteger(Value));
513  stLong:
514  FErrorcode := FPlainDriver.bind_int64(FStmtHandle, i,
515  SoftVarManager.GetAsInteger(Value));
516  stBigDecimal, stFloat, stDouble:
517  FErrorcode := FPlainDriver.bind_double(FStmtHandle, i,
518  SoftVarManager.GetAsFloat(Value));
519  stBytes:
520  begin
521  Bts := SoftVarManager.GetAsBytes(Value);
522  L := Length(Bts);
523  ZSetString(PAnsiChar(Bts), L, TempAnsi);
524  FErrorcode := FPlainDriver.bind_blob(FStmtHandle, i,
525  AsPAnsiChar(TempAnsi, L), L, @BindingDestructor)
526  end;
527  stString:
528  {$IFDEF FPC} //FPC StrNew fails for '' strings and returns nil
529  begin
530  TempAnsi := ZPlainString(SoftVarManager.GetAsString(Value));
531  if TempAnsi = '' then
532  FErrorcode := FPlainDriver.bind_text(FStmtHandle, i,
533  AsPAnsiChar(TempAnsi, 1), 0, @BindingDestructor)
534  else
535  FErrorcode := FPlainDriver.bind_text(FStmtHandle, i,
536  StrNew(PAnsichar(TempAnsi)), -1, @BindingDestructor);
537  end;
538  {$ELSE}
539  FErrorcode := FPlainDriver.bind_text(FStmtHandle, i,
540  {$IFDEF WITH_STRNEW_DEPRECATED}AnsiStrings.{$ENDIF}StrNew(PAnsichar(ZPlainString(SoftVarManager.GetAsString(Value)))),
541  -1, @BindingDestructor);
542  {$ENDIF}
543  stUnicodeString:
544  {$IFDEF FPC} //FPC StrNew fails for '' strings and returns nil
545  begin
546  TempAnsi := ZPlainString(SoftVarManager.GetAsUnicodeString(Value));
547  if TempAnsi = '' then
548  FErrorcode := FPlainDriver.bind_text(FStmtHandle, i,
549  AsPAnsiChar(TempAnsi, 1), 0, @BindingDestructor)
550  else
551  FErrorcode := FPlainDriver.bind_text(FStmtHandle, i,
552  StrNew(PAnsichar(TempAnsi)), -1, @BindingDestructor);
553  end;
554  {$ELSE}
555  FErrorcode := FPlainDriver.bind_text(FStmtHandle, i,
556  {$IFDEF WITH_STRNEW_DEPRECATED}AnsiStrings.{$ENDIF}StrNew(PAnsichar(ZPlainString(SoftVarManager.GetAsUnicodeString(Value)))),
557  -1, @BindingDestructor);
558  {$ENDIF}
559  stDate:
560  FErrorcode := FPlainDriver.bind_text(FStmtHandle, i,
561  {$IFDEF WITH_STRNEW_DEPRECATED}AnsiStrings.{$ENDIF}StrNew(PAnsichar(RawByteString(FormatDateTime('yyyy-mm-dd',
562  SoftVarManager.GetAsDateTime(Value))))),
563  10, @BindingDestructor);
564  stTime:
565  FErrorcode := FPlainDriver.bind_text(FStmtHandle, i,
566  {$IFDEF WITH_STRNEW_DEPRECATED}AnsiStrings.{$ENDIF}StrNew(PAnsichar(RawByteString(FormatDateTime('hh:mm:ss',
567  SoftVarManager.GetAsDateTime(Value))))),
568  8, @BindingDestructor);
569  stTimestamp:
570  FErrorcode := FPlainDriver.bind_text(FStmtHandle, i,
571  {$IFDEF WITH_STRNEW_DEPRECATED}AnsiStrings.{$ENDIF}StrNew(PAnsichar(RawByteString(FormatDateTime('yyyy-mm-dd hh:mm:ss',
572  SoftVarManager.GetAsDateTime(Value))))),
573  19, @BindingDestructor);
574  { works equal but selects from data which was written in string format
575  won't match! e.G. TestQuery etc. On the other hand-> i've prepared
576  this case on the resultsets too. JULIAN_DAY_PRECISION?}
577  {stDate, stTime, stTimestamp:
578  FErrorcode := FPlainDriver.bind_double(FStmtHandle, i,
579  SoftVarManager.GetAsDateTime(Value));}
580  stAsciiStream, stUnicodeStream, stBinaryStream:
581  begin
582  TempBlob := DefVarManager.GetAsInterface(Value) as IZBlob;
583  if not TempBlob.IsEmpty then
584  if InParamTypes[I-1] = stBinaryStream then
585  begin
586  TempAnsi := TempBlob.GetString;
587  FErrorcode := FPlainDriver.bind_blob(FStmtHandle, i,
588  AsPAnsiChar(TempAnsi, TempBlob.Length), TempBlob.Length,
589  @BindingDestructor)
590  end
591  else
592  begin
593  TempAnsi := GetValidatedAnsiStringFromBuffer(TempBlob.GetBuffer,
594  TempBlob.Length, TempBlob.WasDecoded, ConSettings);
595  FErrorcode := FPlainDriver.bind_text(FStmtHandle, i,
596  {$IFDEF WITH_STRNEW_DEPRECATED}AnsiStrings.{$ENDIF}StrNew(PAnsiChar(TempAnsi)),
597  Length(TempAnsi), @BindingDestructor);
598  end
599  else
600  FErrorcode := FPlainDriver.bind_null(FStmtHandle, I);
601  end;
602  end;
603  end;
604  CheckSQLiteError(FPlainDriver, FStmtHandle, FErrorCode, nil, lcBindPrepStmt, SSQL);
605  end;
606 end;
607 
608 constructor TZSQLiteCAPIPreparedStatement.Create(PlainDriver: IZSQLitePlainDriver;
609  Connection: IZConnection; const SQL: string; Info: TStrings; Handle: Psqlite);
610 begin
611  inherited Create(Connection, SQL, Info);
612  FHandle := Handle;
613  FPlainDriver := PlainDriver;
614  ResultSetType := rtForwardOnly;
615 end;
616 
617 procedure TZSQLiteCAPIPreparedStatement.Prepare;
618 begin
619  FErrorCode := FPlainDriver.Prepare_v2(FHandle, PAnsiChar(ASQL), Length(ASQL), FStmtHandle, nil);
620  CheckSQLiteError(FPlainDriver, FHandle, FErrorCode, nil, lcPrepStmt, SSQL);
621  inherited Prepare;
622 end;
623 
624 procedure TZSQLiteCAPIPreparedStatement.Unprepare;
625 begin
626  if Assigned(FStmtHandle) then
627  FErrorCode := FPlainDriver.Finalize(FStmtHandle)
628  else
629  FErrorCode := SQLITE_OK;
630  FStmtHandle := nil;
631  CheckSQLiteError(FPlainDriver, FStmtHandle, FErrorCode, nil,
632  lcUnprepStmt, 'Unprepare SQLite Statement');
633  inherited UnPrepare;
634 end;
635 
636 function TZSQLiteCAPIPreparedStatement.ExecuteQueryPrepared: IZResultSet;
637 var
638  ColumnCount: Integer;
639  ColumnValues: PPAnsiChar;
640  ColumnNames: PPAnsiChar;
641 begin
642  if Not Prepared then
643  Prepare;
644  { after reading the last row we reset the statment. So we don't need this here }
645  ColumnValues := nil;
646  ColumnNames := nil;
647  ColumnCount := 0;
648  try
649  if FOpenResultSet <> nil then
650  IZResultSet(FOpenResultSet).Close; // reset stmt
651  FOpenResultSet := nil;
652 
653  BindInParameters;
654  FErrorCode := FPlainDriver.Step(FStmtHandle, ColumnCount,
655  ColumnValues, ColumnNames);
656  CheckSQLiteError(FPlainDriver, FStmtHandle, FErrorCode, nil, lcOther, SCanNotRetrieveResultsetData);
657  except
658  if ColumnValues <> nil then
659  FreeMem(ColumnValues);
660  ColumnValues := nil;
661  if ColumnNames <> nil then
662  FreeMem(ColumnNames);
663  ColumnNames := nil;
664  raise;
665  end;
666 
667  Result := CreateResultSet(SSQL, FStmtHandle, ColumnCount, ColumnNames,
668  ColumnValues);
669 end;
670 
671 function TZSQLiteCAPIPreparedStatement.ExecuteUpdatePrepared: Integer;
672 begin
673  if Not Prepared then
674  Prepare;
675  BindInParameters;
676 
677  Result := 0;
678  try
679  FErrorCode := FPlainDriver.Step(FStmtHandle);
680  CheckSQLiteError(FPlainDriver, FStmtHandle, FErrorCode, nil, lcExecPrepStmt, SSQL);
681  Result := FPlainDriver.Changes(FHandle);
682  finally
683  FErrorCode := FPlainDriver.reset(FStmtHandle); //no errorcheck!
684  LastUpdateCount := Result;
685  end;
686  { Autocommit statement. }
687  if Connection.GetAutoCommit then
688  Connection.Commit;
689 end;
690 
691 function TZSQLiteCAPIPreparedStatement.ExecutePrepared: Boolean;
692 var
693  ColumnCount: Integer;
694  ColumnValues: PPAnsiChar;
695  ColumnNames: PPAnsiChar;
696 begin
697  if Not Prepared then
698  Prepare;
699 
700  ColumnCount := 0;
701  ColumnValues:=nil;
702  ColumnNames:=nil;
703  try
704  BindInParameters;
705 
706  FErrorCode := FPlainDriver.Step(FStmtHandle, ColumnCount,
707  ColumnValues, ColumnNames);
708  CheckSQLiteError(FPlainDriver, FStmtHandle, FErrorCode, nil, lcExecPrepStmt, 'Step');
709  except
710  raise;
711  end;
712 
713  { Process queries with result sets }
714  if ColumnCount <> 0 then
715  begin
716  Result := True;
717  LastResultSet := CreateResultSet(SSQL, FStmtHandle, ColumnCount, ColumnNames,
718  ColumnValues);
719  end
720  { Processes regular query. }
721  else
722  begin
723  if assigned(ColumnValues) then
724  Freemem(ColumnValues);
725  if assigned(ColumnNames) then
726  Freemem(ColumnNames);
727  Result := False;
728  LastUpdateCount := FPlainDriver.Changes(FHandle);
729  FErrorCode := FPlainDriver.reset(FStmtHandle);
730  CheckSQLiteError(FPlainDriver, FStmtHandle, FErrorCode, nil, lcOther, 'Reset');
731  end;
732  { Autocommit statement. }
733  if not Result and Connection.GetAutoCommit then
734  Connection.Commit;
735 
736  inherited ExecutePrepared;
737 end;
738 
739 end.
740