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 ZDbcMySqlResultSet;
59 Classes, {$IFDEF MSEgui}mclasses,{$ENDIF} SysUtils, Types, Contnrs,
60 ZDbcIntfs, ZDbcResultSet, ZDbcResultSetMetadata, ZCompatibility, ZDbcCache,
61 ZDbcCachedResultSet, ZDbcGenericResolver, ZDbcMySqlStatement,
62 ZPlainMySqlDriver, ZPlainMySqlConstants;
65 {** Implements MySQL ResultSet Metadata. }
66 TZMySQLResultSetMetadata = class(TZAbstractResultSetMetadata)
68 function GetColumnType(Column: Integer): TZSQLType; override;
71 {** Implements MySQL ResultSet. }
72 TZMySQLResultSet = class(TZAbstractResultSet)
74 FHandle: PZMySQLConnect;
75 FQueryHandle: PZMySQLResult;
76 FRowHandle: PZMySQLRow;
77 FPlainDriver: IZMySQLPlainDriver;
79 FIgnoreUseResult: Boolean;
81 FRawTemp: RawByteString;
82 FMySQLTypes: array of TMysqlFieldTypes;
84 procedure Open; override;
85 function InternalGetString(ColumnIndex: Integer): RawByteString; override;
87 constructor Create(PlainDriver: IZMySQLPlainDriver; Statement: IZStatement;
88 SQL: string; Handle: PZMySQLConnect; UseResult: Boolean;
89 AffectedRows: PInteger; IgnoreUseResult: Boolean = False);
90 destructor Destroy; override;
92 procedure Close; override;
94 function IsNull(ColumnIndex: Integer): Boolean; override;
95 function GetPChar(ColumnIndex: Integer): PChar; override;
96 function GetBoolean(ColumnIndex: Integer): Boolean; override;
97 function GetByte(ColumnIndex: Integer): Byte; override;
98 function GetShort(ColumnIndex: Integer): SmallInt; override;
99 function GetInt(ColumnIndex: Integer): Integer; override;
100 function GetLong(ColumnIndex: Integer): Int64; override;
101 function GetFloat(ColumnIndex: Integer): Single; override;
102 function GetDouble(ColumnIndex: Integer): Double; override;
103 function GetBigDecimal(ColumnIndex: Integer): Extended; override;
104 function GetBytes(ColumnIndex: Integer): TByteDynArray; override;
105 function GetDate(ColumnIndex: Integer): TDateTime; override;
106 function GetTime(ColumnIndex: Integer): TDateTime; override;
107 function GetTimestamp(ColumnIndex: Integer): TDateTime; override;
108 function GetBlob(ColumnIndex: Integer): IZBlob; override;
110 function MoveAbsolute(Row: Integer): Boolean; override;
111 function Next: Boolean; override;
112 procedure ReleaseHandle;
115 {** Implements Prepared MySQL ResultSet. }
116 TZMySQLPreparedResultSet = class(TZAbstractResultSet)
118 FHandle: PZMySQLConnect;
119 FPrepStmt: PZMySqlPrepStmt;
120 FResultMetaData : PZMySQLResult;
121 FPlainDriver: IZMySQLPlainDriver;
123 FColumnArray: TZMysqlColumnBuffer;
124 FBindBuffer: TZMySqlResultSetBindBuffer;
125 function bufferasint64(ColumnIndex: Integer): Int64;
126 function bufferasextended(ColumnIndex: Integer): Extended;
129 function InternalGetString(ColumnIndex: Integer): RawByteString; override;
130 procedure Open; override;
132 constructor Create(PlainDriver: IZMySQLPlainDriver; Statement: IZStatement;
133 SQL: string; Handle: PZMySQLConnect; UseResult: Boolean);
134 destructor Destroy; override;
136 procedure Close; override;
138 function IsNull(ColumnIndex: Integer): Boolean; override;
139 function GetBoolean(ColumnIndex: Integer): Boolean; override;
140 function GetByte(ColumnIndex: Integer): Byte; override;
141 function GetShort(ColumnIndex: Integer): SmallInt; override;
142 function GetInt(ColumnIndex: Integer): Integer; override;
143 function GetLong(ColumnIndex: Integer): Int64; override;
144 function GetFloat(ColumnIndex: Integer): Single; override;
145 function GetDouble(ColumnIndex: Integer): Double; override;
146 function GetBigDecimal(ColumnIndex: Integer): Extended; override;
147 function GetBytes(ColumnIndex: Integer): TByteDynArray; override;
148 function GetDate(ColumnIndex: Integer): TDateTime; override;
149 function GetTime(ColumnIndex: Integer): TDateTime; override;
150 function GetTimestamp(ColumnIndex: Integer): TDateTime; override;
151 function GetAsciiStream(ColumnIndex: Integer): TStream; override;
152 function GetUnicodeStream(ColumnIndex: Integer): TStream; override;
153 function GetBinaryStream(ColumnIndex: Integer): TStream; override;
154 function GetBlob(ColumnIndex: Integer): IZBlob; override;
156 function MoveAbsolute(Row: Integer): Boolean; override;
157 function Next: Boolean; override;
160 {** Implements a cached resolver with MySQL specific functionality. }
161 TZMySQLCachedResolver = class (TZGenericCachedResolver, IZCachedResolver)
163 FHandle: PZMySQLConnect;
164 FPlainDriver: IZMySQLPlainDriver;
165 FAutoColumnIndex: Integer;
166 FStatement: IZMysqlStatement;
168 constructor Create(PlainDriver: IZMySQLPlainDriver; Handle: PZMySQLConnect;
169 Statement: IZMysqlStatement; Metadata: IZResultSetMetadata);
171 function FormWhereClause(Columns: TObjectList;
172 OldRowAccessor: TZRowAccessor): string; override;
173 procedure PostUpdates(Sender: IZCachedResultSet; UpdateType: TZRowUpdateType;
174 OldRowAccessor, NewRowAccessor: TZRowAccessor); override;
176 // --> ms, 31/10/2005
177 function FormCalculateStatement(Columns: TObjectList): string; override;
179 {BEGIN of PATCH [1185969]: Do tasks after posting updates. ie: Updating AutoInc fields in MySQL }
180 procedure UpdateAutoIncrementFields(Sender: IZCachedResultSet; UpdateType: TZRowUpdateType;
181 OldRowAccessor, NewRowAccessor: TZRowAccessor; Resolver: IZCachedResolver); override;
182 {END of PATCH [1185969]: Do tasks after posting updates. ie: Updating AutoInc fields in MySQL }
188 Math, ZMessages, ZDbcMySqlUtils, ZMatchPattern, ZDbcMysql, ZEncoding, ZSysUtils;
190 { TZMySQLResultSetMetadata }
193 Retrieves the designated column's SQL type.
194 @param column the first column is 1, the second is 2, ...
195 @return SQL type from java.sql.Types
197 function TZMySQLResultSetMetadata.GetColumnType(Column: Integer): TZSQLType;
198 begin {EH: does anyone know why the LoadColumns was made? Note the column-types are perfect determinable on MySQL}
201 Result := TZColumnInfo(ResultSet.ColumnsInfo[Column - 1]).ColumnType;
207 Constructs this object, assignes main properties and
208 opens the record set.
209 @param PlainDriver a native MySQL plain driver.
210 @param Statement a related SQL statement object.
211 @param Handle a MySQL specific query handle.
212 @param UseResult <code>True</code> to use results,
213 <code>False</code> to store result.
215 constructor TZMySQLResultSet.Create(PlainDriver: IZMySQLPlainDriver;
216 Statement: IZStatement; SQL: string; Handle: PZMySQLConnect;
217 UseResult: Boolean; AffectedRows: PInteger; IgnoreUseResult: Boolean = False);
219 inherited Create(Statement, SQL, TZMySQLResultSetMetadata.Create(
220 Statement.GetConnection.GetMetadata, SQL, Self),
221 Statement.GetConnection.GetConSettings);
226 FPlainDriver := PlainDriver;
227 ResultSetConcurrency := rcReadOnly;
228 FUseResult := UseResult;
229 FIgnoreUseResult := IgnoreUseResult;
232 if Assigned(AffectedRows) then
233 AffectedRows^ := LastRowNo;
237 Destroys this object and cleanups the memory.
239 destructor TZMySQLResultSet.Destroy;
245 Opens this recordset.
247 procedure TZMySQLResultSet.Open;
250 FieldHandle: PZMySQLField;
252 if ResultSetConcurrency = rcUpdatable then
253 raise EZSQLException.Create(SLiveResultSetsAreNotSupported);
255 if FUseResult and (not FIgnoreUseResult) then
257 FQueryHandle := FPlainDriver.UseResult(FHandle);
262 FQueryHandle := FPlainDriver.StoreResult(FHandle);
263 if Assigned(FQueryHandle) then
264 LastRowNo := FPlainDriver.GetRowCount(FQueryHandle)
269 if not Assigned(FQueryHandle) then
270 raise EZSQLException.Create(SCanNotRetrieveResultSetData);
272 { Fills the column info. }
274 SetLength(FMySQLTypes, FPlainDriver.GetFieldCount(FQueryHandle));
275 for I := 0 to FPlainDriver.GetFieldCount(FQueryHandle) - 1 do
277 FPlainDriver.SeekField(FQueryHandle, I);
278 FieldHandle := FPlainDriver.FetchField(FQueryHandle);
279 FMySQLTypes[i] := PMYSQL_FIELD(FieldHandle)^._type;
280 if FieldHandle = nil then
283 ColumnsInfo.Add(GetMySQLColumnInfoFromFieldHandle(FPlainDriver,
284 FieldHandle, ConSettings, FUseResult));
291 Releases this <code>ResultSet</code> object's database and
292 JDBC resources immediately instead of waiting for
293 this to happen when it is automatically closed.
295 <P><B>Note:</B> A <code>ResultSet</code> object
296 is automatically closed by the
297 <code>Statement</code> object that generated it when
298 that <code>Statement</code> object is closed,
299 re-executed, or is used to retrieve the next result from a
300 sequence of multiple results. A <code>ResultSet</code> object
301 is also automatically closed when it is garbage collected.
303 procedure TZMySQLResultSet.Close;
305 if FQueryHandle <> nil then
307 FPlainDriver.FreeResult(FQueryHandle);
308 while(FPlainDriver.RetrieveNextRowset(FHandle) = 0) do
310 FQueryHandle := FPlainDriver.StoreResult(FHandle);
311 if FQueryHandle <> nil then
313 FPlainDriver.FreeResult(FQueryHandle);
323 Indicates if the value of the designated column in the current row
324 of this <code>ResultSet</code> object is Null.
326 @param columnIndex the first column is 1, the second is 2, ...
327 @return if the value is SQL <code>NULL</code>, the
328 value returned is <code>true</code>. <code>false</code> otherwise.
330 function TZMySQLResultSet.IsNull(ColumnIndex: Integer): Boolean;
334 {$IFNDEF DISABLE_CHECKING}
336 if FRowHandle = nil then
337 raise EZSQLException.Create(SRowDataIsNotAvailable);
340 Temp := FPlainDriver.GetFieldData(FRowHandle, ColumnIndex - 1);
341 Result := (Temp = nil);
342 if not Result and (TZAbstractResultSetMetadata(Metadata).
343 GetColumnType(ColumnIndex) in [stDate, stTimestamp]) then
345 Result := (AnsiSQLDateToDateTime(String(Temp)) = 0)
346 and (TimestampStrToDateTime(String(Temp)) = 0);
351 Gets the value of the designated column in the current row
352 of this <code>ResultSet</code> object as
353 a <code>PAnsiChar</code> in the Delphi programming language.
355 @param columnIndex the first column is 1, the second is 2, ...
356 @return the column value; if the value is SQL <code>NULL</code>, the
357 value returned is <code>null</code>
359 function TZMySQLResultSet.GetPChar(ColumnIndex: Integer): PChar;
361 {$IFNDEF DISABLE_CHECKING}
363 if FRowHandle = nil then
364 raise EZSQLException.Create(SRowDataIsNotAvailable);
367 TempStr := ZDbcString(FPlainDriver.GetFieldData(FRowHandle, ColumnIndex - 1));
368 Result := PChar(TempStr);
369 LastWasNull := Result = nil;
373 Gets the value of the designated column in the current row
374 of this <code>ResultSet</code> object as
375 a <code>String</code>.
377 @param columnIndex the first column is 1, the second is 2, ...
378 @return the column value; if the value is SQL <code>NULL</code>, the
379 value returned is <code>null</code>
381 function TZMySQLResultSet.InternalGetString(ColumnIndex: Integer): RawByteString;
383 LengthPointer: PULong;
387 {$IFNDEF DISABLE_CHECKING}
389 if FRowHandle = nil then
390 raise EZSQLException.Create(SRowDataIsNotAvailable);
393 ColumnIndex := ColumnIndex - 1;
394 LengthPointer := FPlainDriver.FetchLengths(FQueryHandle);
395 if LengthPointer <> nil then
396 Length := PULong(NativeUint(LengthPointer) + NativeUInt(ColumnIndex) * SizeOf(ULOng))^
399 Buffer := FPlainDriver.GetFieldData(FRowHandle, ColumnIndex);
400 LastWasNull := Buffer = nil;
402 if not LastWasNull then
403 {$IFDEF WITH_RAWBYTESTRING}
405 SetLength(Result, Length);
406 Move(Buffer^, PAnsiChar(Result)^, Length);
409 SetString(Result, Buffer, Length);
414 Gets the value of the designated column in the current row
415 of this <code>ResultSet</code> object as
416 a <code>boolean</code> in the Java programming language.
418 @param columnIndex the first column is 1, the second is 2, ...
419 @return the column value; if the value is SQL <code>NULL</code>, the
420 value returned is <code>false</code>
422 function TZMySQLResultSet.GetBoolean(ColumnIndex: Integer): Boolean;
426 {$IFNDEF DISABLE_CHECKING}
427 CheckColumnConvertion(ColumnIndex, stBoolean);
429 Temp := UpperCase(String(InternalGetString(ColumnIndex)));
430 Result := (Temp = 'Y') or (Temp = 'YES') or (Temp = 'T') or
431 (Temp = 'TRUE') or (StrToIntDef(Temp, 0) <> 0);
435 Gets the value of the designated column in the current row
436 of this <code>ResultSet</code> object as
437 a <code>byte</code> in the Java programming language.
439 @param columnIndex the first column is 1, the second is 2, ...
440 @return the column value; if the value is SQL <code>NULL</code>, the
441 value returned is <code>0</code>
443 function TZMySQLResultSet.GetByte(ColumnIndex: Integer): Byte;
445 {$IFNDEF DISABLE_CHECKING}
446 CheckColumnConvertion(ColumnIndex, stByte);
448 FRawTemp := InternalGetString(ColumnIndex);
452 if FMySQLTypes[ColumnIndex -1] = FIELD_TYPE_BIT then
453 case Length(FRawTemp) of
454 1: Result := PByte(Pointer(FRawTemp))^;
455 2: Result := ReverseWordBytes(Pointer(FRawTemp));
456 3, 4: Result := ReverseLongWordBytes(Pointer(FRawTemp), Length(FRawTemp));
457 else //5..8: makes compiler happy
458 Result := ReverseQuadWordBytes(Pointer(FRawTemp), Length(FRawTemp));
461 Result := StrToIntDef(String(FRawTemp), 0);
465 Gets the value of the designated column in the current row
466 of this <code>ResultSet</code> object as
467 a <code>short</code> in the Java programming language.
469 @param columnIndex the first column is 1, the second is 2, ...
470 @return the column value; if the value is SQL <code>NULL</code>, the
471 value returned is <code>0</code>
473 function TZMySQLResultSet.GetShort(ColumnIndex: Integer): SmallInt;
475 {$IFNDEF DISABLE_CHECKING}
476 CheckColumnConvertion(ColumnIndex, stShort);
478 Result := SmallInt(StrToIntDef(String(InternalGetString(ColumnIndex)), 0));
482 Gets the value of the designated column in the current row
483 of this <code>ResultSet</code> object as
484 an <code>int</code> in the Java programming language.
486 @param columnIndex the first column is 1, the second is 2, ...
487 @return the column value; if the value is SQL <code>NULL</code>, the
488 value returned is <code>0</code>
490 function TZMySQLResultSet.GetInt(ColumnIndex: Integer): Integer;
492 {$IFNDEF DISABLE_CHECKING}
493 CheckColumnConvertion(ColumnIndex, stInteger);
495 Result := StrToIntDef(String(InternalGetString(ColumnIndex)), 0);
499 Gets the value of the designated column in the current row
500 of this <code>ResultSet</code> object as
501 a <code>long</code> in the Java programming language.
503 @param columnIndex the first column is 1, the second is 2, ...
504 @return the column value; if the value is SQL <code>NULL</code>, the
505 value returned is <code>0</code>
507 function TZMySQLResultSet.GetLong(ColumnIndex: Integer): Int64;
509 {$IFNDEF DISABLE_CHECKING}
510 CheckColumnConvertion(ColumnIndex, stLong);
512 Result := StrToInt64Def(String(InternalGetString(ColumnIndex)), 0);
516 Gets the value of the designated column in the current row
517 of this <code>ResultSet</code> object as
518 a <code>float</code> in the Java programming language.
520 @param columnIndex the first column is 1, the second is 2, ...
521 @return the column value; if the value is SQL <code>NULL</code>, the
522 value returned is <code>0</code>
524 function TZMySQLResultSet.GetFloat(ColumnIndex: Integer): Single;
526 {$IFNDEF DISABLE_CHECKING}
527 CheckColumnConvertion(ColumnIndex, stFloat);
529 Result := SQLStrToFloatDef(InternalGetString(ColumnIndex), 0);
533 Gets the value of the designated column in the current row
534 of this <code>ResultSet</code> object as
535 a <code>double</code> in the Java programming language.
537 @param columnIndex the first column is 1, the second is 2, ...
538 @return the column value; if the value is SQL <code>NULL</code>, the
539 value returned is <code>0</code>
541 function TZMySQLResultSet.GetDouble(ColumnIndex: Integer): Double;
543 {$IFNDEF DISABLE_CHECKING}
544 CheckColumnConvertion(ColumnIndex, stDouble);
546 Result := SQLStrToFloatDef(InternalGetString(ColumnIndex), 0);
550 Gets the value of the designated column in the current row
551 of this <code>ResultSet</code> object as
552 a <code>java.sql.BigDecimal</code> in the Java programming language.
554 @param columnIndex the first column is 1, the second is 2, ...
555 @param scale the number of digits to the right of the decimal point
556 @return the column value; if the value is SQL <code>NULL</code>, the
557 value returned is <code>null</code>
559 function TZMySQLResultSet.GetBigDecimal(ColumnIndex: Integer): Extended;
561 {$IFNDEF DISABLE_CHECKING}
562 CheckColumnConvertion(ColumnIndex, stBigDecimal);
564 Result := SQLStrToFloatDef(InternalGetString(ColumnIndex), 0);
568 Gets the value of the designated column in the current row
569 of this <code>ResultSet</code> object as
570 a <code>byte</code> array in the Java programming language.
571 The bytes represent the raw values returned by the driver.
573 @param columnIndex the first column is 1, the second is 2, ...
574 @return the column value; if the value is SQL <code>NULL</code>, the
575 value returned is <code>null</code>
577 function TZMySQLResultSet.GetBytes(ColumnIndex: Integer): TByteDynArray;
579 {$IFNDEF DISABLE_CHECKING}
580 CheckColumnConvertion(ColumnIndex, stBytes);
582 Result := StrToBytes(InternalGetString(ColumnIndex));
586 Gets the value of the designated column in the current row
587 of this <code>ResultSet</code> object as
588 a <code>java.sql.Date</code> object in the Java programming language.
590 @param columnIndex the first column is 1, the second is 2, ...
591 @return the column value; if the value is SQL <code>NULL</code>, the
592 value returned is <code>null</code>
594 function TZMySQLResultSet.GetDate(ColumnIndex: Integer): TDateTime;
598 {$IFNDEF DISABLE_CHECKING}
599 CheckColumnConvertion(ColumnIndex, stDate);
601 Value := String(InternalGetString(ColumnIndex));
603 LastWasNull := (LastWasNull or (Copy(Value, 1, 10)='0000-00-00'));
610 if IsMatch('????-??-??*', Value) then
611 Result := Trunc(AnsiSQLDateToDateTime(Value))
613 Result := Trunc(TimestampStrToDateTime(Value));
614 LastWasNull := Result = 0;
618 Gets the value of the designated column in the current row
619 of this <code>ResultSet</code> object as
620 a <code>java.sql.Time</code> object in the Java programming language.
622 @param columnIndex the first column is 1, the second is 2, ...
623 @return the column value; if the value is SQL <code>NULL</code>, the
624 value returned is <code>null</code>
626 function TZMySQLResultSet.GetTime(ColumnIndex: Integer): TDateTime;
630 {$IFNDEF DISABLE_CHECKING}
631 CheckColumnConvertion(ColumnIndex, stTime);
633 Value := String(InternalGetString(ColumnIndex));
641 if IsMatch('*??:??:??*', Value) then
642 Result := Frac(AnsiSQLDateToDateTime(Value))
644 Result := Frac(TimestampStrToDateTime(Value));
648 Gets the value of the designated column in the current row
649 of this <code>ResultSet</code> object as
650 a <code>java.sql.Timestamp</code> object in the Java programming language.
652 @param columnIndex the first column is 1, the second is 2, ...
653 @return the column value; if the value is SQL <code>NULL</code>, the
654 value returned is <code>null</code>
655 @exception SQLException if a database access error occurs
657 function TZMySQLResultSet.GetTimestamp(ColumnIndex: Integer): TDateTime;
661 {$IFNDEF DISABLE_CHECKING}
662 CheckColumnConvertion(ColumnIndex, stTimestamp);
664 Temp := String(GetPChar(ColumnIndex));
672 if IsMatch('????-??-??*', Temp) or IsMatch('??:??:??*', Temp) then
673 Result := AnsiSQLDateToDateTime(Temp)
675 Result := TimestampStrToDateTime(Temp);
676 LastWasNull := Result = 0;
680 Returns the value of the designated column in the current row
681 of this <code>ResultSet</code> object as a <code>Blob</code> object
682 in the Java programming language.
684 @param ColumnIndex the first column is 1, the second is 2, ...
685 @return a <code>Blob</code> object representing the SQL <code>BLOB</code> value in
688 function TZMySQLResultSet.GetBlob(ColumnIndex: Integer): IZBlob;
692 {$IFNDEF DISABLE_CHECKING}
693 CheckBlobColumn(ColumnIndex);
697 if not IsNull(ColumnIndex) then
699 case GetMetaData.GetColumnType(ColumnIndex) of
700 stAsciiStream: Stream := TStringStream.Create(GetValidatedAnsiString(InternalGetString(ColumnIndex), ConSettings, True));
701 stUnicodeStream: Stream := GetValidatedUnicodeStream(InternalGetString(ColumnIndex), ConSettings, True);
703 Stream := TStringStream.Create(InternalGetString(ColumnIndex));
705 if not Assigned(Stream) then //improve TZTestCompMySQLBugReport.Test1045286
706 Stream := TMemoryStream.Create;
707 Result := TZAbstractBlob.CreateWithStream(Stream, GetStatement.GetConnection,
708 GetMetaData.GetColumnType(ColumnIndex) = stUnicodeStream);
711 Result := TZAbstractBlob.CreateWithStream(nil, GetStatement.GetConnection);
713 if Assigned(Stream) then
719 Moves the cursor to the given row number in
720 this <code>ResultSet</code> object.
722 <p>If the row number is positive, the cursor moves to
723 the given row number with respect to the
724 beginning of the result set. The first row is row 1, the second
727 <p>If the given row number is negative, the cursor moves to
728 an absolute row position with respect to
729 the end of the result set. For example, calling the method
730 <code>absolute(-1)</code> positions the
731 cursor on the last row; calling the method <code>absolute(-2)</code>
732 moves the cursor to the next-to-last row, and so on.
734 <p>An attempt to position the cursor beyond the first/last row in
735 the result set leaves the cursor before the first row or after
738 <p><B>Note:</B> Calling <code>absolute(1)</code> is the same
739 as calling <code>first()</code>. Calling <code>absolute(-1)</code>
740 is the same as calling <code>last()</code>.
742 @return <code>true</code> if the cursor is on the result set;
743 <code>false</code> otherwise
745 function TZMySQLResultSet.MoveAbsolute(Row: Integer): Boolean;
749 { Checks for maximum row. }
751 if (MaxRows > 0) and (Row > MaxRows) then
754 if not FUseResult then
756 { Process negative rows. }
759 Row := LastRowNo - Row + 1;
764 if (Row >= 0) and (Row <= LastRowNo + 1) then
767 if (Row >= 1) and (Row <= LastRowNo) then
769 FPlainDriver.SeekData(FQueryHandle, RowNo - 1);
770 FRowHandle := FPlainDriver.FetchRow(FQueryHandle);
775 Result := FRowHandle <> nil;
778 RaiseForwardOnlyException;
782 Moves the cursor down one row from its current position.
783 A <code>ResultSet</code> cursor is initially positioned
784 before the first row; the first call to the method
785 <code>next</code> makes the first row the current row; the
786 second call makes the second row the current row, and so on.
788 <P>If an input stream is open for the current row, a call
789 to the method <code>next</code> will
790 implicitly close it. A <code>ResultSet</code> object's
791 warning chain is cleared when a new row is read.
793 @return <code>true</code> if the new current row is valid;
794 <code>false</code> if there are no more rows
796 function TZMySQLResultSet.Next: Boolean;
798 { Checks for maximum row. }
800 if (MaxRows > 0) and (RowNo >= MaxRows) then
802 if Assigned(FQueryHandle) then
803 FRowHandle := FPlainDriver.FetchRow(FQueryHandle);
804 if FRowHandle <> nil then
807 if LastRowNo < RowNo then
813 if RowNo <= LastRowNo then
814 RowNo := LastRowNo + 1;
819 procedure TZMySQLResultSet.ReleaseHandle;
821 if FQueryHandle <> nil then
822 FPlainDriver.FreeResult(FQueryHandle);
825 { TZMySQLPreparedResultSet }
828 Constructs this object, assignes main properties and
829 opens the record set.
830 @param PlainDriver a native MySQL plain driver.
831 @param Statement a related SQL statement object.
832 @param Handle a MySQL specific query handle.
833 @param UseResult <code>True</code> to use results,
834 <code>False</code> to store result.
836 constructor TZMySQLPreparedResultSet.Create(PlainDriver: IZMySQLPlainDriver;
837 Statement: IZStatement; SQL: string; Handle: PZMySQLConnect;
840 tempPrepStmt : IZMysqlPreparedStatement;
842 inherited Create(Statement, SQL, TZMySQLResultSetMetadata.Create(
843 Statement.GetConnection.GetMetadata, SQL, Self),
844 Statement.GetConnection.GetConSettings);
847 tempPrepStmt := Statement as IZMysqlPreparedStatement;
848 FPrepStmt:= tempPrepStmt.GetStmtHandle;
849 FResultMetaData := nil;
850 FPlainDriver := PlainDriver;
851 ResultSetConcurrency := rcReadOnly;
852 FUseResult := UseResult;
858 Destroys this object and cleanups the memory.
860 destructor TZMySQLPreparedResultSet.Destroy;
866 Opens this recordset.
868 procedure TZMySQLPreparedResultSet.Open;
869 const one = AnsiString('1');
872 ColumnInfo: TZColumnInfo;
873 FieldHandle: PZMySQLField;
876 if ResultSetConcurrency = rcUpdatable then
877 raise EZSQLException.Create(SLiveResultSetsAreNotSupported);
879 FieldCount := FPlainDriver.GetPreparedFieldCount(FPrepStmt);
880 if FieldCount = 0 then
881 raise EZSQLException.Create(SCanNotRetrieveResultSetData);
883 FResultMetaData := FPlainDriver.GetPreparedMetaData(FPrepStmt);
884 if not Assigned(FResultMetaData) then
885 raise EZSQLException.Create(SCanNotRetrieveResultSetData);
891 FPlainDriver.StmtAttrSet(FPrepStmt,STMT_ATTR_UPDATE_MAX_LENGTH,PAnsiChar(one));
892 if (FPlainDriver.StorePreparedResult(FPrepStmt)=0) then
893 LastRowNo := FPlainDriver.GetPreparedNumRows(FPrepStmt)
898 { Initialize Bind Array and Column Array }
899 FBindBuffer := TZMySqlResultSetBindBuffer.Create(FPlainDriver,FieldCount,FColumnArray);
901 { Fills the column info. }
903 for I := 0 to FPlainDriver.GetFieldCount(FResultMetaData) - 1 do
905 FPlainDriver.SeekField(FResultMetaData, I);
906 FieldHandle := FPlainDriver.FetchField(FResultMetaData);
907 if FieldHandle = nil then
910 ColumnInfo := GetMySQLColumnInfoFromFieldHandle(FPlainDriver,
911 FieldHandle, GetStatement.GetConnection.GetConSettings, FUseResult);
913 ColumnsInfo.Add(ColumnInfo);
915 FBindBuffer.AddColumn(FPlainDriver, FieldHandle);
917 FPlainDriver.FreeResult(FResultMetaData);
918 FResultMetaData := nil;
920 if (FPlainDriver.BindResult(FPrepStmt,FBindBuffer.GetBufferAddress)<>0) then
921 raise EZSQLException.Create(SFailedToBindResults);
927 Releases this <code>ResultSet</code> object's database and
928 JDBC resources immediately instead of waiting for
929 this to happen when it is automatically closed.
931 <P><B>Note:</B> A <code>ResultSet</code> object
932 is automatically closed by the
933 <code>Statement</code> object that generated it when
934 that <code>Statement</code> object is closed,
935 re-executed, or is used to retrieve the next result from a
936 sequence of multiple results. A <code>ResultSet</code> object
937 is also automatically closed when it is garbage collected.
939 procedure TZMySQLPreparedResultSet.Close;
941 if Assigned(FResultMetaData) then
942 FPlainDriver.FreeResult(FResultMetaData);
943 FResultMetaData := nil;
944 if Assigned(FBindBuffer) then
945 FreeAndNil(FBindBuffer);
946 if Assigned(FPrepStmt) then
948 FPlainDriver.FreePreparedResult(FPrepStmt);
949 while(FPlainDriver.GetPreparedNextResult(FPrepStmt) = 0) do
950 FPlainDriver.FreePreparedResult(FPrepStmt);
957 Indicates if the value of the designated column in the current row
958 of this <code>ResultSet</code> object is Null.
960 @param columnIndex the first column is 1, the second is 2, ...
961 @return if the value is SQL <code>NULL</code>, the
962 value returned is <code>true</code>. <code>false</code> otherwise.
964 function TZMySQLPreparedResultSet.IsNull(ColumnIndex: Integer): Boolean;
966 {$IFNDEF DISABLE_CHECKING}
970 Result := FColumnArray[ColumnIndex-1].is_null =1;
974 Gets the value of the designated column in the current row
975 of this <code>ResultSet</code> object as
976 a <code>String</code> in the Java programming language.
978 @param columnIndex the first column is 1, the second is 2, ...
979 @return the column value; if the value is SQL <code>NULL</code>, the
980 value returned is <code>null</code>
982 function TZMySQLPreparedResultSet.InternalGetString(ColumnIndex: Integer): RawByteString;
984 {$IFNDEF DISABLE_CHECKING}
987 Result := PAnsiChar(FColumnArray[ColumnIndex - 1].buffer);
988 LastWasNull := FColumnArray[ColumnIndex-1].is_null =1;
992 Gets the value of the designated column in the current row
993 of this <code>ResultSet</code> object as
994 a <code>boolean</code> in the Java programming language.
996 @param columnIndex the first column is 1, the second is 2, ...
997 @return the column value; if the value is SQL <code>NULL</code>, the
998 value returned is <code>false</code>
1000 function TZMySQLPreparedResultSet.GetBoolean(ColumnIndex: Integer): Boolean;
1004 {$IFNDEF DISABLE_CHECKING}
1005 CheckColumnConvertion(ColumnIndex, stBoolean);
1007 Temp := UpperCase(String(InternalGetString(ColumnIndex)));
1008 Result := (Temp = 'Y') or (Temp = 'YES') or (Temp = 'T') or
1009 (Temp = 'TRUE') or (StrToIntDef(Temp, 0) <> 0);
1013 Gets the value of the designated column in the current row
1014 of this <code>ResultSet</code> object as
1015 a <code>byte</code> in the Java programming language.
1017 @param columnIndex the first column is 1, the second is 2, ...
1018 @return the column value; if the value is SQL <code>NULL</code>, the
1019 value returned is <code>0</code>
1021 function TZMySQLPreparedResultSet.GetByte(ColumnIndex: Integer): Byte;
1023 {$IFNDEF DISABLE_CHECKING}
1024 CheckColumnConvertion(ColumnIndex, stByte);
1026 Result := Byte(bufferasInt64(ColumnIndex));
1027 LastWasNull := FColumnArray[ColumnIndex-1].is_null =1;
1031 Gets the value of the designated column in the current row
1032 of this <code>ResultSet</code> object as
1033 a <code>short</code> in the Java programming language.
1035 @param columnIndex the first column is 1, the second is 2, ...
1036 @return the column value; if the value is SQL <code>NULL</code>, the
1037 value returned is <code>0</code>
1039 function TZMySQLPreparedResultSet.GetShort(ColumnIndex: Integer): SmallInt;
1041 {$IFNDEF DISABLE_CHECKING}
1042 CheckColumnConvertion(ColumnIndex, stShort);
1044 Result := Integer(bufferasInt64(ColumnIndex));
1045 LastWasNull := FColumnArray[ColumnIndex-1].is_null =1;
1049 Gets the value of the designated column in the current row
1050 of this <code>ResultSet</code> object as
1051 an <code>int</code> in the Java programming language.
1053 @param columnIndex the first column is 1, the second is 2, ...
1054 @return the column value; if the value is SQL <code>NULL</code>, the
1055 value returned is <code>0</code>
1057 function TZMySQLPreparedResultSet.GetInt(ColumnIndex: Integer): Integer;
1059 {$IFNDEF DISABLE_CHECKING}
1060 CheckColumnConvertion(ColumnIndex, stInteger);
1062 Result := bufferasInt64(ColumnIndex);
1063 LastWasNull := FColumnArray[ColumnIndex-1].is_null =1;
1067 Gets the value of the designated column in the current row
1068 of this <code>ResultSet</code> object as
1069 a <code>long</code> in the Java programming language.
1071 @param columnIndex the first column is 1, the second is 2, ...
1072 @return the column value; if the value is SQL <code>NULL</code>, the
1073 value returned is <code>0</code>
1075 function TZMySQLPreparedResultSet.GetLong(ColumnIndex: Integer): Int64;
1077 {$IFNDEF DISABLE_CHECKING}
1078 CheckColumnConvertion(ColumnIndex, stLong);
1080 Result := bufferasInt64(ColumnIndex);
1081 LastWasNull := FColumnArray[ColumnIndex-1].is_null =1;
1085 Gets the value of the designated column in the current row
1086 of this <code>ResultSet</code> object as
1087 a <code>float</code> in the Java programming language.
1089 @param columnIndex the first column is 1, the second is 2, ...
1090 @return the column value; if the value is SQL <code>NULL</code>, the
1091 value returned is <code>0</code>
1093 function TZMySQLPreparedResultSet.GetFloat(ColumnIndex: Integer): Single;
1095 {$IFNDEF DISABLE_CHECKING}
1096 CheckColumnConvertion(ColumnIndex, stFloat);
1098 Result := BufferAsExtended(ColumnIndex);
1099 LastWasNull := FColumnArray[ColumnIndex-1].is_null =1;
1103 Gets the value of the designated column in the current row
1104 of this <code>ResultSet</code> object as
1105 a <code>double</code> in the Java programming language.
1107 @param columnIndex the first column is 1, the second is 2, ...
1108 @return the column value; if the value is SQL <code>NULL</code>, the
1109 value returned is <code>0</code>
1111 function TZMySQLPreparedResultSet.GetDouble(ColumnIndex: Integer): Double;
1113 {$IFNDEF DISABLE_CHECKING}
1114 CheckColumnConvertion(ColumnIndex, stDouble);
1116 Result := BufferAsExtended(ColumnIndex);
1117 LastWasNull := FColumnArray[ColumnIndex-1].is_null =1;
1121 Gets the value of the designated column in the current row
1122 of this <code>ResultSet</code> object as
1123 a <code>java.sql.BigDecimal</code> in the Java programming language.
1125 @param columnIndex the first column is 1, the second is 2, ...
1126 @param scale the number of digits to the right of the decimal point
1127 @return the column value; if the value is SQL <code>NULL</code>, the
1128 value returned is <code>null</code>
1130 function TZMySQLPreparedResultSet.GetBigDecimal(ColumnIndex: Integer): Extended;
1132 {$IFNDEF DISABLE_CHECKING}
1133 CheckColumnConvertion(ColumnIndex, stBigDecimal);
1135 Result := BufferAsExtended(ColumnIndex);
1136 LastWasNull := FColumnArray[ColumnIndex-1].is_null =1;
1140 Gets the value of the designated column in the current row
1141 of this <code>ResultSet</code> object as
1142 a <code>byte</code> array in the Java programming language.
1143 The bytes represent the raw values returned by the driver.
1145 @param columnIndex the first column is 1, the second is 2, ...
1146 @return the column value; if the value is SQL <code>NULL</code>, the
1147 value returned is <code>null</code>
1149 function TZMySQLPreparedResultSet.GetBytes(ColumnIndex: Integer): TByteDynArray;
1151 {$IFNDEF DISABLE_CHECKING}
1152 CheckColumnConvertion(ColumnIndex, stBytes);
1154 Result := StrToBytes(InternalGetString(ColumnIndex));
1155 LastWasNull := FColumnArray[ColumnIndex-1].is_null =1;
1159 Gets the value of the designated column in the current row
1160 of this <code>ResultSet</code> object as
1161 a <code>java.sql.Date</code> object in the Java programming language.
1163 @param columnIndex the first column is 1, the second is 2, ...
1164 @return the column value; if the value is SQL <code>NULL</code>, the
1165 value returned is <code>null</code>
1167 function TZMySQLPreparedResultSet.GetDate(ColumnIndex: Integer): TDateTime;
1169 {$IFNDEF DISABLE_CHECKING}
1170 CheckColumnConvertion(ColumnIndex, stDate);
1172 if not sysUtils.TryEncodeDate(PMYSQL_TIME(FColumnArray[ColumnIndex - 1].buffer)^.Year,
1173 PMYSQL_TIME(FColumnArray[ColumnIndex - 1].buffer)^.Month,
1174 PMYSQL_TIME(FColumnArray[ColumnIndex - 1].buffer)^.Day,
1176 Result := encodeDate(1900, 1, 1);
1177 LastWasNull := FColumnArray[ColumnIndex-1].is_null =1;
1181 Gets the value of the designated column in the current row
1182 of this <code>ResultSet</code> object as
1183 a <code>java.sql.Time</code> object in the Java programming language.
1185 @param columnIndex the first column is 1, the second is 2, ...
1186 @return the column value; if the value is SQL <code>NULL</code>, the
1187 value returned is <code>null</code>
1189 function TZMySQLPreparedResultSet.GetTime(ColumnIndex: Integer): TDateTime;
1191 {$IFNDEF DISABLE_CHECKING}
1192 CheckColumnConvertion(ColumnIndex, stTime);
1194 if not sysUtils.TryEncodeTime(PMYSQL_TIME(FColumnArray[ColumnIndex - 1].buffer)^.Hour,
1195 PMYSQL_TIME(FColumnArray[ColumnIndex - 1].buffer)^.Minute,
1196 PMYSQL_TIME(FColumnArray[ColumnIndex - 1].buffer)^.Second,
1199 Result := encodeTime(0,0,0,0);
1200 LastWasNull := FColumnArray[ColumnIndex-1].is_null =1;
1204 Gets the value of the designated column in the current row
1205 of this <code>ResultSet</code> object as
1206 a <code>java.sql.Timestamp</code> object in the Java programming language.
1208 @param columnIndex the first column is 1, the second is 2, ...
1209 @return the column value; if the value is SQL <code>NULL</code>, the
1210 value returned is <code>null</code>
1211 @exception SQLException if a database access error occurs
1213 function TZMySQLPreparedResultSet.GetTimestamp(ColumnIndex: Integer): TDateTime;
1215 {$IFNDEF DISABLE_CHECKING}
1216 CheckColumnConvertion(ColumnIndex, stTimestamp);
1218 Result := GetDate(ColumnIndex) + GetTime(ColumnIndex);
1219 LastWasNull := FColumnArray[ColumnIndex-1].is_null =1;
1223 Gets the value of the designated column in the current row
1224 of this <code>ResultSet</code> object as
1225 a stream of ASCII characters. The value can then be read in chunks from the
1226 stream. This method is particularly
1227 suitable for retrieving large <char>LONGVARCHAR</char> values.
1228 The JDBC driver will
1229 do any necessary conversion from the database format into ASCII.
1231 <P><B>Note:</B> All the data in the returned stream must be
1232 read prior to getting the value of any other column. The next
1233 call to a <code>getXXX</code> method implicitly closes the stream. Also, a
1234 stream may return <code>0</code> when the method
1235 <code>InputStream.available</code>
1236 is called whether there is data available or not.
1238 @param columnIndex the first column is 1, the second is 2, ...
1239 @return a Java input stream that delivers the database column value
1240 as a stream of one-byte ASCII characters; if the value is SQL
1241 <code>NULL</code>, the value returned is <code>null</code>
1243 function TZMySQLPreparedResultSet.GetAsciiStream(ColumnIndex: Integer): TStream;
1245 {$IFNDEF DISABLE_CHECKING}
1246 CheckColumnConvertion(ColumnIndex, stAsciiStream);
1248 Result := TStringStream.Create(InternalGetString(ColumnIndex));
1249 LastWasNull := FColumnArray[ColumnIndex-1].is_null =1;
1253 Gets the value of a column in the current row as a stream of
1254 Gets the value of the designated column in the current row
1255 of this <code>ResultSet</code> object as
1256 as a stream of Unicode characters.
1257 The value can then be read in chunks from the
1258 stream. This method is particularly
1259 suitable for retrieving large<code>LONGVARCHAR</code>values. The JDBC driver will
1260 do any necessary conversion from the database format into Unicode.
1261 The byte format of the Unicode stream must be Java UTF-8,
1262 as specified in the Java virtual machine specification.
1264 <P><B>Note:</B> All the data in the returned stream must be
1265 read prior to getting the value of any other column. The next
1266 call to a <code>getXXX</code> method implicitly closes the stream. Also, a
1267 stream may return <code>0</code> when the method
1268 <code>InputStream.available</code>
1269 is called whether there is data available or not.
1271 @param columnIndex the first column is 1, the second is 2, ...
1272 @return a Java input stream that delivers the database column value
1273 as a stream in Java UTF-8 byte format; if the value is SQL
1274 <code>NULL</code>, the value returned is <code>null</code>
1276 function TZMySQLPreparedResultSet.GetUnicodeStream(ColumnIndex: Integer): TStream;
1280 {$IFNDEF DISABLE_CHECKING}
1281 CheckColumnConvertion(ColumnIndex, stUnicodeStream);
1283 WS := ZDbcUnicodeString(InternalGetString(ColumnIndex));
1284 Result := TMemoryStream.Create;
1285 Result.Write(PWideChar(WS)^, Length(WS) *2);
1286 Result.Position := 0;
1287 LastWasNull := FColumnArray[ColumnIndex-1].is_null =1;
1291 Gets the value of a column in the current row as a stream of
1292 Gets the value of the designated column in the current row
1293 of this <code>ResultSet</code> object as a binary stream of
1294 uninterpreted bytes. The value can then be read in chunks from the
1295 stream. This method is particularly
1296 suitable for retrieving large <code>LONGVARBINARY</code> values.
1298 <P><B>Note:</B> All the data in the returned stream must be
1299 read prior to getting the value of any other column. The next
1300 call to a <code>getXXX</code> method implicitly closes the stream. Also, a
1301 stream may return <code>0</code> when the method
1302 <code>InputStream.available</code>
1303 is called whether there is data available or not.
1305 @param columnIndex the first column is 1, the second is 2, ...
1306 @return a Java input stream that delivers the database column value
1307 as a stream of uninterpreted bytes;
1308 if the value is SQL <code>NULL</code>, the value returned is <code>null</code>
1310 function TZMySQLPreparedResultSet.GetBinaryStream(ColumnIndex: Integer): TStream;
1312 {$IFNDEF DISABLE_CHECKING}
1313 CheckColumnConvertion(ColumnIndex, stBinaryStream);
1315 Result := TMemoryStream.Create;
1316 Result.Write(FColumnArray[ColumnIndex - 1].buffer[0], FColumnArray[ColumnIndex - 1].length);
1317 Result.Position := 0;
1318 LastWasNull := FColumnArray[ColumnIndex-1].is_null =1;
1322 Returns the value of the designated column in the current row
1323 of this <code>ResultSet</code> object as a <code>Blob</code> object
1324 in the Java programming language.
1326 @param ColumnIndex the first column is 1, the second is 2, ...
1327 @return a <code>Blob</code> object representing the SQL <code>BLOB</code> value in
1328 the specified column
1330 function TZMySQLPreparedResultSet.GetBlob(ColumnIndex: Integer): IZBlob;
1335 {$IFNDEF DISABLE_CHECKING}
1336 CheckBlobColumn(ColumnIndex);
1339 LastWasNull := IsNull(ColumnIndex);
1345 if not LastWasNull then
1347 case GetMetadata.GetColumnType(ColumnIndex) of
1348 stBinaryStream: Stream := GetBinaryStream(ColumnIndex);
1349 stUnicodeStream: Stream := GetUnicodeStream(ColumnIndex);
1351 Stream := TStringStream.Create(GetValidatedAnsiString(InternalGetString(ColumnIndex), ConSettings, True));
1353 Result := TZAbstractBlob.CreateWithStream(Stream, GetStatement.GetConnection,
1354 GetMetadata.GetColumnType(ColumnIndex) = stUnicodeStream)
1357 Result := TZAbstractBlob.CreateWithStream(nil, GetStatement.GetConnection);
1359 if Assigned(Stream) then
1364 function TZMySQLPreparedResultSet.bufferasint64(ColumnIndex: Integer): Int64;
1366 //http://dev.mysql.com/doc/refman/5.1/de/numeric-types.html
1367 if FBindBuffer.GetBufferIsSigned(ColumnIndex) then
1368 Case FBindBuffer.GetBufferType(ColumnIndex) of
1369 FIELD_TYPE_DECIMAL: Result := 0;
1370 FIELD_TYPE_TINY: Result := PByte(FColumnArray[ColumnIndex-1].buffer)^;
1371 FIELD_TYPE_SHORT: Result := PWord(FColumnArray[ColumnIndex-1].buffer)^;
1372 FIELD_TYPE_LONG: Result := PCardinal(FColumnArray[ColumnIndex-1].buffer)^;
1373 FIELD_TYPE_FLOAT: Result := 0;
1374 FIELD_TYPE_DOUBLE: Result := 0;
1375 FIELD_TYPE_NULL: Result := 0;
1376 FIELD_TYPE_TIMESTAMP: Result := 0;
1377 FIELD_TYPE_LONGLONG: Result := PULongLong(FColumnArray[ColumnIndex-1].buffer)^;
1378 FIELD_TYPE_INT24: Result := PCardinal(FColumnArray[ColumnIndex-1].buffer)^;
1379 FIELD_TYPE_BIT: Result := PByte(FColumnArray[ColumnIndex-1].buffer)^;
1380 (*FIELD_TYPE_DATE = 10,
1381 FIELD_TYPE_TIME = 11,
1382 FIELD_TYPE_DATETIME = 12,*)
1383 FIELD_TYPE_YEAR: Result := PWord(FColumnArray[ColumnIndex-1].buffer)^;
1384 (*FIELD_TYPE_NEWDATE = 14,
1385 FIELD_TYPE_VARCHAR = 15, //<--ADDED by fduenas 20-06-2006
1387 FIELD_TYPE_NEWDECIMAL = 246, //<--ADDED by fduenas 20-06-2006
1388 FIELD_TYPE_ENUM = 247,
1389 FIELD_TYPE_SET = 248,
1390 FIELD_TYPE_TINY_BLOB,
1391 FIELD_TYPE_MEDIUM_BLOB,
1392 FIELD_TYPE_LONG_BLOB,
1393 FIELD_TYPE_BLOB: Result := 0;
1394 FIELD_TYPE_VAR_STRING = 253,
1395 FIELD_TYPE_STRING: = 254,
1396 FIELD_TYPE_GEOMETRY: = 255*)
1400 Case FBindBuffer.GetBufferType(ColumnIndex) of
1401 FIELD_TYPE_DECIMAL: Result := 0;
1402 FIELD_TYPE_TINY: Result := PShortInt(FColumnArray[ColumnIndex-1].buffer)^;
1403 FIELD_TYPE_SHORT: Result := PSmallInt(FColumnArray[ColumnIndex-1].buffer)^;
1404 FIELD_TYPE_LONG: Result := PInteger(FColumnArray[ColumnIndex-1].buffer)^;
1405 FIELD_TYPE_FLOAT: Result := 0;
1406 FIELD_TYPE_DOUBLE: Result := 0;
1407 FIELD_TYPE_NULL: Result := 0;
1408 FIELD_TYPE_TIMESTAMP: Result := 0;
1409 FIELD_TYPE_LONGLONG: Result := PInt64(FColumnArray[ColumnIndex-1].buffer)^;
1410 FIELD_TYPE_INT24: Result := PInteger(FColumnArray[ColumnIndex-1].buffer)^;
1411 FIELD_TYPE_BIT: Result := PShortInt(FColumnArray[ColumnIndex-1].buffer)^;
1412 (*FIELD_TYPE_DATE = 10,
1413 FIELD_TYPE_TIME = 11,
1414 FIELD_TYPE_DATETIME = 12, *)
1415 FIELD_TYPE_YEAR: Result := PSmallInt(FColumnArray[ColumnIndex-1].buffer)^;
1416 (*FIELD_TYPE_NEWDATE = 14,
1417 FIELD_TYPE_VARCHAR = 15, //<--ADDED by fduenas 20-06-2006
1418 FIELD_TYPE_NEWDECIMAL = 246, //<--ADDED by fduenas 20-06-2006
1419 FIELD_TYPE_ENUM = 247,
1420 FIELD_TYPE_SET = 248,
1421 FIELD_TYPE_TINY_BLOB,
1422 FIELD_TYPE_MEDIUM_BLOB,
1423 FIELD_TYPE_LONG_BLOB,
1424 FIELD_TYPE_BLOB: Result := 0;
1425 FIELD_TYPE_VAR_STRING = 253,
1426 FIELD_TYPE_STRING: = 254,
1427 FIELD_TYPE_GEOMETRY: = 255*)
1432 function TZMySQLPreparedResultSet.bufferasextended(ColumnIndex: Integer): Extended;
1434 Case FBindBuffer.GetBufferType(ColumnIndex) of
1435 FIELD_TYPE_FLOAT: Result := psingle(FColumnArray[ColumnIndex-1].buffer)^;
1436 FIELD_TYPE_DOUBLE: Result := pdouble(FColumnArray[ColumnIndex-1].buffer)^;
1438 Result := SQLStrToFloatDef(InternalGetString(ColumnIndex), 0);
1443 Moves the cursor to the given row number in
1444 this <code>ResultSet</code> object.
1446 <p>If the row number is positive, the cursor moves to
1447 the given row number with respect to the
1448 beginning of the result set. The first row is row 1, the second
1449 is row 2, and so on.
1451 <p>If the given row number is negative, the cursor moves to
1452 an absolute row position with respect to
1453 the end of the result set. For example, calling the method
1454 <code>absolute(-1)</code> positions the
1455 cursor on the last row; calling the method <code>absolute(-2)</code>
1456 moves the cursor to the next-to-last row, and so on.
1458 <p>An attempt to position the cursor beyond the first/last row in
1459 the result set leaves the cursor before the first row or after
1462 <p><B>Note:</B> Calling <code>absolute(1)</code> is the same
1463 as calling <code>first()</code>. Calling <code>absolute(-1)</code>
1464 is the same as calling <code>last()</code>.
1466 @return <code>true</code> if the cursor is on the result set;
1467 <code>false</code> otherwise
1469 function TZMySQLPreparedResultSet.MoveAbsolute(Row: Integer): Boolean;
1473 { Checks for maximum row. }
1475 if (MaxRows > 0) and (Row > MaxRows) then
1478 if not FUseResult then
1480 { Process negative rows. }
1483 Row := LastRowNo - Row + 1;
1488 if (Row >= 0) and (Row <= LastRowNo + 1) then
1491 if (Row >= 1) and (Row <= LastRowNo) then
1493 FPlainDriver.SeekPreparedData(FPrepStmt, RowNo - 1);
1494 Result := (FPlainDriver.FetchBoundResults(FPrepStmt) =0);
1499 RaiseForwardOnlyException;
1503 Moves the cursor down one row from its current position.
1504 A <code>ResultSet</code> cursor is initially positioned
1505 before the first row; the first call to the method
1506 <code>next</code> makes the first row the current row; the
1507 second call makes the second row the current row, and so on.
1509 <P>If an input stream is open for the current row, a call
1510 to the method <code>next</code> will
1511 implicitly close it. A <code>ResultSet</code> object's
1512 warning chain is cleared when a new row is read.
1514 @return <code>true</code> if the new current row is valid;
1515 <code>false</code> if there are no more rows
1517 function TZMySQLPreparedResultSet.Next: Boolean;
1519 { Checks for maximum row. }
1521 if (MaxRows > 0) and (RowNo >= MaxRows) then
1524 if FPlainDriver.FetchBoundResults(FPrepStmt) in [0, MYSQL_DATA_TRUNCATED] then
1527 if LastRowNo < RowNo then
1533 if RowNo <= LastRowNo then
1534 RowNo := LastRowNo + 1;
1539 { TZMySQLCachedResolver }
1542 Creates a MySQL specific cached resolver object.
1543 @param PlainDriver a native MySQL plain driver.
1544 @param Handle a MySQL specific query handle.
1545 @param Statement a related SQL statement object.
1546 @param Metadata a resultset metadata reference.
1548 constructor TZMySQLCachedResolver.Create(PlainDriver: IZMySQLPlainDriver;
1549 Handle: PZMySQLConnect; Statement: IZMysqlStatement; Metadata: IZResultSetMetadata);
1553 inherited Create(Statement, Metadata);
1554 FPlainDriver := PlainDriver;
1556 FStatement := Statement as IZMysqlStatement;
1558 { Defines an index of autoincrement field. }
1559 FAutoColumnIndex := 0;
1560 for I := 1 to Metadata.GetColumnCount do
1562 if Metadata.IsAutoIncrement(I) and
1563 (Metadata.GetColumnType(I) in [stByte, stShort, stInteger, stLong]) then
1565 FAutoColumnIndex := I;
1572 Forms a where clause for UPDATE or DELETE DML statements.
1573 @param Columns a collection of key columns.
1574 @param OldRowAccessor an accessor object to old column values.
1576 function TZMySQLCachedResolver.FormWhereClause(Columns: TObjectList;
1577 OldRowAccessor: TZRowAccessor): string;
1580 Current: TZResolverParameter;
1583 N := Columns.Count - WhereColumns.Count;
1585 for I := 0 to WhereColumns.Count - 1 do
1587 Current := TZResolverParameter(WhereColumns[I]);
1588 if Result <> '' then
1589 Result := Result + ' AND ';
1591 Result := Result + IdentifierConvertor.Quote(Current.ColumnName);
1592 if OldRowAccessor.IsNull(Current.ColumnIndex) then
1594 if not (Metadata.IsNullable(Current.ColumnIndex) = ntNullable) then
1595 case OldRowAccessor.GetColumnType(Current.ColumnIndex) of
1599 Current := TZResolverParameter(WhereColumns[I-1]);
1600 Result := Result+ '=''0000-00-00'' OR '+Result + ' IS NULL';
1601 Columns.Add(TZResolverParameter.Create(Current.ColumnIndex,
1602 Current.ColumnName, Current.ColumnType, Current.NewValue, ''));
1607 Current := TZResolverParameter(WhereColumns[I-1]);
1608 Result := Result+ '=''00:00:00'' OR '+Result + ' IS NULL';
1609 Columns.Add(TZResolverParameter.Create(Current.ColumnIndex,
1610 Current.ColumnName, Current.ColumnType, Current.NewValue, ''));
1615 Current := TZResolverParameter(WhereColumns[I-1]);
1616 Result := Result+ '=''0000-00-00 00:00:00'' OR '+Result + ' IS NULL';
1617 Columns.Add(TZResolverParameter.Create(Current.ColumnIndex,
1618 Current.ColumnName, Current.ColumnType, Current.NewValue, ''));
1621 Result := Result + ' IS NULL';
1624 Result := Result + ' IS NULL ';
1629 Result := Result + '=?';
1634 if Result <> '' then
1635 Result := ' WHERE ' + Result;
1638 Posts updates to database.
1639 @param Sender a cached result set object.
1640 @param UpdateType a type of updates.
1641 @param OldRowAccessor an accessor object to old column values.
1642 @param NewRowAccessor an accessor object to new column values.
1644 procedure TZMySQLCachedResolver.PostUpdates(Sender: IZCachedResultSet;
1645 UpdateType: TZRowUpdateType; OldRowAccessor, NewRowAccessor: TZRowAccessor);
1647 inherited PostUpdates(Sender, UpdateType, OldRowAccessor, NewRowAccessor);
1648 if (UpdateType = utInserted) then
1650 UpdateAutoIncrementFields(Sender, UpdateType, OldRowAccessor, NewRowAccessor, Self);
1655 Do Tasks after Post updates to database.
1656 @param Sender a cached result set object.
1657 @param UpdateType a type of updates.
1658 @param OldRowAccessor an accessor object to old column values.
1659 @param NewRowAccessor an accessor object to new column values.
1661 procedure TZMySQLCachedResolver.UpdateAutoIncrementFields(
1662 Sender: IZCachedResultSet; UpdateType: TZRowUpdateType; OldRowAccessor,
1663 NewRowAccessor: TZRowAccessor; Resolver: IZCachedResolver);
1665 Plaindriver : IZMysqlPlainDriver;
1667 inherited UpdateAutoIncrementFields(Sender, UpdateType, OldRowAccessor, NewRowAccessor, Resolver);
1668 if not ((FAutoColumnIndex > 0) and
1669 (OldRowAccessor.IsNull(FAutoColumnIndex) or (OldRowAccessor.GetValue(FAutoColumnIndex).VInteger=0))) then
1671 Plaindriver := (Connection as IZMysqlConnection).GetPlainDriver;
1672 // THIS IS WRONG, I KNOW (MDAEMS) : which function to use depends on the insert statement, not the resultset statement
1673 { IF FStatement.IsPreparedStatement then
1674 NewRowAccessor.SetLong(FAutoColumnIndex, PlainDriver.GetPreparedInsertID(FStatement.GetStmtHandle))
1676 NewRowAccessor.SetLong(FAutoColumnIndex, PlainDriver.GetLastInsertID(FHandle));
1681 Forms a where clause for SELECT statements to calculate default values.
1682 @param Columns a collection of key columns.
1683 @param OldRowAccessor an accessor object to old column values.
1685 function TZMySQLCachedResolver.FormCalculateStatement(
1686 Columns: TObjectList): string;
1689 Current: TZResolverParameter;
1692 if Columns.Count = 0 then
1695 for I := 0 to Columns.Count - 1 do
1697 Current := TZResolverParameter(Columns[I]);
1698 if Result <> '' then
1699 Result := Result + ',';
1700 if Current.DefaultValue <> '' then
1701 Result := Result + Current.DefaultValue
1703 Result := Result + 'NULL';
1705 Result := 'SELECT ' + Result;