1 {*********************************************************}
3 { Zeos Database Objects }
4 { ADO Statement Classes }
6 { Originally written by Janos Fegyverneki }
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 ZDbcAdoStatement;
59 Types, Classes, SysUtils, ZCompatibility, ZClasses, ZSysUtils, ZCollections,
60 ZDbcIntfs, ZPlainDriver, ZDbcStatement, ZDbcAdo, ZPlainAdoDriver, ZPlainAdo,
61 ZVariant, ZDbcAdoUtils;
64 {** Implements Generic ADO Statement. }
65 TZAdoStatement = class(TZAbstractStatement)
67 AdoRecordSet: ZPlainAdo.RecordSet;
69 function ExecuteQuery(const SQL: ZWideString): IZResultSet; override;
70 function ExecuteUpdate(const SQL: ZWideString): Integer; override;
71 function Execute(const SQL: ZWideString): Boolean; override;
73 function ExecuteQuery(const SQL: RawByteString): IZResultSet; override;
74 function ExecuteUpdate(const SQL: RawByteString): Integer; override;
75 function Execute(const SQL: RawByteString): Boolean; override;
77 function GetMoreResults: Boolean; override;
80 {** Implements Prepared ADO Statement. }
81 TZAdoPreparedStatement = class(TZAbstractPreparedStatement)
83 AdoRecordSet: ZPlainAdo.RecordSet;
84 FAdoCommand: ZPlainAdo.Command;
86 procedure PrepareInParameters; override;
87 procedure BindInParameters; override;
89 constructor Create(Connection: IZConnection; const SQL: string; Info: TStrings);
90 destructor Destroy; override;
92 function ExecuteQueryPrepared: IZResultSet; override;
93 function ExecuteUpdatePrepared: Integer; override;
94 function ExecutePrepared: Boolean; override;
96 function GetMoreResults: Boolean; override;
97 procedure Unprepare; override;
100 {** Implements Callable ADO Statement. }
101 TZAdoCallableStatement = class(TZAbstractCallableStatement)
103 AdoRecordSet: ZPlainAdo.RecordSet;
104 FAdoCommand: ZPlainAdo.Command;
105 FDirectionTypes: TDirectionTypes;
107 function GetOutParam(ParameterIndex: Integer): TZVariant; override;
108 procedure PrepareInParameters; override;
109 procedure BindInParameters; override;
111 constructor Create(Connection: IZConnection;
112 SQL: string; Info: TStrings);
113 function ExecuteQueryPrepared: IZResultSet; override;
114 function ExecuteUpdatePrepared: Integer; override;
115 function ExecutePrepared: Boolean; override;
117 procedure RegisterParamType(ParameterIndex: Integer; ParamType: Integer); override;
118 function GetMoreResults: Boolean; override;
119 procedure Unprepare; override;
129 {$IFDEF WITH_TOBJECTLIST_INLINE} System.Contnrs{$ELSE} Contnrs{$ENDIF},
130 {$IFNDEF UNICODE}ZEncoding,{$ENDIF}
131 ZDbcLogging, ZDbcCachedResultSet, ZDbcResultSet, ZDbcAdoResultSet,
132 ZDbcMetadata, ZDbcResultSetMetadata, ZDbcUtils;
134 function TZAdoStatement.ExecuteQuery(const SQL: ZWideString): IZResultSet;
137 function GetMoreResults(var RS: IZResultSet): Boolean;
139 if Assigned(AdoRecordSet) then
141 AdoRecordSet := AdoRecordSet.NextRecordset(RC);
142 RS := GetCurrentResultSet(AdoRecordSet, (Connection as IZAdoConnection), Self,
143 SQL, ConSettings, ResultSetConcurrency);
144 Result := Assigned(RS);
145 LastUpdateCount := RC;
147 else Result := False;
154 LastUpdateCount := -1;
155 if IsSelect(SSQL) then
157 AdoRecordSet := CoRecordSet.Create;
158 AdoRecordSet.MaxRecords := MaxRows;
159 AdoRecordSet.Open(SQL, (Connection as IZAdoConnection).GetAdoConnection,
160 adOpenStatic, adLockOptimistic, adAsyncFetch);
163 AdoRecordSet := (Connection as IZAdoConnection).GetAdoConnection.Execute(WSQL, RC, adExecuteNoRecords);
164 Result := GetCurrentResultSet(AdoRecordSet, (Connection as IZAdoConnection), Self,
165 SSQL, ConSettings, ResultSetConcurrency);
166 if not Assigned(Result) then
167 while (not GetMoreResults(Result)) and (LastUpdateCount > -1) do ;
170 function TZAdoStatement.ExecuteUpdate(const SQL: ZWideString): Integer;
174 LastResultSet := nil;
175 LastUpdateCount := -1;
180 (Connection as IZAdoConnection).GetAdoConnection.Execute(WSQL, RC, adExecuteNoRecords);
182 LastUpdateCount := Result;
183 DriverManager.LogMessage(lcExecute, Connection.GetIZPlainDriver.GetProtocol, LogSQL);
187 DriverManager.LogError(lcExecute, Connection.GetIZPlainDriver.GetProtocol, LogSQL, 0, E.Message);
193 function TZAdoStatement.Execute(const SQL: ZWideString): Boolean;
200 LastResultSet := nil;
201 LastUpdateCount := -1;
203 if IsSelect(SSQL) then
205 AdoRecordSet := CoRecordSet.Create;
206 AdoRecordSet.MaxRecords := MaxRows;
207 AdoRecordSet.Open(SQL, (Connection as IZAdoConnection).GetAdoConnection,
208 adOpenStatic, adLockOptimistic, adAsyncFetch);
211 AdoRecordSet := (Connection as IZAdoConnection).GetAdoConnection.Execute(WSQL, RC, adExecuteNoRecords);
212 LastResultSet := GetCurrentResultSet(AdoRecordSet, (Connection as IZAdoConnection), Self,
213 LogSQL, ConSettings, ResultSetConcurrency);
214 Result := Assigned(LastResultSet);
215 LastUpdateCount := RC;
216 DriverManager.LogMessage(lcExecute, Connection.GetIZPlainDriver.GetProtocol, LogSQL);
220 DriverManager.LogError(lcExecute, Connection.GetIZPlainDriver.GetProtocol, LogSQL, 0, E.Message);
226 function TZAdoStatement.ExecuteQuery(const SQL: RawByteString): IZResultSet;
230 Result := ExecuteQuery(WSQL);
233 function TZAdoStatement.ExecuteUpdate(const SQL: RawByteString): Integer;
237 Result := ExecuteUpdate(WSQL);
240 function TZAdoStatement.Execute(const SQL: RawByteString): Boolean;
244 Result := Execute(WSQL);
247 function TZAdoStatement.GetMoreResults: Boolean;
252 LastResultSet := nil;
253 LastUpdateCount := -1;
254 if Assigned(AdoRecordSet) then
256 AdoRecordSet := AdoRecordSet.NextRecordset(RC);
257 LastResultSet := GetCurrentResultSet(AdoRecordSet, (Connection as IZAdoConnection), Self,
258 SSQL, ConSettings, ResultSetConcurrency);
259 Result := Assigned(LastResultSet);
260 LastUpdateCount := RC;
264 { TZAdoPreparedStatement }
266 constructor TZAdoPreparedStatement.Create(Connection: IZConnection;
267 const SQL: string; Info: TStrings);
269 FAdoCommand := CoCommand.Create;
270 inherited Create(Connection, SQL, Info);
271 FAdoCommand.CommandText := WSQL;
272 FAdoCommand._Set_ActiveConnection((Connection as IZAdoConnection).GetAdoConnection);
275 destructor TZAdoPreparedStatement.Destroy;
282 procedure TZAdoPreparedStatement.PrepareInParameters;
284 if InParamCount > 0 then
285 RefreshParameters(FAdoCommand);
286 FAdoCommand.Prepared := True;
289 procedure TZAdoPreparedStatement.BindInParameters;
292 if InParamCount = 0 then
295 for i := 0 to InParamCount-1 do
296 if DefVarManager.IsNull(InParamValues[i]) then
297 if (InParamDefaultValues[i] <> '') and (UpperCase(InParamDefaultValues[i]) <> 'NULL') and
298 StrToBoolEx(DefineStatementParameter(Self, 'defaults', 'true')) then
300 DefVarManager.SetAsString(InParamValues[i], InParamDefaultValues[i]);
301 ADOSetInParam(FAdoCommand, (Connection as IZAdoConnection), InParamCount, I+1, InParamTypes[i], InParamValues[i], adParamInput)
304 ADOSetInParam(FAdoCommand, (Connection as IZAdoConnection), InParamCount, I+1, InParamTypes[i], NullVariant, adParamInput)
306 ADOSetInParam(FAdoCommand, (Connection as IZAdoConnection), InParamCount, I+1, InParamTypes[i], InParamValues[i], adParamInput);
310 Executes the SQL query in this <code>PreparedStatement</code> object
311 and returns the result set generated by the query.
313 @return a <code>ResultSet</code> object that contains the data produced by the
314 query; never <code>null</code>
316 function TZAdoPreparedStatement.ExecuteQueryPrepared: IZResultSet;
318 if not ExecutePrepared then
319 while (not GetMoreResults) and (LastUpdateCount > -1) do ;
320 Result := LastResultSet;
324 Executes the SQL INSERT, UPDATE or DELETE statement
325 in this <code>PreparedStatement</code> object.
327 SQL statements that return nothing, such as SQL DDL statements,
330 @return either the row count for INSERT, UPDATE or DELETE statements;
331 or 0 for SQL statements that return nothing
333 function TZAdoPreparedStatement.ExecuteUpdatePrepared: Integer;
336 Result := LastUpdateCount;
340 Executes any kind of SQL statement.
341 Some prepared statements return multiple results; the <code>execute</code>
342 method handles these complex statements as well as the simpler
343 form of statements handled by the methods <code>executeQuery</code>
344 and <code>executeUpdate</code>.
345 @see Statement#execute
347 function TZAdoPreparedStatement.ExecutePrepared: Boolean;
351 LastResultSet := nil;
352 LastUpdateCount := -1;
357 if IsSelect(SQL) then
359 AdoRecordSet := CoRecordSet.Create;
360 AdoRecordSet.MaxRecords := MaxRows;
361 AdoRecordSet._Set_ActiveConnection(FAdoCommand.Get_ActiveConnection);
362 AdoRecordSet.Open(FAdoCommand, EmptyParam, adOpenForwardOnly, adLockOptimistic, adAsyncFetch);
365 AdoRecordSet := FAdoCommand.Execute(RC, EmptyParam, -1{, adExecuteNoRecords});
366 LastResultSet := GetCurrentResultSet(AdoRecordSet, (Connection as IZAdoConnection), Self,
367 SQL, ConSettings, ResultSetConcurrency);
368 LastUpdateCount := RC;
369 Result := Assigned(LastResultSet);
370 DriverManager.LogMessage(lcExecute, Connection.GetIZPlainDriver.GetProtocol, SQL);
374 DriverManager.LogError(lcExecute, Connection.GetIZPlainDriver.GetProtocol, SQL, 0, E.Message);
380 function TZAdoPreparedStatement.GetMoreResults: Boolean;
385 LastResultSet := nil;
386 LastUpdateCount := -1;
387 if Assigned(AdoRecordSet) then
389 AdoRecordSet := AdoRecordSet.NextRecordset(RC);
390 LastResultSet := GetCurrentResultSet(AdoRecordSet, (Connection as IZAdoConnection), Self,
391 SQL, ConSettings, ResultSetConcurrency);
392 Result := Assigned(LastResultSet);
393 LastUpdateCount := RC;
397 procedure TZAdoPreparedStatement.Unprepare;
399 if FAdoCommand.Prepared then
400 FAdoCommand.Prepared := False;
404 { TZAdoCallableStatement }
406 constructor TZAdoCallableStatement.Create(Connection: IZConnection;
407 SQL: string; Info: TStrings);
409 inherited Create(Connection, SQL, Info);
410 FAdoCommand := CoCommand.Create;
411 FAdoCommand.CommandText := WSQL;
412 FAdoCommand._Set_ActiveConnection((Connection as IZAdoConnection).GetAdoConnection);
413 FAdoCommand.CommandType := adCmdStoredProc;
416 function TZAdoCallableStatement.ExecuteQueryPrepared: IZResultSet;
419 ColumnInfo: TZColumnInfo;
420 ColumnsInfo: TObjectList;
421 RS: TZVirtualResultSet;
422 IndexAlign: TIntegerDynArray;
427 SetLength(IndexAlign, 0);
428 ColumnsInfo := TObjectList.Create(True);
431 for I := 0 to FAdoCommand.Parameters.Count -1 do
432 if FAdoCommand.Parameters.Item[i].Direction in [adParamOutput,
433 adParamInputOutput, adParamReturnValue] then
435 SetLength(IndexAlign, Length(IndexAlign)+1);
436 ColumnInfo := TZColumnInfo.Create;
439 ColumnLabel := FAdoCommand.Parameters.Item[i].Name;
440 ColumnType := ConvertAdoToSqlType(FAdoCommand.Parameters.Item[I].Type_, ConSettings.CPType);
441 ColumnDisplaySize := FAdoCommand.Parameters.Item[I].Precision;
442 Precision := FAdoCommand.Parameters.Item[I].Precision;
443 IndexAlign[High(IndexAlign)] := I;
445 ColumnsInfo.Add(ColumnInfo);
448 RS := TZVirtualResultSet.CreateWithColumns(ColumnsInfo, '', ConSettings);
451 SetType(rtScrollInsensitive);
452 SetConcurrency(rcReadOnly);
454 for i := 1 to ColumnsInfo.Count do
455 case TZColumnInfo(ColumnsInfo[i-1]).ColumnType of
457 RS.UpdateBoolean(i, FAdoCommand.Parameters.Item[IndexAlign[i-1]].Value);
458 stByte, stShort, stInteger, stLong:
459 RS.UpdateInt(i, FAdoCommand.Parameters.Item[IndexAlign[i-1]].Value);
460 stFloat, stDouble, stBigDecimal:
461 RS.UpdateFloat(i, FAdoCommand.Parameters.Item[IndexAlign[i-1]].Value);
463 RS.UpdateString(i, FAdoCommand.Parameters.Item[IndexAlign[i-1]].Value);
466 Stream := TStringStream.Create(AnsiString(FAdoCommand.Parameters.Item[IndexAlign[i-1]].Value));
467 RS.UpdateAsciiStream(I, Stream);
471 RS.UpdateUnicodeString(i, FAdoCommand.Parameters.Item[IndexAlign[i-1]].Value);
474 Stream := WideStringStream(WideString(FAdoCommand.Parameters.Item[IndexAlign[i-1]].Value));
475 RS.UpdateUnicodeStream(I, Stream);
479 RS.UpdateBytes(i, FAdoCommand.Parameters.Item[IndexAlign[i-1]].Value);
481 RS.UpdateDate(i, FAdoCommand.Parameters.Item[IndexAlign[i-1]].Value);
483 RS.UpdateTime(i, FAdoCommand.Parameters.Item[IndexAlign[i-1]].Value);
485 RS.UpdateTimestamp(i, FAdoCommand.Parameters.Item[IndexAlign[i-1]].Value);
488 if VarIsStr(FAdoCommand.Parameters.Item[IndexAlign[i-1]].Value) then
490 Stream := TStringStream.Create(AnsiString(FAdoCommand.Parameters.Item[IndexAlign[i-1]].Value));
491 RS.UpdateBinaryStream(I, Stream);
495 if VarIsArray(FAdoCommand.Parameters.Item[IndexAlign[i-1]].Value) then
497 P := VarArrayLock(FAdoCommand.Parameters.Item[IndexAlign[i-1]].Value);
499 Stream := TMemoryStream.Create;
500 Stream.Size := VarArrayHighBound(FAdoCommand.Parameters.Item[IndexAlign[i-1]].Value, 1)+1;
501 System.Move(P^, TMemoryStream(Stream).Memory^, Stream.Size);
502 RS.UpdateBinaryStream(I, Stream);
505 VarArrayUnLock(FAdoCommand.Parameters.Item[IndexAlign[i-1]].Value);
517 if Assigned(Stream) then Stream.Free;
523 Executes the SQL INSERT, UPDATE or DELETE statement
524 in this <code>PreparedStatement</code> object.
526 SQL statements that return nothing, such as SQL DDL statements,
529 @return either the row count for INSERT, UPDATE or DELETE statements;
530 or 0 for SQL statements that return nothing
532 function TZAdoCallableStatement.ExecuteUpdatePrepared: Integer;
535 Result := LastUpdateCount;
539 Executes any kind of SQL statement.
540 Some prepared statements return multiple results; the <code>execute</code>
541 method handles these complex statements as well as the simpler
542 form of statements handled by the methods <code>executeQuery</code>
543 and <code>executeUpdate</code>.
544 @see Statement#execute
546 function TZAdoCallableStatement.ExecutePrepared: Boolean;
550 LastResultSet := nil;
551 LastUpdateCount := -1;
552 if Not Prepared then Prepare;
556 if IsSelect(SQL) then
558 AdoRecordSet := CoRecordSet.Create;
559 AdoRecordSet.MaxRecords := MaxRows;
560 AdoRecordSet._Set_ActiveConnection(FAdoCommand.Get_ActiveConnection);
561 AdoRecordSet.Open(FAdoCommand, EmptyParam, adOpenForwardOnly, adLockOptimistic, adAsyncFetch);
564 AdoRecordSet := FAdoCommand.Execute(RC, EmptyParam, -1{, adExecuteNoRecords});
565 LastResultSet := GetCurrentResultSet(AdoRecordSet, (Connection as IZAdoConnection), Self,
566 SQL, ConSettings, ResultSetConcurrency);
567 LastUpdateCount := RC;
568 Result := Assigned(LastResultSet);
569 DriverManager.LogMessage(lcExecute, Connection.GetIZPlainDriver.GetProtocol, SQL);
573 DriverManager.LogError(lcExecute, Connection.GetIZPlainDriver.GetProtocol, SQL, 0, E.Message);
579 procedure TZAdoCallableStatement.RegisterParamType(ParameterIndex: Integer;
582 inherited RegisterParamType(ParameterIndex, ParamType);
583 if Length(FDirectionTypes) < ParameterIndex then
584 SetLength(FDirectionTypes, ParameterIndex);
586 case Self.FDBParamTypes[ParameterIndex-1] of
588 FDirectionTypes[ParameterIndex-1] := adParamInput;
590 FDirectionTypes[ParameterIndex-1] := adParamOutput;
592 FDirectionTypes[ParameterIndex-1] := adParamInputOutput;
594 FDirectionTypes[ParameterIndex-1] := adParamReturnValue;
597 FDirectionTypes[ParameterIndex-1] := adParamUnknown;
601 function TZAdoCallableStatement.GetMoreResults: Boolean;
606 LastResultSet := nil;
607 LastUpdateCount := -1;
608 if Assigned(AdoRecordSet) then
610 AdoRecordSet := AdoRecordSet.NextRecordset(RC);
611 LastResultSet := GetCurrentResultSet(AdoRecordSet, (Connection as IZAdoConnection), Self,
612 SQL, ConSettings, ResultSetConcurrency);
613 Result := Assigned(LastResultSet);
614 LastUpdateCount := RC;
618 procedure TZAdoCallableStatement.Unprepare;
620 if FAdoCommand.Prepared then
621 FAdoCommand.Prepared := False;
625 function TZAdoCallableStatement.GetOutParam(ParameterIndex: Integer): TZVariant;
633 if ParameterIndex > OutParamCount then
634 Result := NullVariant
637 Temp := FAdoCommand.Parameters.Item[ParameterIndex - 1].Value;
639 case ConvertAdoToSqlType(FAdoCommand.Parameters.Item[ParameterIndex - 1].Type_,
640 ConSettings.CPType) of
642 DefVarManager.SetAsBoolean(Result, Temp);
643 stByte, stShort, stInteger, stLong:
644 DefVarManager.SetAsInteger(Result, Temp);
645 stFloat, stDouble, stBigDecimal:
646 DefVarManager.SetAsFloat(Result, Temp);
647 stString, stAsciiStream:
648 DefVarManager.SetAsString(Result, Temp);
649 stUnicodeString, stUnicodeStream:
650 DefVarManager.SetAsUnicodeString(Result, Temp);
652 DefVarManager.SetAsBytes(Result, VarToBytes(Temp));
653 stDate, stTime, stTimestamp:
654 DefVarManager.SetAsDateTime(Result, Temp);
659 TempBlob := TZAbstractBlob.CreateWithStream(nil, GetConnection);
660 TempBlob.SetString(AnsiString(V));
663 if VarIsArray(V) then
665 P := VarArrayLock(V);
667 TempBlob := TZAbstractBlob.CreateWithData(P, VarArrayHighBound(V, 1)+1, GetConnection);
672 DefVarManager.SetAsInterface(Result, TempBlob);
676 DefVarManager.SetNull(Result);
680 LastWasNull := DefVarManager.IsNull(Result) or VarIsNull(Temp) or VarIsClear(Temp);
683 procedure TZAdoCallableStatement.PrepareInParameters;
685 if InParamCount > 0 then
686 RefreshParameters(FAdoCommand, @FDirectionTypes);
687 FAdoCommand.Prepared := True;
690 procedure TZAdoCallableStatement.BindInParameters;
694 if InParamCount = 0 then
697 for i := 0 to InParamCount-1 do
698 if FDBParamTypes[i] in [1,3] then //ptInput, ptInputOutput
699 if DefVarManager.IsNull(InParamValues[i]) then
700 if (InParamDefaultValues[i] <> '') and (UpperCase(InParamDefaultValues[i]) <> 'NULL') and
701 StrToBoolEx(DefineStatementParameter(Self, 'defaults', 'true')) then
703 DefVarManager.SetAsString(InParamValues[i], InParamDefaultValues[i]);
704 ADOSetInParam(FAdoCommand, (Connection as IZAdoConnection), InParamCount, I+1, InParamTypes[i], InParamValues[i], adParamInput)
707 ADOSetInParam(FAdoCommand, (Connection as IZAdoConnection), InParamCount, I+1, InParamTypes[i], NullVariant, FDirectionTypes[i])
709 ADOSetInParam(FAdoCommand, (Connection as IZAdoConnection), InParamCount, I+1, InParamTypes[i], InParamValues[i], FDirectionTypes[i])
711 ADOSetInParam(FAdoCommand, (Connection as IZAdoConnection), InParamCount, I+1, InParamTypes[i], NullVariant, FDirectionTypes[i]);