1 {*********************************************************}
3 { Zeos Database Objects }
4 { MySQL Database Connectivity Classes }
6 { Originally written by Sergey Seroukhov }
8 {*********************************************************}
10 {@********************************************************}
11 { Copyright (c) 1999-2012 Zeos Development Group }
13 { License Agreement: }
15 { This library is distributed in the hope that it will be }
16 { useful, but WITHOUT ANY WARRANTY; without even the }
17 { implied warranty of MERCHANTABILITY or FITNESS FOR }
18 { A PARTICULAR PURPOSE. See the GNU Lesser General }
19 { Public License for more details. }
21 { The source code of the ZEOS Libraries and packages are }
22 { distributed under the Library GNU General Public }
23 { License (see the file COPYING / COPYING.ZEOS) }
24 { with the following modification: }
25 { As a special exception, the copyright holders of this }
26 { library give you permission to link this library with }
27 { independent modules to produce an executable, }
28 { regardless of the license terms of these independent }
29 { modules, and to copy and distribute the resulting }
30 { executable under terms of your choice, provided that }
31 { you also meet, for each linked independent module, }
32 { the terms and conditions of the license of that module. }
33 { An independent module is a module which is not derived }
34 { from or based on this library. If you modify this }
35 { library, you may extend this exception to your version }
36 { of the library, but you are not obligated to do so. }
37 { If you do not wish to do so, delete this exception }
38 { statement from your version. }
41 { The project web site is located on: }
42 { http://zeos.firmos.at (FORUM) }
43 { http://sourceforge.net/p/zeoslib/tickets/ (BUGTRACKER)}
44 { svn://svn.code.sf.net/p/zeoslib/code-0/trunk (SVN) }
46 { http://www.sourceforge.net/projects/zeoslib. }
49 { Zeos Development Group. }
50 {********************************************************@}
52 unit ZDbcMySqlStatement;
59 Classes, {$IFDEF MSEgui}mclasses,{$ENDIF} SysUtils,
60 ZClasses, ZDbcIntfs, ZDbcStatement, ZDbcMySql, ZVariant, ZPlainMySqlDriver,
61 ZPlainMySqlConstants, ZCompatibility, ZDbcLogging;
65 {** Represents a MYSQL specific connection interface. }
66 IZMySQLStatement = interface (IZStatement)
67 ['{A05DB91F-1E40-46C7-BF2E-25D74978AC83}']
69 function IsUseResult: Boolean;
70 function IsPreparedStatement: Boolean;
71 function GetStmtHandle: PZMySqlPrepStmt;
74 {** Represents a MYSQL prepared Statement specific connection interface. }
75 IZMySQLPreparedStatement = interface (IZMySQLStatement)
76 ['{A05DB91F-1E40-46C7-BF2E-25D74978AC83}']
79 {** Implements Generic MySQL Statement. }
80 TZMySQLStatement = class(TZAbstractStatement, IZMySQLStatement)
82 FHandle: PZMySQLConnect;
83 FPlainDriver: IZMySQLPlainDriver;
86 function CreateResultSet(const SQL: string): IZResultSet;
87 function GetStmtHandle : PZMySqlPrepStmt;
89 constructor Create(PlainDriver: IZMySQLPlainDriver;
90 Connection: IZConnection; Info: TStrings; Handle: PZMySQLConnect);
92 function ExecuteQuery(const SQL: RawByteString): IZResultSet; override;
93 function ExecuteUpdate(const SQL: RawByteString): Integer; override;
94 function Execute(const SQL: RawByteString): Boolean; override;
96 function GetMoreResults: Boolean; override;
98 function IsUseResult: Boolean;
99 function IsPreparedStatement: Boolean;
102 {** Implements Prepared SQL Statement. }
103 TZMySQLEmulatedPreparedStatement = class(TZEmulatedPreparedStatement)
105 FHandle: PZMySQLConnect;
106 FPlainDriver: IZMySQLPlainDriver;
107 FUseDefaults: Boolean;
109 function CreateExecStatement: IZStatement; override;
110 function PrepareAnsiSQLParam(ParamIndex: Integer): RawByteString; override;
112 constructor Create(PlainDriver: IZMySQLPlainDriver;
113 Connection: IZConnection; const SQL: string; Info: TStrings;
114 Handle: PZMySQLConnect);
117 TZMysqlColumnBuffer = Array of PDOBindRecord2;
118 { TZMySQLBindBuffer }
119 {** Encapsulates a MySQL bind buffer. }
120 TZMySQLAbstractBindBuffer = class(TZAbstractObject)
122 FAddedColumnCount : Integer;
123 FBindOffsets: MYSQL_BINDOFFSETS;
124 FBindArray: Array of byte;
125 FPColumnArray: ^TZMysqlColumnBuffer;
127 constructor Create(PlainDriver:IZMysqlPlainDriver;
128 const BindCount : Integer; var ColumnArray:TZMysqlColumnBuffer);
129 function GetColumnArray : TZMysqlColumnBuffer;
130 function GetBufferAddress : Pointer;
131 function GetBufferType(ColumnIndex: Integer): TMysqlFieldTypes;
132 function GetBufferIsSigned(ColumnIndex: Integer): Boolean;
135 {** Encapsulates a MySQL bind buffer for ResultSets. }
136 TZMySQLResultSetBindBuffer = class(TZMySQLAbstractBindBuffer)
138 procedure AddColumn(PlainDriver: IZMysqlPlainDriver; const FieldHandle: PZMySQLField);
141 {** Encapsulates a MySQL bind buffer for updates. }
142 TZMySQLParamBindBuffer = class(TZMySQLAbstractBindBuffer)
144 procedure AddColumn(buffertype: TMysqlFieldTypes; const field_length: integer;
145 const largeblobparameter: boolean);
147 {** Implements Prepared SQL Statement. }
149 { TZMySQLPreparedStatement }
151 TZMySQLPreparedStatement = class(TZAbstractPreparedStatement,IZMySQLPreparedStatement)
153 FHandle: PZMySQLConnect;
154 FMySQLConnection: IZMySQLConnection;
155 FStmtHandle: PZMySqlPrepStmt;
156 FPlainDriver: IZMySQLPlainDriver;
158 FUseDefaults: Boolean;
160 FColumnArray: TZMysqlColumnBuffer;
161 FParamBindBuffer: TZMySQLParamBindBuffer;
163 function CreateResultSet(const SQL: string): IZResultSet;
165 function getFieldType (testVariant: TZVariant): TMysqlFieldTypes;
167 function GetStmtHandle : PZMySqlPrepStmt;
168 procedure BindInParameters; override;
169 procedure UnPrepareInParameters; override;
171 property StmtHandle: PZMySqlPrepStmt read GetStmtHandle;
172 constructor Create(PlainDriver: IZMysqlPlainDriver; Connection: IZConnection;
173 const SQL: string; Info: TStrings);
174 procedure Prepare; override;
175 destructor Destroy; override;
177 function ExecuteQueryPrepared: IZResultSet; override;
178 function ExecuteUpdatePrepared: Integer; override;
179 function ExecutePrepared: Boolean; override;
181 function IsUseResult: Boolean;
182 function IsPreparedStatement: Boolean;
185 {** Implements callable Postgresql Statement. }
186 TZMySQLCallableStatement = class(TZAbstractCallableStatement, IZMySQLStatement,
187 IZParamNamedCallableStatement)
189 FPlainDriver: IZMysqlPlainDriver;
190 FHandle: PZMySQLConnect;
191 FQueryHandle: PZMySQLResult;
193 FParamNames: array [0..1024] of String;
194 FParamTypeNames: array [0..1024] of String;
195 FUseDefaults: Boolean;
196 function GetCallSQL: RawByteString;
197 function GetOutParamSQL: String;
198 function GetSelectFunctionSQL: RawByteString;
199 function PrepareAnsiSQLParam(ParamIndex: Integer): RawByteString;
200 function GetStmtHandle : PZMySqlPrepStmt;
202 procedure ClearResultSets; override;
203 procedure BindInParameters; override;
204 function CreateResultSet(const SQL: string): IZResultSet;
205 procedure FetchOutParams(ResultSet: IZResultSet);
206 procedure RegisterParamTypeAndName(const ParameterIndex:integer;
207 const ParamTypeName, ParamName: String; Const ColumnSize, Precision: Integer);
209 constructor Create(PlainDriver: IZMySQLPlainDriver;
210 Connection: IZConnection; const SQL: string; Info: TStrings;
211 Handle: PZMySQLConnect);
213 function Execute(const SQL: RawByteString): Boolean; override;
214 function ExecuteQuery(const SQL: RawByteString): IZResultSet; override;
215 function ExecuteUpdate(const SQL: RawByteString): Integer; override;
217 function ExecuteQueryPrepared: IZResultSet; override;
218 function ExecuteUpdatePrepared: Integer; override;
220 function IsUseResult: Boolean;
221 function IsPreparedStatement: Boolean;
223 function HasMoreResultSets: Boolean; override;
224 function GetFirstResultSet: IZResultSet; override;
225 function GetPreviousResultSet: IZResultSet; override;
226 function GetNextResultSet: IZResultSet; override;
227 function GetLastResultSet: IZResultSet; override;
228 function BOR: Boolean; override;
229 function EOR: Boolean; override;
230 function GetResultSetByIndex(const Index: Integer): IZResultSet; override;
231 function GetResultSetCount: Integer; override;
237 Types, Math, DateUtils, ZDbcMySqlUtils, ZDbcMySqlResultSet, ZSysUtils,
238 ZDbcResultSetMetadata, ZMessages, ZDbcCachedResultSet, ZDbcUtils, ZEncoding,
239 ZDbcResultSet{$IFDEF WITH_UNITANSISTRINGS}, AnsiStrings{$ENDIF};
244 Constructs this object and assignes the main properties.
245 @param PlainDriver a native MySQL plain driver.
246 @param Connection a database connection object.
247 @param Handle a connection handle pointer.
248 @param Info a statement parameters.
250 constructor TZMySQLStatement.Create(PlainDriver: IZMySQLPlainDriver;
251 Connection: IZConnection; Info: TStrings; Handle: PZMySQLConnect);
253 inherited Create(Connection, Info);
255 FPlainDriver := PlainDriver;
256 ResultSetType := rtScrollInsensitive;
258 FUseResult := StrToBoolEx(DefineStatementParameter(Self, 'useresult', 'false'));
262 Checks is use result should be used in result sets.
263 @return <code>True</code> use result in result sets,
264 <code>False</code> store result in result sets.
266 function TZMySQLStatement.IsUseResult: Boolean;
268 Result := FUseResult;
272 Checks if this is a prepared mysql statement.
273 @return <code>False</code> This is not a prepared mysql statement.
275 function TZMySQLStatement.IsPreparedStatement: Boolean;
280 function TZMySQLStatement.GetStmtHandle: PZMySqlPrepStmt;
286 Creates a result set based on the current settings.
287 @return a created result set object.
289 function TZMySQLStatement.CreateResultSet(const SQL: string): IZResultSet;
291 CachedResolver: TZMySQLCachedResolver;
292 NativeResultSet: TZMySQLResultSet;
293 CachedResultSet: TZCachedResultSet;
295 NativeResultSet := TZMySQLResultSet.Create(FPlainDriver, Self, SQL, FHandle,
297 NativeResultSet.SetConcurrency(rcReadOnly);
298 if (GetResultSetConcurrency <> rcReadOnly) or (FUseResult
299 and (GetResultSetType <> rtForwardOnly)) then
301 CachedResolver := TZMySQLCachedResolver.Create(FPlainDriver, FHandle, Self,
302 NativeResultSet.GetMetaData);
303 CachedResultSet := TZCachedResultSet.Create(NativeResultSet, SQL,
304 CachedResolver, ConSettings);
305 CachedResultSet.SetConcurrency(GetResultSetConcurrency);
306 Result := CachedResultSet;
309 Result := NativeResultSet;
314 Executes an SQL statement that returns a single <code>ResultSet</code> object.
315 @param sql typically this is a static SQL <code>SELECT</code> statement
316 @return a <code>ResultSet</code> object that contains the data produced by the
317 given query; never <code>null</code>
319 function TZMySQLStatement.ExecuteQuery(const SQL: RawByteString): IZResultSet;
323 if FPlainDriver.ExecQuery(FHandle, PAnsiChar(ASQL)) = 0 then
325 DriverManager.LogMessage(lcExecute, FPlainDriver.GetProtocol, LogSQL);
326 if not FPlainDriver.ResultSetExists(FHandle) then
327 raise EZSQLException.Create(SCanNotOpenResultSet);
328 Result := CreateResultSet(LogSQL);
331 CheckMySQLError(FPlainDriver, FHandle, lcExecute, LogSQL);
335 Executes an SQL <code>INSERT</code>, <code>UPDATE</code> or
336 <code>DELETE</code> statement. In addition,
337 SQL statements that return nothing, such as SQL DDL statements,
340 @param sql an SQL <code>INSERT</code>, <code>UPDATE</code> or
341 <code>DELETE</code> statement or an SQL statement that returns nothing
342 @return either the row count for <code>INSERT</code>, <code>UPDATE</code>
343 or <code>DELETE</code> statements, or 0 for SQL statements that return nothing
345 function TZMySQLStatement.ExecuteUpdate(const SQL: RawByteString): Integer;
347 QueryHandle: PZMySQLResult;
348 HasResultset : Boolean;
352 if FPlainDriver.ExecQuery(FHandle, PAnsichar(ASQL)) = 0 then
354 DriverManager.LogMessage(lcExecute, FPlainDriver.GetProtocol, LogSQL);
355 HasResultSet := FPlainDriver.ResultSetExists(FHandle);
356 { Process queries with result sets }
359 QueryHandle := FPlainDriver.StoreResult(FHandle);
360 if QueryHandle <> nil then
362 Result := FPlainDriver.GetRowCount(QueryHandle);
363 FPlainDriver.FreeResult(QueryHandle);
366 Result := FPlainDriver.GetAffectedRows(FHandle);
367 while(FPlainDriver.RetrieveNextRowset(FHandle) = 0) do
369 QueryHandle := FPlainDriver.StoreResult(FHandle);
370 if QueryHandle <> nil then
372 FPlainDriver.FreeResult(QueryHandle);
376 { Process regular query }
378 Result := FPlainDriver.GetAffectedRows(FHandle);
381 CheckMySQLError(FPlainDriver, FHandle, lcExecute, LogSQL);
382 LastUpdateCount := Result;
386 Executes an SQL statement that may return multiple results.
387 Under some (uncommon) situations a single SQL statement may return
388 multiple result sets and/or update counts. Normally you can ignore
389 this unless you are (1) executing a stored procedure that you know may
390 return multiple results or (2) you are dynamically executing an
391 unknown SQL string. The methods <code>execute</code>,
392 <code>getMoreResults</code>, <code>getResultSet</code>,
393 and <code>getUpdateCount</code> let you navigate through multiple results.
395 The <code>execute</code> method executes an SQL statement and indicates the
396 form of the first result. You can then use the methods
397 <code>getResultSet</code> or <code>getUpdateCount</code>
398 to retrieve the result, and <code>getMoreResults</code> to
399 move to any subsequent result(s).
401 @param sql any SQL statement
402 @return <code>true</code> if the next result is a <code>ResultSet</code> object;
403 <code>false</code> if it is an update count or there are no more results
405 function TZMySQLStatement.Execute(const SQL: RawByteString): Boolean;
407 HasResultset : Boolean;
411 if FPlainDriver.ExecQuery(FHandle, PAnsiChar(ASQL)) = 0 then
413 DriverManager.LogMessage(lcExecute, FPlainDriver.GetProtocol, LogSQL);
414 HasResultSet := FPlainDriver.ResultSetExists(FHandle);
415 { Process queries with result sets }
419 LastResultSet := CreateResultSet(LogSQL);
421 { Processes regular query. }
425 LastUpdateCount := FPlainDriver.GetAffectedRows(FHandle);
429 CheckMySQLError(FPlainDriver, FHandle, lcExecute, LogSQL);
433 Moves to a <code>Statement</code> object's next result. It returns
434 <code>true</code> if this result is a <code>ResultSet</code> object.
435 This method also implicitly closes any current <code>ResultSet</code>
436 object obtained with the method <code>getResultSet</code>.
438 <P>There are no more results when the following is true:
440 <code>(!getMoreResults() && (getUpdateCount() == -1)</code>
443 @return <code>true</code> if the next result is a <code>ResultSet</code> object;
444 <code>false</code> if it is an update count or there are no more results
447 function TZMySQLStatement.GetMoreResults: Boolean;
451 Result := inherited GetMoreResults;
452 if FPlainDriver.GetClientVersion >= 40100 then
454 AStatus := FPlainDriver.RetrieveNextRowset(FHandle);
456 CheckMySQLError(FPlainDriver, FHandle, lcExecute, SSQL)
458 Result := (AStatus = 0);
460 if LastResultSet <> nil then
462 LastResultSet := nil;
463 LastUpdateCount := -1;
464 if FPlainDriver.ResultSetExists(FHandle) then
465 LastResultSet := CreateResultSet(SSQL)
467 LastUpdateCount := FPlainDriver.GetAffectedRows(FHandle);
471 { TZMySQLEmulatedPreparedStatement }
474 Constructs this object and assignes the main properties.
475 @param PlainDriver a native MySQL Plain driver.
476 @param Connection a database connection object.
477 @param Info a statement parameters.
478 @param Handle a connection handle pointer.
480 constructor TZMySQLEmulatedPreparedStatement.Create(PlainDriver: IZMySQLPlainDriver;
481 Connection: IZConnection; const SQL: string; Info: TStrings; Handle: PZMySQLConnect);
483 inherited Create(Connection, SQL, Info);
485 FPlainDriver := PlainDriver;
486 ResultSetType := rtScrollInsensitive;
487 FUseDefaults := StrToBoolEx(DefineStatementParameter(Self, 'defaults', 'true'));
491 Creates a temporary statement which executes queries.
492 @param Info a statement parameters.
493 @return a created statement object.
495 function TZMySQLEmulatedPreparedStatement.CreateExecStatement: IZStatement;
497 Result := TZMySQLStatement.Create(FPlainDriver, Connection, Info,FHandle);
501 Prepares an SQL parameter for the query.
502 @param ParameterIndex the first parameter is 1, the second is 2, ...
503 @return a string representation of the parameter.
505 function TZMySQLEmulatedPreparedStatement.PrepareAnsiSQLParam(ParamIndex: Integer): RawByteString;
508 TempBytes: TByteDynArray;
510 AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond: Word;
513 if InParamCount <= ParamIndex then
514 raise EZSQLException.Create(SInvalidInputParameterCount);
516 Value := InParamValues[ParamIndex];
517 if DefVarManager.IsNull(Value) then
518 if FUseDefaults and (InParamDefaultValues[ParamIndex] <> '') then
519 Result := ConSettings^.ConvFuncs.ZStringToRaw(InParamDefaultValues[ParamIndex],
520 ConSettings^.CTRL_CP, ConSettings^.ClientCodePage^.CP)
525 case InParamTypes[ParamIndex] of
527 if SoftVarManager.GetAsBoolean(Value) then
531 stByte, stShort, stInteger, stLong, stBigDecimal, stFloat, stDouble:
532 Result := RawByteString(SoftVarManager.GetAsString(Value));
535 TempBytes := SoftVarManager.GetAsBytes(Value);
536 Result := GetSQLHexAnsiString(PAnsiChar(TempBytes), Length(TempBytes));
539 Result := FPlainDriver.EscapeString(FHandle, ZPlainString(SoftVarManager.GetAsString(Value)), ConSettings, True);
541 Result := FPlainDriver.EscapeString(FHandle, ZPlainString(SoftVarManager.GetAsUnicodeString(Value)), ConSettings, True);
544 DecodeDateTime(SoftVarManager.GetAsDateTime(Value),
545 AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond);
546 Result := RawByteString('''' + Format('%0.4d-%0.2d-%0.2d',
547 [AYear, AMonth, ADay]) + '''');
551 DecodeDateTime(SoftVarManager.GetAsDateTime(Value),
552 AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond);
553 Result := RawByteString('''' + Format('%0.2d:%0.2d:%0.2d',
554 [AHour, AMinute, ASecond]) + '''');
558 DecodeDateTime(SoftVarManager.GetAsDateTime(Value),
559 AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond);
560 Result := RawByteString('''' + Format('%0.4d-%0.2d-%0.2d %0.2d:%0.2d:%0.2d',
561 [AYear, AMonth, ADay, AHour, AMinute, ASecond]) + '''');
563 stAsciiStream, stUnicodeStream, stBinaryStream:
565 TempBlob := DefVarManager.GetAsInterface(Value) as IZBlob;
566 if not TempBlob.IsEmpty then
568 case InParamTypes[ParamIndex] of
570 Result := GetSQLHexAnsiString(PAnsichar(TempBlob.GetBuffer), TempBlob.Length);
572 Result := FPlainDriver.EscapeString(FHandle,
573 GetValidatedAnsiStringFromBuffer(TempBlob.GetBuffer,
574 TempBlob.Length, TempBlob.WasDecoded, ConSettings),
585 { TZMySQLPreparedStatement }
588 Constructs this object and assignes the main properties.
589 @param PlainDriver a Oracle plain driver.
590 @param Connection a database connection object.
591 @param Info a statement parameters.
592 @param Handle a connection handle pointer.
594 constructor TZMySQLPreparedStatement.Create(
595 PlainDriver: IZMySQLPlainDriver; Connection: IZConnection;
596 const SQL: string; Info: TStrings);
598 inherited Create(Connection, SQL, Info);
599 FMySQLConnection := Connection as IZMySQLConnection;
600 FHandle := FMysqlConnection.GetConnectionHandle;
601 FPlainDriver := PlainDriver;
602 ResultSetType := rtScrollInsensitive;
604 FUseResult := StrToBoolEx(DefineStatementParameter(Self, 'useresult', 'false'));
605 FUseDefaults := StrToBoolEx(DefineStatementParameter(Self, 'defaults', 'true'));
609 Destroys this object and cleanups the memory.
611 destructor TZMySQLPreparedStatement.Destroy;
613 FStmtHandle := FPlainDriver.ClosePrepStmt(FStmtHandle);
617 procedure TZMySQLPreparedStatement.Prepare;
621 FStmtHandle := FPlainDriver.InitializePrepStmt(FHandle);
622 if (FStmtHandle = nil) then
624 CheckMySQLPrepStmtError(FPlainDriver, FStmtHandle, lcPrepStmt, SFailedtoInitPrepStmt);
627 if (FPlainDriver.PrepareStmt(FStmtHandle, PAnsiChar(ASQL), length(ASQL)) <> 0) then
629 CheckMySQLPrepStmtError(FPlainDriver, FStmtHandle, lcPrepStmt, SFailedtoPrepareStmt);
632 LogPrepStmtMessage(lcPrepStmt, SQL);
638 Checks is use result should be used in result sets.
639 @return <code>True</code> use result in result sets,
640 <code>False</code> store result in result sets.
642 function TZMySQLPreparedStatement.IsUseResult: Boolean;
644 Result := FUseResult;
648 Checks if this is a prepared mysql statement.
649 @return <code>True</code> This is a prepared mysql statement.
651 function TZMySQLPreparedStatement.IsPreparedStatement: Boolean;
657 Creates a result set based on the current settings.
658 @return a created result set object.
660 function TZMySQLPreparedStatement.CreateResultSet(const SQL: string): IZResultSet;
662 CachedResolver: TZMySQLCachedResolver;
663 NativeResultSet: TZMySQLPreparedResultSet;
664 CachedResultSet: TZCachedResultSet;
666 NativeResultSet := TZMySQLPreparedResultSet.Create(FPlainDriver, Self, SQL, FHandle,
668 NativeResultSet.SetConcurrency(rcReadOnly);
669 if (GetResultSetConcurrency <> rcReadOnly) or (FUseResult
670 and (GetResultSetType <> rtForwardOnly)) then
672 CachedResolver := TZMySQLCachedResolver.Create(FPlainDriver, FHandle, (Self as IZMysqlStatement),
673 NativeResultSet.GetMetaData);
674 CachedResultSet := TZCachedResultSet.Create(NativeResultSet, SQL,
675 CachedResolver, ConSettings);
676 CachedResultSet.SetConcurrency(GetResultSetConcurrency);
677 Result := CachedResultSet;
680 Result := NativeResultSet;
683 procedure TZMysqlPreparedStatement.BindInParameters;
686 year, month, day, hour, minute, second, millisecond: word;
687 MyType: TMysqlFieldTypes;
688 I, OffSet, PieceSize: integer;
690 TempAnsi: RawByteString;
692 //http://dev.mysql.com/doc/refman/5.0/en/storage-requirements.html
693 if InParamCount = 0 then
695 { Initialize Bind Array and Column Array }
696 FParamBindBuffer := TZMySqlParamBindBuffer.Create(FPlainDriver,InParamCount,FColumnArray);
698 For I := 0 to InParamCount - 1 do
700 if (InParamValues[I].VType = vtNull) and (InParamDefaultValues[I] <> '') and
701 StrToBoolEx(DefineStatementParameter(Self, 'defaults', 'true')) then
702 SoftVarManager.SetAsString(InParamValues[I], Copy(InParamDefaultValues[I], 2, Length(InParamDefaultValues[I])-2));
703 MyType := GetFieldType(InParamValues[I]);
707 TempAnsi := ZPlainString(InParamValues[I].VUnicodeString);
708 FParamBindBuffer.AddColumn(FIELD_TYPE_STRING, Length(TempAnsi),false);
712 TempAnsi := ZPlainString(InParamValues[I].VString);
713 FParamBindBuffer.AddColumn(FIELD_TYPE_STRING, Length(TempAnsi),false);
717 TempBlob := (InParamValues[I].VInterface as IZBlob);
718 if TempBlob.IsEmpty then
719 DefVarManager.SetNull(InParamValues[I])
721 if InParamTypes[I] = stBinaryStream then
722 FParamBindBuffer.AddColumn(FIELD_TYPE_BLOB, TempBlob.Length, TempBlob.Length > ChunkSize)
725 TempAnsi := GetValidatedAnsiStringFromBuffer(TempBlob.GetBuffer,
726 TempBlob.Length, TempBlob.WasDecoded, ConSettings);
727 TempBlob := TZAbstractBlob.CreateWithData(PAnsiChar(TempAnsi), Length(TempAnsi));
728 TempBlob.SetString(TempAnsi);
729 InParamValues[I].VInterface := TempBlob;
730 FParamBindBuffer.AddColumn(FIELD_TYPE_STRING, TempBlob.Length, TempBlob.Length > ChunkSize);
734 FParamBindBuffer.AddColumn(FIELD_TYPE_STRING,1,false);
735 FIELD_TYPE_TINY_BLOB:
736 FParamBindBuffer.AddColumn(MyType,Length(InParamValues[i].VBytes),false);
738 FParamBindBuffer.AddColumn(MyType,getMySQLFieldSize(MyType, 0),false);
740 PBuffer := @FColumnArray[I].buffer[0];
742 if InParamValues[I].VType=vtNull then
743 FColumnArray[I].is_null := 1
745 FColumnArray[I].is_null := 0;
746 case FParamBindBuffer.GetBufferType(I+1) of
748 FIELD_TYPE_FLOAT: Single(PBuffer^) := InParamValues[I].VFloat;
749 FIELD_TYPE_DOUBLE: Double(PBuffer^) := InParamValues[I].VFloat;
753 if InParamValues[I].VBoolean then
754 PAnsiChar(PBuffer)^ := 'Y'
756 PAnsiChar(PBuffer)^ := 'N';
758 System.Move(PAnsiChar(TempAnsi)^, PBuffer^, Length(TempAnsi));
760 if not (Length(TempAnsi) > ChunkSize ) then
761 System.Move(PAnsiChar(TempAnsi)^, PBuffer^, Length(TempAnsi));
763 System.Move(PAnsiChar(TempAnsi)^, PBuffer^, Length(TempAnsi));
765 FIELD_TYPE_LONGLONG: Int64(PBuffer^) := InParamValues[I].VInteger;
768 DecodeDateTime(InParamValues[I].VDateTime, Year, Month, Day, hour, minute, second, millisecond);
769 PMYSQL_TIME(PBuffer)^.year := year;
770 PMYSQL_TIME(PBuffer)^.month := month;
771 PMYSQL_TIME(PBuffer)^.day := day;
772 PMYSQL_TIME(PBuffer)^.hour := hour;
773 PMYSQL_TIME(PBuffer)^.minute := minute;
774 PMYSQL_TIME(PBuffer)^.second := second;
775 PMYSQL_TIME(PBuffer)^.second_part := millisecond;
777 FIELD_TYPE_TINY_BLOB:
778 System.Move(PAnsiChar(InParamValues[i].VBytes)^, PBuffer^, Length(InParamValues[i].VBytes));
779 FIELD_TYPE_MEDIUM_BLOB, FIELD_TYPE_LONG_BLOB,
782 if TempBlob.Length<=ChunkSize then
783 System.Move(TempBlob.GetBuffer^, PBuffer^, TempBlob.Length);
790 if (FPlainDriver.BindParameters(FStmtHandle, FParamBindBuffer.GetBufferAddress) <> 0) then
792 checkMySQLPrepStmtError (FPlainDriver, FStmtHandle, lcPrepStmt, SBindingFailure);
795 inherited BindInParameters;
797 // Send large blobs in chuncks
798 For I := 0 to InParamCount - 1 do
800 if FParamBindBuffer.GetBufferType(I+1) in [FIELD_TYPE_STRING,FIELD_TYPE_BLOB] then
802 MyType := GetFieldType(InParamValues[I]);
803 if MyType = FIELD_TYPE_BLOB then
805 TempBlob := (InParamValues[I].VInterface as IZBlob);
806 if TempBlob.Length>ChunkSize then
809 PieceSize := ChunkSize;
810 while OffSet < TempBlob.Length do
812 if OffSet+PieceSize > TempBlob.Length then
813 PieceSize := TempBlob.Length - OffSet;
814 if (FPlainDriver.SendPreparedLongData(FStmtHandle, I, PAnsiChar(TempBlob.GetBuffer)+OffSet, PieceSize) <> 0) then
816 checkMySQLPrepStmtError (FPlainDriver, FStmtHandle, lcPrepStmt, SBindingFailure);
819 Inc(OffSet, PieceSize);
828 procedure TZMySQLPreparedStatement.UnPrepareInParameters;
830 // Empty : Mysql can't prepare datastructures before the actual parameters are known, because the
831 // number/datatype of parameters isn't returned by the server.
832 inherited UnPrepareInParameters;
835 function TZMysqlPreparedStatement.getFieldType (testVariant: TZVariant): TMysqlFieldTypes;
837 case testVariant.vType of
838 vtNull: Result := FIELD_TYPE_TINY;
839 vtBoolean: Result := FIELD_TYPE_TINY;
840 vtBytes: Result := FIELD_TYPE_TINY_BLOB;
841 vtInteger: Result := FIELD_TYPE_LONGLONG;
842 vtFloat: Result := FIELD_TYPE_DOUBLE;
843 vtString: Result := FIELD_TYPE_STRING;
844 vtDateTime: Result := FIELD_TYPE_DATETIME;
845 vtUnicodeString: Result := FIELD_TYPE_VARCHAR;
846 vtInterface: Result := FIELD_TYPE_BLOB;
848 raise EZSQLException.Create(SUnsupportedDataType);
853 Executes the SQL query in this <code>PreparedStatement</code> object
854 and returns the result set generated by the query.
856 @return a <code>ResultSet</code> object that contains the data produced by the
857 query; never <code>null</code>
859 function TZMySQLPreparedStatement.ExecuteQueryPrepared: IZResultSet;
864 if (self.FPlainDriver.ExecuteStmt(FStmtHandle) <> 0) then
866 checkMySQLPrepStmtError(FPlainDriver,FStmtHandle, lcExecPrepStmt, SPreparedStmtExecFailure);
868 if Assigned(FParamBindBuffer) then
869 FreeAndNil(FParamBindBuffer);
873 if Assigned(FParamBindBuffer) then
874 FreeAndNil(FParamBindBuffer);
876 if FPlainDriver.GetPreparedFieldCount(FStmtHandle) = 0 then
877 raise EZSQLException.Create(SCanNotOpenResultSet);
878 Result := CreateResultSet(SQL);
879 inherited ExecuteQueryPrepared;
883 Executes the SQL INSERT, UPDATE or DELETE statement
884 in this <code>PreparedStatement</code> object.
886 SQL statements that return nothing, such as SQL DDL statements,
889 @return either the row count for INSERT, UPDATE or DELETE statements;
890 or 0 for SQL statements that return nothing
892 function TZMySQLPreparedStatement.ExecuteUpdatePrepared: Integer;
896 if (self.FPlainDriver.ExecuteStmt(FStmtHandle) <> 0) then
898 checkMySQLPrepStmtError(FPlainDriver,FStmtHandle, lcExecPrepStmt, SPreparedStmtExecFailure);
900 if Assigned(FParamBindBuffer) then
901 FreeAndNil(FParamBindBuffer); //MemLeak closed
905 if Assigned(FParamBindBuffer) then
906 FreeAndNil(FParamBindBuffer); //MemLeak closed
908 { Process queries with result sets }
909 if FPlainDriver.GetPreparedFieldCount(FStmtHandle) > 0 then
911 FPlainDriver.StorePreparedResult(FStmtHandle);
912 Result := FPlainDriver.GetPreparedAffectedRows(FStmtHandle);
913 if Assigned(FStmtHandle) then
915 FPlainDriver.FreePreparedResult(FStmtHandle);
916 while(FPlainDriver.GetPreparedNextResult(FStmtHandle) = 0) do
917 FPlainDriver.FreePreparedResult(FStmtHandle);
921 { Process regular query }
923 Result := FPlainDriver.GetPreparedAffectedRows(FStmtHandle);
924 LastUpdateCount := Result;
925 Inherited ExecuteUpdatePrepared;
929 Executes any kind of SQL statement.
930 Some prepared statements return multiple results; the <code>execute</code>
931 method handles these complex statements as well as the simpler
932 form of statements handled by the methods <code>executeQuery</code>
933 and <code>executeUpdate</code>.
934 @see Statement#execute
936 function TZMySQLPreparedStatement.ExecutePrepared: Boolean;
940 if (FPlainDriver.ExecuteStmt(FStmtHandle) <> 0) then
942 checkMySQLPrepStmtError(FPlainDriver,FStmtHandle, lcExecPrepStmt, SPreparedStmtExecFailure);
944 if Assigned(FParamBindBuffer) then
945 FreeAndNil(FParamBindBuffer); //MemLeak closed
949 if Assigned(FParamBindBuffer) then
950 FreeAndNil(FParamBindBuffer); //MemLeak closed
952 if FPlainDriver.GetPreparedFieldCount(FStmtHandle) > 0 then
955 LastResultSet := CreateResultSet(SQL);
957 { Processes regular query. }
961 LastUpdateCount := FPlainDriver.GetPreparedAffectedRows(FStmtHandle);
964 inherited ExecutePrepared;
967 function TZMySQLPreparedStatement.GetStmtHandle: PZMySqlPrepStmt;
969 Result := FStmtHandle;
972 { TZMySQLCallableStatement }
975 Create sql string for calling stored procedure.
976 @return a Stored Procedure SQL string
978 function TZMySQLCallableStatement.GetCallSQL: RawByteString;
979 function GenerateParamsStr(Count: integer): RawByteString;
984 for I := 0 to Count-1 do
987 Result := Result + ', ';
988 if FDBParamTypes[i] in [1, 2, 3, 4] then
989 Result := Result + '@'+ZPlainString(FParamNames[I])
994 InParams: RawByteString;
996 if HasOutParameter then
997 InParams := GenerateParamsStr(OutParamCount)
999 InParams := GenerateParamsStr(InParamCount);
1000 Result := 'CALL '+ZPlainString(SQL)+'('+InParams+')';
1003 function TZMySQLCallableStatement.GetOutParamSQL: String;
1004 function GenerateParamsStr(Count: integer): string;
1011 if (FDBParamTypes[i] = 0) or ( I = Length(FDBParamTypes)) then
1015 if FDBParamTypes[i] in [2, 3, 4] then
1017 if Result <> '' then
1018 Result := Result + ',';
1019 if FParamTypeNames[i] = '' then
1020 Result := Result + ' @'+FParamNames[I]+' AS '+FParamNames[I]
1022 Result := Result + ' CAST(@'+FParamNames[I]+ ' AS '+FParamTypeNames[i]+') AS '+FParamNames[I];
1031 OutParams := GenerateParamsStr(Self.OutParamCount-Length(InParamValues));
1032 Result := 'SELECT '+ OutParams;
1035 function TZMySQLCallableStatement.GetSelectFunctionSQL: RawByteString;
1036 function GenerateInParamsStr: RawByteString;
1041 for i := 0 to Length(InParamValues) -1 do
1043 Result := PrepareAnsiSQLParam(I)
1045 Result := Result+', '+ PrepareAnsiSQLParam(I);
1048 InParams: RawByteString;
1050 InParams := GenerateInParamsStr;
1051 Result := 'SELECT '+ZPlainString(SQL)+'('+InParams+')';
1052 Result := Result + ' AS ReturnValue';
1056 Executes an SQL <code>INSERT</code>, <code>UPDATE</code> or
1057 <code>DELETE</code> statement. In addition,
1058 SQL statements that return nothing, such as SQL DDL statements,
1061 @param sql an SQL <code>INSERT</code>, <code>UPDATE</code> or
1062 <code>DELETE</code> statement or an SQL statement that returns nothing
1063 @return either the row count for <code>INSERT</code>, <code>UPDATE</code>
1064 or <code>DELETE</code> statements, or 0 for SQL statements that return nothing
1066 function TZMySQLCallableStatement.PrepareAnsiSQLParam(ParamIndex: Integer): RawByteString;
1069 TempBytes: TByteDynArray;
1071 AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond: Word;
1074 if InParamCount <= ParamIndex then
1075 raise EZSQLException.Create(SInvalidInputParameterCount);
1077 Value := InParamValues[ParamIndex];
1078 if DefVarManager.IsNull(Value) then
1079 if FUseDefaults and (InParamDefaultValues[ParamIndex] <> '') then
1080 Result := ConSettings^.ConvFuncs.ZStringToRaw(InParamDefaultValues[ParamIndex],
1081 ConSettings^.CTRL_CP, ConSettings^.ClientCodePage^.CP)
1086 case InParamTypes[ParamIndex] of
1088 if SoftVarManager.GetAsBoolean(Value) then
1092 stByte, stShort, stInteger, stLong, stBigDecimal, stFloat, stDouble:
1093 Result := RawByteString(SoftVarManager.GetAsString(Value));
1096 TempBytes := SoftVarManager.GetAsBytes(Value);
1097 Result := GetSQLHexAnsiString(PAnsiChar(TempBytes), Length(TempBytes));
1100 Result := FPlainDriver.EscapeString(FHandle, ZPlainString(SoftVarManager.GetAsString(Value), ConSettings), ConSettings, True);
1102 Result := FPlainDriver.EscapeString(FHandle, ZPlainString(SoftVarManager.GetAsUnicodeString(Value)), ConSettings, True);
1105 DecodeDateTime(SoftVarManager.GetAsDateTime(Value),
1106 AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond);
1107 Result := '''' + RawByteString(Format('%0.4d-%0.2d-%0.2d',
1108 [AYear, AMonth, ADay])) + '''';
1112 DecodeDateTime(SoftVarManager.GetAsDateTime(Value),
1113 AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond);
1114 Result := '''' + RawByteString(Format('%0.2d:%0.2d:%0.2d',
1115 [AHour, AMinute, ASecond])) + '''';
1119 DecodeDateTime(SoftVarManager.GetAsDateTime(Value),
1120 AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond);
1121 Result := '''' + RawByteString(Format('%0.4d-%0.2d-%0.2d %0.2d:%0.2d:%0.2d',
1122 [AYear, AMonth, ADay, AHour, AMinute, ASecond])) + '''';
1124 stAsciiStream, stUnicodeStream, stBinaryStream:
1126 TempBlob := DefVarManager.GetAsInterface(Value) as IZBlob;
1127 if not TempBlob.IsEmpty then
1128 case InParamTypes[ParamIndex] of
1130 Result := GetSQLHexAnsiString(PAnsichar(TempBlob.GetBuffer), TempBlob.Length);
1132 Result := FPlainDriver.EscapeString(FHandle,
1133 GetValidatedAnsiStringFromBuffer(TempBlob.GetBuffer,
1134 TempBlob.Length, TempBlob.WasDecoded, ConSettings),
1144 function TZMySQLCallableStatement.GetStmtHandle: PZMySqlPrepStmt;
1149 procedure TZMySQLCallableStatement.ClearResultSets;
1152 FPlainDriver.FreeResult(FQueryHandle);
1153 FQueryHandle := nil;
1156 procedure TZMySQLCallableStatement.BindInParameters;
1159 ExecQuery: RawByteString;
1164 if (i = Length(FDBParamTypes)) then
1168 if FDBParamTypes[i] in [1, 3] then //ptInputOutput
1169 if ExecQuery = '' then
1170 ExecQuery := 'SET @'+ZPlainString(FParamNames[i])+' = '+PrepareAnsiSQLParam(I)
1172 ExecQuery := ExecQuery + ', @'+ZPlainString(FParamNames[i])+' = '+PrepareAnsiSQLParam(I);
1175 if not (ExecQuery = '') then
1176 if FPlainDriver.ExecQuery(Self.FHandle, PAnsiChar(ExecQuery)) = 0 then
1177 DriverManager.LogMessage(lcBindPrepStmt, FPlainDriver.GetProtocol, String(ExecQuery))
1179 CheckMySQLError(FPlainDriver, FHandle, lcExecute, String(ExecQuery));
1183 Creates a result set based on the current settings.
1184 @return a created result set object.
1186 function TZMySQLCallableStatement.CreateResultSet(const SQL: string): IZResultSet;
1188 CachedResolver: TZMySQLCachedResolver;
1189 NativeResultSet: TZMySQLResultSet;
1190 CachedResultSet: TZCachedResultSet;
1192 NativeResultSet := TZMySQLResultSet.Create(FPlainDriver, Self, SQL, FHandle,
1193 FUseResult, @LastUpdateCount, not IsFunction);
1194 NativeResultSet.SetConcurrency(rcReadOnly);
1195 if (GetResultSetConcurrency <> rcReadOnly) or (FUseResult
1196 and (GetResultSetType <> rtForwardOnly)) or (not IsFunction) then
1198 CachedResolver := TZMySQLCachedResolver.Create(FPlainDriver, FHandle, Self,
1199 NativeResultSet.GetMetaData);
1200 CachedResultSet := TZCachedResultSet.Create(NativeResultSet, SQL,
1201 CachedResolver, ConSettings);
1202 CachedResultSet.SetConcurrency(rcReadOnly);
1203 {Need to fetch all data. The handles must be released for mutiple
1205 CachedResultSet.AfterLast;//Fetch all
1206 CachedResultSet.BeforeFirst;//Move to first pos
1207 NativeResultSet.ReleaseHandle; //Release the handles
1208 Result := CachedResultSet;
1211 Result := NativeResultSet;
1215 Sets output parameters from a ResultSet
1216 @param Value a IZResultSet object.
1218 procedure TZMySQLCallableStatement.FetchOutParams(ResultSet: IZResultSet);
1220 ParamIndex, I: Integer;
1224 ResultSet.BeforeFirst;
1225 HasRows := ResultSet.Next;
1228 for ParamIndex := 0 to OutParamCount - 1 do
1230 if not (FDBParamTypes[ParamIndex] in [2, 3, 4]) then // ptOutput, ptInputOutput, ptResult
1232 if I > ResultSet.GetMetadata.GetColumnCount then
1235 if (not HasRows) or (ResultSet.IsNull(I)) then
1236 DefVarManager.SetNull(Temp)
1238 case ResultSet.GetMetadata.GetColumnType(I) of
1240 DefVarManager.SetAsBoolean(Temp, ResultSet.GetBoolean(I));
1242 DefVarManager.SetAsInteger(Temp, ResultSet.GetByte(I));
1244 DefVarManager.SetAsBytes(Temp, ResultSet.GetBytes(I));
1246 DefVarManager.SetAsInteger(Temp, ResultSet.GetShort(I));
1248 DefVarManager.SetAsInteger(Temp, ResultSet.GetInt(I));
1250 DefVarManager.SetAsInteger(Temp, ResultSet.GetLong(I));
1252 DefVarManager.SetAsFloat(Temp, ResultSet.GetFloat(I));
1254 DefVarManager.SetAsFloat(Temp, ResultSet.GetDouble(I));
1256 DefVarManager.SetAsFloat(Temp, ResultSet.GetBigDecimal(I));
1257 stString, stAsciiStream:
1258 DefVarManager.SetAsString(Temp, ResultSet.GetString(I));
1259 stUnicodeString, stUnicodeStream:
1260 DefVarManager.SetAsUnicodeString(Temp, ResultSet.GetUnicodeString(I));
1262 DefVarManager.SetAsDateTime(Temp, ResultSet.GetDate(I));
1264 DefVarManager.SetAsDateTime(Temp, ResultSet.GetTime(I));
1266 DefVarManager.SetAsDateTime(Temp, ResultSet.GetTimestamp(I));
1268 DefVarManager.SetAsInterface(Temp, ResultSet.GetBlob(I));
1270 DefVarManager.SetAsString(Temp, ResultSet.GetString(I));
1272 OutParamValues[ParamIndex] := Temp;
1275 ResultSet.BeforeFirst;
1278 procedure TZMySQLCallableStatement.RegisterParamTypeAndName(const ParameterIndex:integer;
1279 const ParamTypeName, ParamName: String; Const ColumnSize, Precision: Integer);
1281 FParamNames[ParameterIndex] := ParamName;
1282 if ( Pos('char', LowerCase(ParamTypeName)) > 0 ) or
1283 ( Pos('set', LowerCase(ParamTypeName)) > 0 ) then
1284 FParamTypeNames[ParameterIndex] := 'CHAR('+IntToStr(ColumnSize)+')'
1286 if ( Pos('set', LowerCase(ParamTypeName)) > 0 ) then
1287 FParamTypeNames[ParameterIndex] := 'CHAR('+IntToStr(ColumnSize)+')'
1289 if ( Pos('datetime', LowerCase(ParamTypeName)) > 0 ) or
1290 ( Pos('timestamp', LowerCase(ParamTypeName)) > 0 ) then
1291 FParamTypeNames[ParameterIndex] := 'DATETIME'
1293 if ( Pos('date', LowerCase(ParamTypeName)) > 0 ) then
1294 FParamTypeNames[ParameterIndex] := 'DATE'
1296 if ( Pos('time', LowerCase(ParamTypeName)) > 0 ) then
1297 FParamTypeNames[ParameterIndex] := 'TIME'
1299 if ( Pos('int', LowerCase(ParamTypeName)) > 0 ) or
1300 ( Pos('year', LowerCase(ParamTypeName)) > 0 ) then
1301 FParamTypeNames[ParameterIndex] := 'SIGNED'
1303 if ( Pos('binary', LowerCase(ParamTypeName)) > 0 ) then
1304 FParamTypeNames[ParameterIndex] := 'BINARY('+IntToStr(ColumnSize)+')'
1306 FParamTypeNames[ParameterIndex] := '';
1309 constructor TZMySQLCallableStatement.Create(PlainDriver: IZMySQLPlainDriver;
1310 Connection: IZConnection; const SQL: string; Info: TStrings;
1311 Handle: PZMySQLConnect);
1313 inherited Create(Connection, SQL, Info);
1315 FPlainDriver := PlainDriver;
1316 ResultSetType := rtScrollInsensitive;
1317 FUseResult := StrToBoolEx(DefineStatementParameter(Self, 'useresult', 'false'));
1318 FUseDefaults := StrToBoolEx(DefineStatementParameter(Self, 'defaults', 'true'))
1322 Executes an SQL statement that returns a single <code>ResultSet</code> object.
1323 @param sql typically this is a static SQL <code>SELECT</code> statement
1324 @return a <code>ResultSet</code> object that contains the data produced by the
1325 given query; never <code>null</code>
1327 function TZMySQLCallableStatement.ExecuteQuery(const SQL: RawByteString): IZResultSet;
1331 if FPlainDriver.ExecQuery(FHandle, PAnsiChar(SQL)) = 0 then
1333 DriverManager.LogMessage(lcExecute, FPlainDriver.GetProtocol, SSQL);
1334 if not FPlainDriver.ResultSetExists(FHandle) then
1335 raise EZSQLException.Create(SCanNotOpenResultSet);
1338 FResultSets.Add(CreateResultSet(SSQL));
1339 if FPlainDriver.CheckAnotherRowset(FHandle) then
1341 while FPlainDriver.RetrieveNextRowset(FHandle) = 0 do
1342 if FPlainDriver.CheckAnotherRowset(FHandle) then
1343 FResultSets.Add(CreateResultSet(SSQL))
1345 CheckMySQLError(FPlainDriver, FHandle, lcExecute, SSQL);
1347 FActiveResultset := FResultSets.Count-1;
1348 Result := IZResultSet(FResultSets[FActiveResultset]);
1351 CheckMySQLError(FPlainDriver, FHandle, lcExecute, SSQL);
1355 Executes an SQL <code>INSERT</code>, <code>UPDATE</code> or
1356 <code>DELETE</code> statement. In addition,
1357 SQL statements that return nothing, such as SQL DDL statements,
1360 @param sql an SQL <code>INSERT</code>, <code>UPDATE</code> or
1361 <code>DELETE</code> statement or an SQL statement that returns nothing
1362 @return either the row count for <code>INSERT</code>, <code>UPDATE</code>
1363 or <code>DELETE</code> statements, or 0 for SQL statements that return nothing
1365 function TZMySQLCallableStatement.ExecuteUpdate(const SQL: RawByteString): Integer;
1369 if FPlainDriver.ExecQuery(FHandle, PAnsiChar(ASQL)) = 0 then
1371 { Process queries with result sets }
1372 if FPlainDriver.ResultSetExists(FHandle) then
1375 FActiveResultset := 0;
1376 FResultSets.Add(CreateResultSet(LogSQL));
1377 if FPlainDriver.CheckAnotherRowset(FHandle) then
1379 Result := LastUpdateCount;
1380 while FPlainDriver.RetrieveNextRowset(FHandle) = 0 do
1381 if FPlainDriver.CheckAnotherRowset(FHandle) then
1383 FResultSets.Add(CreateResultSet(SSQL));
1384 inc(Result, LastUpdateCount); //LastUpdateCount will be returned from ResultSet.Open
1387 CheckMySQLError(FPlainDriver, FHandle, lcExecute, SSQL);
1390 Result := LastUpdateCount;
1391 FActiveResultset := FResultSets.Count-1;
1392 LastResultSet := IZResultSet(FResultSets[FActiveResultset]);
1394 else { Process regular query }
1395 Result := FPlainDriver.GetAffectedRows(FHandle);
1398 CheckMySQLError(FPlainDriver, FHandle, lcExecute, SSQL);
1399 LastUpdateCount := Result;
1403 Executes an SQL statement that may return multiple results.
1404 Under some (uncommon) situations a single SQL statement may return
1405 multiple result sets and/or update counts. Normally you can ignore
1406 this unless you are (1) executing a stored procedure that you know may
1407 return multiple results or (2) you are dynamically executing an
1408 unknown SQL string. The methods <code>execute</code>,
1409 <code>getMoreResults</code>, <code>getResultSet</code>,
1410 and <code>getUpdateCount</code> let you navigate through multiple results.
1412 The <code>execute</code> method executes an SQL statement and indicates the
1413 form of the first result. You can then use the methods
1414 <code>getResultSet</code> or <code>getUpdateCount</code>
1415 to retrieve the result, and <code>getMoreResults</code> to
1416 move to any subsequent result(s).
1418 @param sql any SQL statement
1419 @return <code>true</code> if the next result is a <code>ResultSet</code> object;
1420 <code>false</code> if it is an update count or there are no more results
1422 function TZMySQLCallableStatement.Execute(const SQL: RawByteString): Boolean;
1424 HasResultset : Boolean;
1427 {$IFNDEF UNICODE}ASQL := SQL;{$ENDIF}
1428 if FPlainDriver.ExecQuery(FHandle, PAnsiChar(ASQL)) = 0 then
1430 DriverManager.LogMessage(lcExecute, FPlainDriver.GetProtocol, LogSQL);
1431 HasResultSet := FPlainDriver.ResultSetExists(FHandle);
1432 { Process queries with result sets }
1433 if HasResultSet then
1436 LastResultSet := CreateResultSet(LogSQL);
1438 { Processes regular query. }
1442 LastUpdateCount := FPlainDriver.GetAffectedRows(FHandle);
1446 CheckMySQLError(FPlainDriver, FHandle, lcExecute, LogSQL);
1450 Executes the SQL query in this <code>PreparedStatement</code> object
1451 and returns the result set generated by the query.
1453 @return a <code>ResultSet</code> object that contains the data produced by the
1454 query; never <code>null</code>
1456 function TZMySQLCallableStatement.ExecuteQueryPrepared: IZResultSet;
1461 Result := ExecuteQuery(GetSelectFunctionSQL);
1466 ExecuteUpdate(GetCallSQL);
1467 if OutParamCount > 0 then
1468 Result := ExecuteQuery(ZPlainString(GetOutParamSQL)) //Get the Last Resultset
1470 Result := GetLastResultSet;
1472 if Assigned(Result) then
1473 FetchOutParams(Result);
1477 Executes the SQL INSERT, UPDATE or DELETE statement
1478 in this <code>PreparedStatement</code> object.
1480 SQL statements that return nothing, such as SQL DDL statements,
1483 @return either the row count for INSERT, UPDATE or DELETE statements;
1484 or 0 for SQL statements that return nothing
1486 function TZMySQLCallableStatement.ExecuteUpdatePrepared: Integer;
1491 Result := ExecuteUpdate(GetSelectFunctionSQL);
1492 FetchOutParams(LastResultSet);
1497 Result := ExecuteUpdate(GetCallSQL);
1498 if OutParamCount > 0 then
1499 FetchOutParams(ExecuteQuery(ZPlainString(GetOutParamSQL))); //Get the Last Resultset
1500 Inc(Result, LastUpdateCount);
1505 Checks is use result should be used in result sets.
1506 @return <code>True</code> use result in result sets,
1507 <code>False</code> store result in result sets.
1509 function TZMySQLCallableStatement.IsUseResult: Boolean;
1511 Result := FUseResult;
1515 Checks if this is a prepared mysql statement.
1516 @return <code>False</code> This is not a prepared mysql statement.
1518 function TZMySQLCallableStatement.IsPreparedStatement: Boolean;
1524 Are more resultsets retrieved?
1525 @result Returns <code>True</code> if more resultsets are retrieved
1527 function TZMySQLCallableStatement.HasMoreResultSets: Boolean;
1529 Result := FResultSets.Count > 1;
1533 Get the first resultset..
1534 @result <code>IZResultSet</code> if supported
1536 function TZMySQLCallableStatement.GetNextResultSet: IZResultSet;
1538 if ( FActiveResultset < FResultSets.Count-1) and ( FResultSets.Count > 1) then
1540 Inc(FActiveResultset);
1541 Result := IZResultSet(FResultSets[FActiveResultset]);
1544 if FResultSets.Count = 0 then
1547 Result := IZResultSet(FResultSets[FActiveResultset]);
1551 Get the previous resultset..
1552 @result <code>IZResultSet</code> if supported
1554 function TZMySQLCallableStatement.GetPreviousResultSet: IZResultSet;
1556 if ( FActiveResultset > 0) and ( FResultSets.Count > 0) then
1558 Dec(FActiveResultset);
1559 Result := IZResultSet(FResultSets[FActiveResultset]);
1562 if FResultSets.Count = 0 then
1565 Result := IZResultSet(FResultSets[FActiveResultset]);
1569 Get the next resultset..
1570 @result <code>IZResultSet</code> if supported
1572 function TZMySQLCallableStatement.GetFirstResultSet: IZResultSet;
1574 if FResultSets.Count = 0 then
1578 FActiveResultset := 0;
1579 Result := IZResultSet(FResultSets[0]);
1584 Get the last resultset..
1585 @result <code>IZResultSet</code> if supported
1587 function TZMySQLCallableStatement.GetLastResultSet: IZResultSet;
1589 if FResultSets.Count = 0 then
1593 FActiveResultset := FResultSets.Count -1;
1594 Result := IZResultSet(FResultSets[FResultSets.Count -1]);
1600 @result <code>True</code> if first ResultSet
1602 function TZMySQLCallableStatement.BOR: Boolean;
1604 Result := FActiveResultset = 0;
1609 @result <code>True</code> if Last ResultSet
1611 function TZMySQLCallableStatement.EOR: Boolean;
1613 Result := FActiveResultset = FResultSets.Count -1;
1617 Retrieves a ResultSet by his index.
1618 @param Integer the index of the Resultset
1619 @result <code>IZResultSet</code> of the Index or nil.
1621 function TZMySQLCallableStatement.GetResultSetByIndex(const Index: Integer): IZResultSet;
1624 if ( Index < 0 ) or ( Index > FResultSets.Count -1 ) then
1625 raise Exception.Create(Format(SListIndexError, [Index]))
1627 Result := IZResultSet(FResultSets[Index]);
1631 Returns the Count of retrived ResultSets.
1632 @result <code>Integer</code> Count
1634 function TZMySQLCallableStatement.GetResultSetCount: Integer;
1636 Result := FResultSets.Count;
1639 { TZMySQLAbstractBindBuffer }
1641 constructor TZMySQLAbstractBindBuffer.Create(PlainDriver: IZMysqlPlainDriver;
1642 const BindCount: Integer; var ColumnArray: TZMysqlColumnBuffer);
1645 FBindOffsets := PlainDriver.GetBindOffsets;
1646 if FBindOffsets.buffer_type=0 then
1647 raise EZSQLException.Create('Unknown dll version : '+IntToStr(PlainDriver.GetClientVersion));
1648 FPColumnArray := @ColumnArray;
1649 setlength(FBindArray,0);
1650 setlength(ColumnArray,BindCount);
1651 setlength(FBindArray,BindCount*FBindOffsets.size);
1654 function TZMySQLAbstractBindBuffer.GetColumnArray: TZMysqlColumnBuffer;
1656 result := FPColumnArray^;
1659 function TZMySQLAbstractBindBuffer.GetBufferAddress: Pointer;
1661 result := @FBindArray[0];
1664 function TZMySQLAbstractBindBuffer.GetBufferType(ColumnIndex: Integer): TMysqlFieldTypes;
1666 result := PTMysqlFieldTypes(@FbindArray[NativeUInt((ColumnIndex-1)*FBindOffsets.size)+FBindOffsets.buffer_type])^;
1669 function TZMySQLAbstractBindBuffer.GetBufferIsSigned(ColumnIndex: Integer): Boolean;
1671 result := PByte(@FbindArray[NativeUInt((ColumnIndex-1)*FBindOffsets.size)+FBindOffsets.is_unsigned])^ <> 0;
1674 { TZMySQLResultSetBindBuffer }
1676 procedure TZMySQLResultSetBindBuffer.AddColumn(PlainDriver: IZMysqlPlainDriver;
1677 const FieldHandle: PZMySQLField);
1679 buffertype: TMysqlFieldTypes;
1680 ColOffset: NativeUInt;
1682 buffertype := PlainDriver.GetFieldType(FieldHandle);
1683 Inc(FAddedColumnCount);
1684 With FPColumnArray^[FAddedColumnCount-1] do
1687 FIELD_TYPE_DATE: Length := sizeOf(MYSQL_TIME);
1688 FIELD_TYPE_TIME: Length := sizeOf(MYSQL_TIME);
1689 FIELD_TYPE_DATETIME: Length := sizeOf(MYSQL_TIME);
1690 FIELD_TYPE_TIMESTAMP: Length := sizeOf(MYSQL_TIME);
1691 FIELD_TYPE_TINY: Length := 1;
1692 FIELD_TYPE_SHORT: Length := 2;
1693 FIELD_TYPE_LONG: Length := 4;
1694 FIELD_TYPE_LONGLONG: Length := 8;
1695 FIELD_TYPE_FLOAT: Length := 4;
1696 FIELD_TYPE_DOUBLE: Length := 8;
1697 FIELD_TYPE_NEWDECIMAL: Length := 11;
1699 FIELD_TYPE_MEDIUM_BLOB,
1700 FIELD_TYPE_LONG_BLOB,
1701 FIELD_TYPE_GEOMETRY:
1702 Length := PlainDriver.GetFieldMaxLength(FieldHandle)+1;
1704 FIELD_TYPE_VAR_STRING,
1706 Length := Min(MaxBlobSize, Max(PlainDriver.GetFieldLength(FieldHandle), PlainDriver.GetFieldMaxLength(FieldHandle)))+1;
1708 Length := PlainDriver.GetFieldLength(FieldHandle);
1710 SetLength(Buffer, Length);
1712 ColOffset := NativeUInt((FAddedColumnCount-1)*FBindOffsets.size);
1713 PTMysqlFieldTypes(@FbindArray[ColOffset+FBindOffsets.buffer_type])^ := buffertype;
1714 PULong(@FbindArray[ColOffset+FBindOffsets.buffer_length])^ := FPColumnArray^[FAddedColumnCount-1].length;
1715 PByte(@FbindArray[ColOffset+FBindOffsets.is_unsigned])^:= PlainDriver.GetFieldFlags(FieldHandle) and UNSIGNED_FLAG;
1716 PPointer(@FbindArray[ColOffset+FBindOffsets.buffer])^:= @FPColumnArray^[FAddedColumnCount-1].buffer[0];
1717 PPointer(@FbindArray[ColOffset+FBindOffsets.length])^:= @FPColumnArray^[FAddedColumnCount-1].length;
1718 PPointer(@FbindArray[ColOffset+FBindOffsets.is_null])^:= @FPColumnArray^[FAddedColumnCount-1].is_null;
1721 { TZMySQLParamBindBuffer }
1723 // largeblobparameter: true to indicate that parameter is a blob that will be
1724 // sent chunked. Set to false for result set columns.
1726 procedure TZMySQLParamBindBuffer.AddColumn(buffertype: TMysqlFieldTypes;
1727 const field_length: integer; const largeblobparameter: boolean);
1729 tempbuffertype: TMysqlFieldTypes;
1730 ColOffset:NativeUInt;
1734 FIELD_TYPE_NEWDECIMAL: tempbuffertype := FIELD_TYPE_DOUBLE;
1736 tempbuffertype := buffertype;
1738 Inc(FAddedColumnCount);
1739 With FPColumnArray^[FAddedColumnCount-1] do
1741 length := getMySQLFieldSize(tempbuffertype,field_length);
1742 if largeblobparameter then
1747 else if field_length = 0 then
1754 if tempbuffertype in [FIELD_TYPE_TINY_BLOB, FIELD_TYPE_MEDIUM_BLOB,
1755 FIELD_TYPE_LONG_BLOB, FIELD_TYPE_BLOB, FIELD_TYPE_VAR_STRING, FIELD_TYPE_STRING] then
1756 //ludob: mysql adds terminating #0 on top of data. Avoid buffer overrun.
1757 SetLength(buffer,length+1)
1759 SetLength(buffer,length);
1763 ColOffset:=NativeUInt((FAddedColumnCount-1)*FBindOffsets.size);
1764 PTMysqlFieldTypes(@FbindArray[ColOffset+FBindOffsets.buffer_type])^:=tempbuffertype;
1765 PULong(@FbindArray[ColOffset+FBindOffsets.buffer_length])^ := FPColumnArray^[FAddedColumnCount-1].length;
1766 PByte(@FbindArray[ColOffset+FBindOffsets.is_unsigned])^:= 0;
1767 PPointer(@FbindArray[ColOffset+FBindOffsets.buffer])^:= @FPColumnArray^[FAddedColumnCount-1].buffer[0];
1768 PPointer(@FbindArray[ColOffset+FBindOffsets.length])^:= @FPColumnArray^[FAddedColumnCount-1].length;
1769 PPointer(@FbindArray[ColOffset+FBindOffsets.is_null])^:= @FPColumnArray^[FAddedColumnCount-1].is_null;