zeoslib  UNKNOWN
 All Files
ZDbcMySqlResultSet.pas
Go to the documentation of this file.
1 {*********************************************************}
2 { }
3 { Zeos Database Objects }
4 { MySQL Database Connectivity Classes }
5 { }
6 { Originally written by Sergey Seroukhov }
7 { }
8 {*********************************************************}
9 
10 {@********************************************************}
11 { Copyright (c) 1999-2012 Zeos Development Group }
12 { }
13 { License Agreement: }
14 { }
15 { This library is distributed in the hope that it will be }
16 { useful, but WITHOUT ANY WARRANTY; without even the }
17 { implied warranty of MERCHANTABILITY or FITNESS FOR }
18 { A PARTICULAR PURPOSE. See the GNU Lesser General }
19 { Public License for more details. }
20 { }
21 { The source code of the ZEOS Libraries and packages are }
22 { distributed under the Library GNU General Public }
23 { License (see the file COPYING / COPYING.ZEOS) }
24 { with the following modification: }
25 { As a special exception, the copyright holders of this }
26 { library give you permission to link this library with }
27 { independent modules to produce an executable, }
28 { regardless of the license terms of these independent }
29 { modules, and to copy and distribute the resulting }
30 { executable under terms of your choice, provided that }
31 { you also meet, for each linked independent module, }
32 { the terms and conditions of the license of that module. }
33 { An independent module is a module which is not derived }
34 { from or based on this library. If you modify this }
35 { library, you may extend this exception to your version }
36 { of the library, but you are not obligated to do so. }
37 { If you do not wish to do so, delete this exception }
38 { statement from your version. }
39 { }
40 { }
41 { The project web site is located on: }
42 { http://zeos.firmos.at (FORUM) }
43 { http://sourceforge.net/p/zeoslib/tickets/ (BUGTRACKER)}
44 { svn://svn.code.sf.net/p/zeoslib/code-0/trunk (SVN) }
45 { }
46 { http://www.sourceforge.net/projects/zeoslib. }
47 { }
48 { }
49 { Zeos Development Group. }
50 {********************************************************@}
51 
52 unit ZDbcMySqlResultSet;
53 
54 interface
55 
56 {$I ZDbc.inc}
57 
58 uses
59  Classes, {$IFDEF MSEgui}mclasses,{$ENDIF} SysUtils, Types, Contnrs,
60  ZDbcIntfs, ZDbcResultSet, ZDbcResultSetMetadata, ZCompatibility, ZDbcCache,
61  ZDbcCachedResultSet, ZDbcGenericResolver, ZDbcMySqlStatement,
62  ZPlainMySqlDriver, ZPlainMySqlConstants;
63 
64 type
65  {** Implements MySQL ResultSet Metadata. }
66  TZMySQLResultSetMetadata = class(TZAbstractResultSetMetadata)
67  public
68  function GetColumnType(Column: Integer): TZSQLType; override;
69  end;
70 
71  {** Implements MySQL ResultSet. }
72  TZMySQLResultSet = class(TZAbstractResultSet)
73  private
74  FHandle: PZMySQLConnect;
75  FQueryHandle: PZMySQLResult;
76  FRowHandle: PZMySQLRow;
77  FPlainDriver: IZMySQLPlainDriver;
78  FUseResult: Boolean;
79  FIgnoreUseResult: Boolean;
80  TempStr: String;
81  FRawTemp: RawByteString;
82  FMySQLTypes: array of TMysqlFieldTypes;
83  protected
84  procedure Open; override;
85  function InternalGetString(ColumnIndex: Integer): RawByteString; override;
86  public
87  constructor Create(PlainDriver: IZMySQLPlainDriver; Statement: IZStatement;
88  SQL: string; Handle: PZMySQLConnect; UseResult: Boolean;
89  AffectedRows: PInteger; IgnoreUseResult: Boolean = False);
90  destructor Destroy; override;
91 
92  procedure Close; override;
93 
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;
109 
110  function MoveAbsolute(Row: Integer): Boolean; override;
111  function Next: Boolean; override;
112  procedure ReleaseHandle;
113  end;
114 
115  {** Implements Prepared MySQL ResultSet. }
116  TZMySQLPreparedResultSet = class(TZAbstractResultSet)
117  private
118  FHandle: PZMySQLConnect;
119  FPrepStmt: PZMySqlPrepStmt;
120  FResultMetaData : PZMySQLResult;
121  FPlainDriver: IZMySQLPlainDriver;
122  FUseResult: Boolean;
123  FColumnArray: TZMysqlColumnBuffer;
124  FBindBuffer: TZMySqlResultSetBindBuffer;
125  function bufferasint64(ColumnIndex: Integer): Int64;
126  function bufferasextended(ColumnIndex: Integer): Extended;
127 
128  protected
129  function InternalGetString(ColumnIndex: Integer): RawByteString; override;
130  procedure Open; override;
131  public
132  constructor Create(PlainDriver: IZMySQLPlainDriver; Statement: IZStatement;
133  SQL: string; Handle: PZMySQLConnect; UseResult: Boolean);
134  destructor Destroy; override;
135 
136  procedure Close; override;
137 
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;
155 
156  function MoveAbsolute(Row: Integer): Boolean; override;
157  function Next: Boolean; override;
158  end;
159 
160  {** Implements a cached resolver with MySQL specific functionality. }
161  TZMySQLCachedResolver = class (TZGenericCachedResolver, IZCachedResolver)
162  private
163  FHandle: PZMySQLConnect;
164  FPlainDriver: IZMySQLPlainDriver;
165  FAutoColumnIndex: Integer;
166  FStatement: IZMysqlStatement;
167  public
168  constructor Create(PlainDriver: IZMySQLPlainDriver; Handle: PZMySQLConnect;
169  Statement: IZMysqlStatement; Metadata: IZResultSetMetadata);
170 
171  function FormWhereClause(Columns: TObjectList;
172  OldRowAccessor: TZRowAccessor): string; override;
173  procedure PostUpdates(Sender: IZCachedResultSet; UpdateType: TZRowUpdateType;
174  OldRowAccessor, NewRowAccessor: TZRowAccessor); override;
175 
176  // --> ms, 31/10/2005
177  function FormCalculateStatement(Columns: TObjectList): string; override;
178  // <-- ms
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 }
183  end;
184 
185 implementation
186 
187 uses
188  Math, ZMessages, ZDbcMySqlUtils, ZMatchPattern, ZDbcMysql, ZEncoding, ZSysUtils;
189 
190 { TZMySQLResultSetMetadata }
191 
192 {**
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
196 }
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}
199  if not Loaded then
200  LoadColumns;
201  Result := TZColumnInfo(ResultSet.ColumnsInfo[Column - 1]).ColumnType;
202 end;
203 
204 { TZMySQLResultSet }
205 
206 {**
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.
214 }
215 constructor TZMySQLResultSet.Create(PlainDriver: IZMySQLPlainDriver;
216  Statement: IZStatement; SQL: string; Handle: PZMySQLConnect;
217  UseResult: Boolean; AffectedRows: PInteger; IgnoreUseResult: Boolean = False);
218 begin
219  inherited Create(Statement, SQL, TZMySQLResultSetMetadata.Create(
220  Statement.GetConnection.GetMetadata, SQL, Self),
221  Statement.GetConnection.GetConSettings);
222 
223  FHandle := Handle;
224  FQueryHandle := nil;
225  FRowHandle := nil;
226  FPlainDriver := PlainDriver;
227  ResultSetConcurrency := rcReadOnly;
228  FUseResult := UseResult;
229  FIgnoreUseResult := IgnoreUseResult;
230 
231  Open;
232  if Assigned(AffectedRows) then
233  AffectedRows^ := LastRowNo;
234 end;
235 
236 {**
237  Destroys this object and cleanups the memory.
238 }
239 destructor TZMySQLResultSet.Destroy;
240 begin
241  inherited Destroy;
242 end;
243 
244 {**
245  Opens this recordset.
246 }
247 procedure TZMySQLResultSet.Open;
248 var
249  I: Integer;
250  FieldHandle: PZMySQLField;
251 begin
252  if ResultSetConcurrency = rcUpdatable then
253  raise EZSQLException.Create(SLiveResultSetsAreNotSupported);
254 
255  if FUseResult and (not FIgnoreUseResult) then
256  begin
257  FQueryHandle := FPlainDriver.UseResult(FHandle);
258  LastRowNo := 0;
259  end
260  else
261  begin
262  FQueryHandle := FPlainDriver.StoreResult(FHandle);
263  if Assigned(FQueryHandle) then
264  LastRowNo := FPlainDriver.GetRowCount(FQueryHandle)
265  else
266  LastRowNo := 0;
267  end;
268 
269  if not Assigned(FQueryHandle) then
270  raise EZSQLException.Create(SCanNotRetrieveResultSetData);
271 
272  { Fills the column info. }
273  ColumnsInfo.Clear;
274  SetLength(FMySQLTypes, FPlainDriver.GetFieldCount(FQueryHandle));
275  for I := 0 to FPlainDriver.GetFieldCount(FQueryHandle) - 1 do
276  begin
277  FPlainDriver.SeekField(FQueryHandle, I);
278  FieldHandle := FPlainDriver.FetchField(FQueryHandle);
279  FMySQLTypes[i] := PMYSQL_FIELD(FieldHandle)^._type;
280  if FieldHandle = nil then
281  Break;
282 
283  ColumnsInfo.Add(GetMySQLColumnInfoFromFieldHandle(FPlainDriver,
284  FieldHandle, ConSettings, FUseResult));
285  end;
286 
287  inherited Open;
288 end;
289 
290 {**
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.
294 
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.
302 }
303 procedure TZMySQLResultSet.Close;
304 begin
305  if FQueryHandle <> nil then
306  begin
307  FPlainDriver.FreeResult(FQueryHandle);
308  while(FPlainDriver.RetrieveNextRowset(FHandle) = 0) do
309  begin
310  FQueryHandle := FPlainDriver.StoreResult(FHandle);
311  if FQueryHandle <> nil then
312  begin
313  FPlainDriver.FreeResult(FQueryHandle);
314  end;
315  end;
316  end;
317  FQueryHandle := nil;
318  FRowHandle := nil;
319  inherited Close;
320 end;
321 
322 {**
323  Indicates if the value of the designated column in the current row
324  of this <code>ResultSet</code> object is Null.
325 
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.
329 }
330 function TZMySQLResultSet.IsNull(ColumnIndex: Integer): Boolean;
331 var
332  Temp: PAnsiChar;
333 begin
334 {$IFNDEF DISABLE_CHECKING}
335  CheckClosed;
336  if FRowHandle = nil then
337  raise EZSQLException.Create(SRowDataIsNotAvailable);
338 {$ENDIF}
339 
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
344  begin
345  Result := (AnsiSQLDateToDateTime(String(Temp)) = 0)
346  and (TimestampStrToDateTime(String(Temp)) = 0);
347  end;
348 end;
349 
350 {**
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.
354 
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>
358 }
359 function TZMySQLResultSet.GetPChar(ColumnIndex: Integer): PChar;
360 begin
361 {$IFNDEF DISABLE_CHECKING}
362  CheckClosed;
363  if FRowHandle = nil then
364  raise EZSQLException.Create(SRowDataIsNotAvailable);
365 {$ENDIF}
366 
367  TempStr := ZDbcString(FPlainDriver.GetFieldData(FRowHandle, ColumnIndex - 1));
368  Result := PChar(TempStr);
369  LastWasNull := Result = nil;
370 end;
371 
372 {**
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>.
376 
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>
380 }
381 function TZMySQLResultSet.InternalGetString(ColumnIndex: Integer): RawByteString;
382 var
383  LengthPointer: PULong;
384  Length: ULong;
385  Buffer: PAnsiChar;
386 begin
387 {$IFNDEF DISABLE_CHECKING}
388  CheckClosed;
389  if FRowHandle = nil then
390  raise EZSQLException.Create(SRowDataIsNotAvailable);
391 {$ENDIF}
392 
393  ColumnIndex := ColumnIndex - 1;
394  LengthPointer := FPlainDriver.FetchLengths(FQueryHandle);
395  if LengthPointer <> nil then
396  Length := PULong(NativeUint(LengthPointer) + NativeUInt(ColumnIndex) * SizeOf(ULOng))^
397  else
398  Length := 0;
399  Buffer := FPlainDriver.GetFieldData(FRowHandle, ColumnIndex);
400  LastWasNull := Buffer = nil;
401  Result := '';
402  if not LastWasNull then
403  {$IFDEF WITH_RAWBYTESTRING}
404  begin
405  SetLength(Result, Length);
406  Move(Buffer^, PAnsiChar(Result)^, Length);
407  end;
408  {$ELSE}
409  SetString(Result, Buffer, Length);
410  {$ENDIF}
411 end;
412 
413 {**
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.
417 
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>
421 }
422 function TZMySQLResultSet.GetBoolean(ColumnIndex: Integer): Boolean;
423 var
424  Temp: string;
425 begin
426 {$IFNDEF DISABLE_CHECKING}
427  CheckColumnConvertion(ColumnIndex, stBoolean);
428 {$ENDIF}
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);
432 end;
433 
434 {**
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.
438 
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>
442 }
443 function TZMySQLResultSet.GetByte(ColumnIndex: Integer): Byte;
444 begin
445 {$IFNDEF DISABLE_CHECKING}
446  CheckColumnConvertion(ColumnIndex, stByte);
447 {$ENDIF}
448  FRawTemp := InternalGetString(ColumnIndex);
449  if LastWasNull then
450  Result := 0
451  else
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));
459  end
460  else
461  Result := StrToIntDef(String(FRawTemp), 0);
462 end;
463 
464 {**
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.
468 
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>
472 }
473 function TZMySQLResultSet.GetShort(ColumnIndex: Integer): SmallInt;
474 begin
475 {$IFNDEF DISABLE_CHECKING}
476  CheckColumnConvertion(ColumnIndex, stShort);
477 {$ENDIF}
478  Result := SmallInt(StrToIntDef(String(InternalGetString(ColumnIndex)), 0));
479 end;
480 
481 {**
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.
485 
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>
489 }
490 function TZMySQLResultSet.GetInt(ColumnIndex: Integer): Integer;
491 begin
492 {$IFNDEF DISABLE_CHECKING}
493  CheckColumnConvertion(ColumnIndex, stInteger);
494 {$ENDIF}
495  Result := StrToIntDef(String(InternalGetString(ColumnIndex)), 0);
496 end;
497 
498 {**
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.
502 
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>
506 }
507 function TZMySQLResultSet.GetLong(ColumnIndex: Integer): Int64;
508 begin
509 {$IFNDEF DISABLE_CHECKING}
510  CheckColumnConvertion(ColumnIndex, stLong);
511 {$ENDIF}
512  Result := StrToInt64Def(String(InternalGetString(ColumnIndex)), 0);
513 end;
514 
515 {**
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.
519 
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>
523 }
524 function TZMySQLResultSet.GetFloat(ColumnIndex: Integer): Single;
525 begin
526 {$IFNDEF DISABLE_CHECKING}
527  CheckColumnConvertion(ColumnIndex, stFloat);
528 {$ENDIF}
529  Result := SQLStrToFloatDef(InternalGetString(ColumnIndex), 0);
530 end;
531 
532 {**
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.
536 
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>
540 }
541 function TZMySQLResultSet.GetDouble(ColumnIndex: Integer): Double;
542 begin
543 {$IFNDEF DISABLE_CHECKING}
544  CheckColumnConvertion(ColumnIndex, stDouble);
545 {$ENDIF}
546  Result := SQLStrToFloatDef(InternalGetString(ColumnIndex), 0);
547 end;
548 
549 {**
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.
553 
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>
558 }
559 function TZMySQLResultSet.GetBigDecimal(ColumnIndex: Integer): Extended;
560 begin
561 {$IFNDEF DISABLE_CHECKING}
562  CheckColumnConvertion(ColumnIndex, stBigDecimal);
563 {$ENDIF}
564  Result := SQLStrToFloatDef(InternalGetString(ColumnIndex), 0);
565 end;
566 
567 {**
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.
572 
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>
576 }
577 function TZMySQLResultSet.GetBytes(ColumnIndex: Integer): TByteDynArray;
578 begin
579 {$IFNDEF DISABLE_CHECKING}
580  CheckColumnConvertion(ColumnIndex, stBytes);
581 {$ENDIF}
582  Result := StrToBytes(InternalGetString(ColumnIndex));
583 end;
584 
585 {**
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.
589 
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>
593 }
594 function TZMySQLResultSet.GetDate(ColumnIndex: Integer): TDateTime;
595 var
596  Value: string;
597 begin
598 {$IFNDEF DISABLE_CHECKING}
599  CheckColumnConvertion(ColumnIndex, stDate);
600 {$ENDIF}
601  Value := String(InternalGetString(ColumnIndex));
602 
603  LastWasNull := (LastWasNull or (Copy(Value, 1, 10)='0000-00-00'));
604  if LastWasNull then
605  begin
606  Result := 0;
607  Exit;
608  end;
609 
610  if IsMatch('????-??-??*', Value) then
611  Result := Trunc(AnsiSQLDateToDateTime(Value))
612  else
613  Result := Trunc(TimestampStrToDateTime(Value));
614  LastWasNull := Result = 0;
615 end;
616 
617 {**
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.
621 
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>
625 }
626 function TZMySQLResultSet.GetTime(ColumnIndex: Integer): TDateTime;
627 var
628  Value: string;
629 begin
630 {$IFNDEF DISABLE_CHECKING}
631  CheckColumnConvertion(ColumnIndex, stTime);
632 {$ENDIF}
633  Value := String(InternalGetString(ColumnIndex));
634 
635  if LastWasNull then
636  begin
637  Result := 0;
638  Exit;
639  end;
640 
641  if IsMatch('*??:??:??*', Value) then
642  Result := Frac(AnsiSQLDateToDateTime(Value))
643  else
644  Result := Frac(TimestampStrToDateTime(Value));
645 end;
646 
647 {**
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.
651 
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
656 }
657 function TZMySQLResultSet.GetTimestamp(ColumnIndex: Integer): TDateTime;
658 var
659  Temp: string;
660 begin
661 {$IFNDEF DISABLE_CHECKING}
662  CheckColumnConvertion(ColumnIndex, stTimestamp);
663 {$ENDIF}
664  Temp := String(GetPChar(ColumnIndex));
665 
666  if LastWasNull then
667  begin
668  Result := 0;
669  Exit;
670  end;
671 
672  if IsMatch('????-??-??*', Temp) or IsMatch('??:??:??*', Temp) then
673  Result := AnsiSQLDateToDateTime(Temp)
674  else
675  Result := TimestampStrToDateTime(Temp);
676  LastWasNull := Result = 0;
677 end;
678 
679 {**
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.
683 
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
686  the specified column
687 }
688 function TZMySQLResultSet.GetBlob(ColumnIndex: Integer): IZBlob;
689 var
690  Stream: TStream;
691 begin
692 {$IFNDEF DISABLE_CHECKING}
693  CheckBlobColumn(ColumnIndex);
694 {$ENDIF}
695  Stream := nil;
696  try
697  if not IsNull(ColumnIndex) then
698  begin
699  case GetMetaData.GetColumnType(ColumnIndex) of
700  stAsciiStream: Stream := TStringStream.Create(GetValidatedAnsiString(InternalGetString(ColumnIndex), ConSettings, True));
701  stUnicodeStream: Stream := GetValidatedUnicodeStream(InternalGetString(ColumnIndex), ConSettings, True);
702  else
703  Stream := TStringStream.Create(InternalGetString(ColumnIndex));
704  end;
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);
709  end
710  else
711  Result := TZAbstractBlob.CreateWithStream(nil, GetStatement.GetConnection);
712  finally
713  if Assigned(Stream) then
714  Stream.Free;
715  end;
716 end;
717 
718 {**
719  Moves the cursor to the given row number in
720  this <code>ResultSet</code> object.
721 
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
725  is row 2, and so on.
726 
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.
733 
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
736  the last row.
737 
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>.
741 
742  @return <code>true</code> if the cursor is on the result set;
743  <code>false</code> otherwise
744 }
745 function TZMySQLResultSet.MoveAbsolute(Row: Integer): Boolean;
746 begin
747  CheckClosed;
748 
749  { Checks for maximum row. }
750  Result := False;
751  if (MaxRows > 0) and (Row > MaxRows) then
752  Exit;
753 
754  if not FUseResult then
755  begin
756  { Process negative rows. }
757  if Row < 0 then
758  begin
759  Row := LastRowNo - Row + 1;
760  if Row < 0 then
761  Row := 0;
762  end;
763 
764  if (Row >= 0) and (Row <= LastRowNo + 1) then
765  begin
766  RowNo := Row;
767  if (Row >= 1) and (Row <= LastRowNo) then
768  begin
769  FPlainDriver.SeekData(FQueryHandle, RowNo - 1);
770  FRowHandle := FPlainDriver.FetchRow(FQueryHandle);
771  end
772  else
773  FRowHandle := nil;
774  end;
775  Result := FRowHandle <> nil;
776  end
777  else
778  RaiseForwardOnlyException;
779 end;
780 
781 {**
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.
787 
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.
792 
793  @return <code>true</code> if the new current row is valid;
794  <code>false</code> if there are no more rows
795 }
796 function TZMySQLResultSet.Next: Boolean;
797 begin
798  { Checks for maximum row. }
799  Result := False;
800  if (MaxRows > 0) and (RowNo >= MaxRows) then
801  Exit;
802  if Assigned(FQueryHandle) then
803  FRowHandle := FPlainDriver.FetchRow(FQueryHandle);
804  if FRowHandle <> nil then
805  begin
806  RowNo := RowNo + 1;
807  if LastRowNo < RowNo then
808  LastRowNo := RowNo;
809  Result := True;
810  end
811  else
812  begin
813  if RowNo <= LastRowNo then
814  RowNo := LastRowNo + 1;
815  Result := False;
816  end;
817 end;
818 
819 procedure TZMySQLResultSet.ReleaseHandle;
820 begin
821  if FQueryHandle <> nil then
822  FPlainDriver.FreeResult(FQueryHandle);
823  FQueryHandle := nil;
824 end;
825 { TZMySQLPreparedResultSet }
826 
827 {**
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.
835 }
836 constructor TZMySQLPreparedResultSet.Create(PlainDriver: IZMySQLPlainDriver;
837  Statement: IZStatement; SQL: string; Handle: PZMySQLConnect;
838  UseResult: Boolean);
839 var
840  tempPrepStmt : IZMysqlPreparedStatement;
841 begin
842  inherited Create(Statement, SQL, TZMySQLResultSetMetadata.Create(
843  Statement.GetConnection.GetMetadata, SQL, Self),
844  Statement.GetConnection.GetConSettings);
845 
846  FHandle := Handle;
847  tempPrepStmt := Statement as IZMysqlPreparedStatement;
848  FPrepStmt:= tempPrepStmt.GetStmtHandle;
849  FResultMetaData := nil;
850  FPlainDriver := PlainDriver;
851  ResultSetConcurrency := rcReadOnly;
852  FUseResult := UseResult;
853 
854  Open;
855 end;
856 
857 {**
858  Destroys this object and cleanups the memory.
859 }
860 destructor TZMySQLPreparedResultSet.Destroy;
861 begin
862  inherited Destroy;
863 end;
864 
865 {**
866  Opens this recordset.
867 }
868 procedure TZMySQLPreparedResultSet.Open;
869 const one = AnsiString('1');
870 var
871  I: Integer;
872  ColumnInfo: TZColumnInfo;
873  FieldHandle: PZMySQLField;
874  FieldCount: Integer;
875 begin
876  if ResultSetConcurrency = rcUpdatable then
877  raise EZSQLException.Create(SLiveResultSetsAreNotSupported);
878 
879  FieldCount := FPlainDriver.GetPreparedFieldCount(FPrepStmt);
880  if FieldCount = 0 then
881  raise EZSQLException.Create(SCanNotRetrieveResultSetData);
882 
883  FResultMetaData := FPlainDriver.GetPreparedMetaData(FPrepStmt);
884  if not Assigned(FResultMetaData) then
885  raise EZSQLException.Create(SCanNotRetrieveResultSetData);
886 
887  if FUseResult then
888  LastRowNo := 0
889  else
890  begin
891  FPlainDriver.StmtAttrSet(FPrepStmt,STMT_ATTR_UPDATE_MAX_LENGTH,PAnsiChar(one));
892  if (FPlainDriver.StorePreparedResult(FPrepStmt)=0) then
893  LastRowNo := FPlainDriver.GetPreparedNumRows(FPrepStmt)
894  else
895  LastRowNo := 0;
896  end;
897 
898  { Initialize Bind Array and Column Array }
899  FBindBuffer := TZMySqlResultSetBindBuffer.Create(FPlainDriver,FieldCount,FColumnArray);
900 
901  { Fills the column info. }
902  ColumnsInfo.Clear;
903  for I := 0 to FPlainDriver.GetFieldCount(FResultMetaData) - 1 do
904  begin
905  FPlainDriver.SeekField(FResultMetaData, I);
906  FieldHandle := FPlainDriver.FetchField(FResultMetaData);
907  if FieldHandle = nil then
908  Break;
909 
910  ColumnInfo := GetMySQLColumnInfoFromFieldHandle(FPlainDriver,
911  FieldHandle, GetStatement.GetConnection.GetConSettings, FUseResult);
912 
913  ColumnsInfo.Add(ColumnInfo);
914 
915  FBindBuffer.AddColumn(FPlainDriver, FieldHandle);
916  end;
917  FPlainDriver.FreeResult(FResultMetaData);
918  FResultMetaData := nil;
919 
920  if (FPlainDriver.BindResult(FPrepStmt,FBindBuffer.GetBufferAddress)<>0) then
921  raise EZSQLException.Create(SFailedToBindResults);
922 
923  inherited Open;
924 end;
925 
926 {**
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.
930 
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.
938 }
939 procedure TZMySQLPreparedResultSet.Close;
940 begin
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
947  begin
948  FPlainDriver.FreePreparedResult(FPrepStmt);
949  while(FPlainDriver.GetPreparedNextResult(FPrepStmt) = 0) do
950  FPlainDriver.FreePreparedResult(FPrepStmt);
951  end;
952  inherited Close;
953 
954 end;
955 
956 {**
957  Indicates if the value of the designated column in the current row
958  of this <code>ResultSet</code> object is Null.
959 
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.
963 }
964 function TZMySQLPreparedResultSet.IsNull(ColumnIndex: Integer): Boolean;
965 begin
966 {$IFNDEF DISABLE_CHECKING}
967  CheckClosed;
968 {$ENDIF}
969 
970  Result := FColumnArray[ColumnIndex-1].is_null =1;
971 end;
972 
973 {**
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.
977 
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>
981 }
982 function TZMySQLPreparedResultSet.InternalGetString(ColumnIndex: Integer): RawByteString;
983 begin
984 {$IFNDEF DISABLE_CHECKING}
985  CheckClosed;
986 {$ENDIF}
987  Result := PAnsiChar(FColumnArray[ColumnIndex - 1].buffer);
988  LastWasNull := FColumnArray[ColumnIndex-1].is_null =1;
989 end;
990 
991 {**
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.
995 
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>
999 }
1000 function TZMySQLPreparedResultSet.GetBoolean(ColumnIndex: Integer): Boolean;
1001 var
1002  Temp: string;
1003 begin
1004 {$IFNDEF DISABLE_CHECKING}
1005  CheckColumnConvertion(ColumnIndex, stBoolean);
1006 {$ENDIF}
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);
1010 end;
1011 
1012 {**
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.
1016 
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>
1020 }
1021 function TZMySQLPreparedResultSet.GetByte(ColumnIndex: Integer): Byte;
1022 begin
1023 {$IFNDEF DISABLE_CHECKING}
1024  CheckColumnConvertion(ColumnIndex, stByte);
1025 {$ENDIF}
1026  Result := Byte(bufferasInt64(ColumnIndex));
1027  LastWasNull := FColumnArray[ColumnIndex-1].is_null =1;
1028 end;
1029 
1030 {**
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.
1034 
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>
1038 }
1039 function TZMySQLPreparedResultSet.GetShort(ColumnIndex: Integer): SmallInt;
1040 Begin
1041 {$IFNDEF DISABLE_CHECKING}
1042  CheckColumnConvertion(ColumnIndex, stShort);
1043 {$ENDIF}
1044  Result := Integer(bufferasInt64(ColumnIndex));
1045  LastWasNull := FColumnArray[ColumnIndex-1].is_null =1;
1046 end;
1047 
1048 {**
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.
1052 
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>
1056 }
1057 function TZMySQLPreparedResultSet.GetInt(ColumnIndex: Integer): Integer;
1058 begin
1059 {$IFNDEF DISABLE_CHECKING}
1060  CheckColumnConvertion(ColumnIndex, stInteger);
1061 {$ENDIF}
1062  Result := bufferasInt64(ColumnIndex);
1063  LastWasNull := FColumnArray[ColumnIndex-1].is_null =1;
1064 end;
1065 
1066 {**
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.
1070 
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>
1074 }
1075 function TZMySQLPreparedResultSet.GetLong(ColumnIndex: Integer): Int64;
1076 Begin
1077 {$IFNDEF DISABLE_CHECKING}
1078  CheckColumnConvertion(ColumnIndex, stLong);
1079 {$ENDIF}
1080  Result := bufferasInt64(ColumnIndex);
1081  LastWasNull := FColumnArray[ColumnIndex-1].is_null =1;
1082 end;
1083 
1084 {**
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.
1088 
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>
1092 }
1093 function TZMySQLPreparedResultSet.GetFloat(ColumnIndex: Integer): Single;
1094 begin
1095 {$IFNDEF DISABLE_CHECKING}
1096  CheckColumnConvertion(ColumnIndex, stFloat);
1097 {$ENDIF}
1098  Result := BufferAsExtended(ColumnIndex);
1099  LastWasNull := FColumnArray[ColumnIndex-1].is_null =1;
1100 end;
1101 
1102 {**
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.
1106 
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>
1110 }
1111 function TZMySQLPreparedResultSet.GetDouble(ColumnIndex: Integer): Double;
1112 begin
1113 {$IFNDEF DISABLE_CHECKING}
1114  CheckColumnConvertion(ColumnIndex, stDouble);
1115 {$ENDIF}
1116  Result := BufferAsExtended(ColumnIndex);
1117  LastWasNull := FColumnArray[ColumnIndex-1].is_null =1;
1118 end;
1119 
1120 {**
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.
1124 
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>
1129 }
1130 function TZMySQLPreparedResultSet.GetBigDecimal(ColumnIndex: Integer): Extended;
1131 begin
1132 {$IFNDEF DISABLE_CHECKING}
1133  CheckColumnConvertion(ColumnIndex, stBigDecimal);
1134 {$ENDIF}
1135  Result := BufferAsExtended(ColumnIndex);
1136  LastWasNull := FColumnArray[ColumnIndex-1].is_null =1;
1137 end;
1138 
1139 {**
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.
1144 
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>
1148 }
1149 function TZMySQLPreparedResultSet.GetBytes(ColumnIndex: Integer): TByteDynArray;
1150 begin
1151 {$IFNDEF DISABLE_CHECKING}
1152  CheckColumnConvertion(ColumnIndex, stBytes);
1153 {$ENDIF}
1154  Result := StrToBytes(InternalGetString(ColumnIndex));
1155  LastWasNull := FColumnArray[ColumnIndex-1].is_null =1;
1156 end;
1157 
1158 {**
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.
1162 
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>
1166 }
1167 function TZMySQLPreparedResultSet.GetDate(ColumnIndex: Integer): TDateTime;
1168 begin
1169 {$IFNDEF DISABLE_CHECKING}
1170  CheckColumnConvertion(ColumnIndex, stDate);
1171 {$ENDIF}
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,
1175  Result) then
1176  Result := encodeDate(1900, 1, 1);
1177  LastWasNull := FColumnArray[ColumnIndex-1].is_null =1;
1178 end;
1179 
1180 {**
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.
1184 
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>
1188 }
1189 function TZMySQLPreparedResultSet.GetTime(ColumnIndex: Integer): TDateTime;
1190 begin
1191 {$IFNDEF DISABLE_CHECKING}
1192  CheckColumnConvertion(ColumnIndex, stTime);
1193 {$ENDIF}
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,
1197  0,
1198  Result) then
1199  Result := encodeTime(0,0,0,0);
1200  LastWasNull := FColumnArray[ColumnIndex-1].is_null =1;
1201 end;
1202 
1203 {**
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.
1207 
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
1212 }
1213 function TZMySQLPreparedResultSet.GetTimestamp(ColumnIndex: Integer): TDateTime;
1214 begin
1215 {$IFNDEF DISABLE_CHECKING}
1216  CheckColumnConvertion(ColumnIndex, stTimestamp);
1217 {$ENDIF}
1218  Result := GetDate(ColumnIndex) + GetTime(ColumnIndex);
1219  LastWasNull := FColumnArray[ColumnIndex-1].is_null =1;
1220 end;
1221 
1222 {**
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.
1230 
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.
1237 
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>
1242 }
1243 function TZMySQLPreparedResultSet.GetAsciiStream(ColumnIndex: Integer): TStream;
1244 begin
1245 {$IFNDEF DISABLE_CHECKING}
1246  CheckColumnConvertion(ColumnIndex, stAsciiStream);
1247 {$ENDIF}
1248  Result := TStringStream.Create(InternalGetString(ColumnIndex));
1249  LastWasNull := FColumnArray[ColumnIndex-1].is_null =1;
1250 end;
1251 
1252 {**
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.
1263 
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.
1270 
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>
1275 }
1276 function TZMySQLPreparedResultSet.GetUnicodeStream(ColumnIndex: Integer): TStream;
1277 var
1278  WS: ZWideString;
1279 begin
1280 {$IFNDEF DISABLE_CHECKING}
1281  CheckColumnConvertion(ColumnIndex, stUnicodeStream);
1282 {$ENDIF}
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;
1288 end;
1289 
1290 {**
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.
1297 
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.
1304 
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>
1309 }
1310 function TZMySQLPreparedResultSet.GetBinaryStream(ColumnIndex: Integer): TStream;
1311 begin
1312 {$IFNDEF DISABLE_CHECKING}
1313  CheckColumnConvertion(ColumnIndex, stBinaryStream);
1314 {$ENDIF}
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;
1319 end;
1320 
1321 {**
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.
1325 
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
1329 }
1330 function TZMySQLPreparedResultSet.GetBlob(ColumnIndex: Integer): IZBlob;
1331 var
1332  Stream: TStream;
1333 begin
1334  Result := nil;
1335 {$IFNDEF DISABLE_CHECKING}
1336  CheckBlobColumn(ColumnIndex);
1337 {$ENDIF}
1338 
1339  LastWasNull := IsNull(ColumnIndex);
1340  if LastWasNull then
1341  Exit;
1342 
1343  Stream := nil;
1344  try
1345  if not LastWasNull then
1346  begin
1347  case GetMetadata.GetColumnType(ColumnIndex) of
1348  stBinaryStream: Stream := GetBinaryStream(ColumnIndex);
1349  stUnicodeStream: Stream := GetUnicodeStream(ColumnIndex);
1350  else
1351  Stream := TStringStream.Create(GetValidatedAnsiString(InternalGetString(ColumnIndex), ConSettings, True));
1352  end;
1353  Result := TZAbstractBlob.CreateWithStream(Stream, GetStatement.GetConnection,
1354  GetMetadata.GetColumnType(ColumnIndex) = stUnicodeStream)
1355  end
1356  else
1357  Result := TZAbstractBlob.CreateWithStream(nil, GetStatement.GetConnection);
1358  finally
1359  if Assigned(Stream) then
1360  Stream.Free;
1361  end;
1362 end;
1363 
1364 function TZMySQLPreparedResultSet.bufferasint64(ColumnIndex: Integer): Int64;
1365 begin
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
1386  FIELD_TYPE_BIT: ;
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*)
1397  else Result := 0;
1398  end
1399  else
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*)
1428  else Result := 0;
1429  end;
1430 end;
1431 
1432 function TZMySQLPreparedResultSet.bufferasextended(ColumnIndex: Integer): Extended;
1433 begin
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)^;
1437  else
1438  Result := SQLStrToFloatDef(InternalGetString(ColumnIndex), 0);
1439  End
1440 end;
1441 
1442 {**
1443  Moves the cursor to the given row number in
1444  this <code>ResultSet</code> object.
1445 
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.
1450 
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.
1457 
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
1460  the last row.
1461 
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>.
1465 
1466  @return <code>true</code> if the cursor is on the result set;
1467  <code>false</code> otherwise
1468 }
1469 function TZMySQLPreparedResultSet.MoveAbsolute(Row: Integer): Boolean;
1470 begin
1471  CheckClosed;
1472 
1473  { Checks for maximum row. }
1474  Result := False;
1475  if (MaxRows > 0) and (Row > MaxRows) then
1476  Exit;
1477 
1478  if not FUseResult then
1479  begin
1480  { Process negative rows. }
1481  if Row < 0 then
1482  begin
1483  Row := LastRowNo - Row + 1;
1484  if Row < 0 then
1485  Row := 0;
1486  end;
1487 
1488  if (Row >= 0) and (Row <= LastRowNo + 1) then
1489  begin
1490  RowNo := Row;
1491  if (Row >= 1) and (Row <= LastRowNo) then
1492  begin
1493  FPlainDriver.SeekPreparedData(FPrepStmt, RowNo - 1);
1494  Result := (FPlainDriver.FetchBoundResults(FPrepStmt) =0);
1495  end;
1496  end;
1497  end
1498  else
1499  RaiseForwardOnlyException;
1500 end;
1501 
1502 {**
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.
1508 
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.
1513 
1514  @return <code>true</code> if the new current row is valid;
1515  <code>false</code> if there are no more rows
1516 }
1517 function TZMySQLPreparedResultSet.Next: Boolean;
1518 begin
1519  { Checks for maximum row. }
1520  Result := False;
1521  if (MaxRows > 0) and (RowNo >= MaxRows) then
1522  Exit;
1523 
1524  if FPlainDriver.FetchBoundResults(FPrepStmt) in [0, MYSQL_DATA_TRUNCATED] then
1525  begin
1526  RowNo := RowNo + 1;
1527  if LastRowNo < RowNo then
1528  LastRowNo := RowNo;
1529  Result := True;
1530  end
1531  else
1532  begin
1533  if RowNo <= LastRowNo then
1534  RowNo := LastRowNo + 1;
1535  Result := False;
1536  end;
1537 end;
1538 
1539 { TZMySQLCachedResolver }
1540 
1541 {**
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.
1547 }
1548 constructor TZMySQLCachedResolver.Create(PlainDriver: IZMySQLPlainDriver;
1549  Handle: PZMySQLConnect; Statement: IZMysqlStatement; Metadata: IZResultSetMetadata);
1550 var
1551  I: Integer;
1552 begin
1553  inherited Create(Statement, Metadata);
1554  FPlainDriver := PlainDriver;
1555  FHandle := Handle;
1556  FStatement := Statement as IZMysqlStatement;
1557 
1558  { Defines an index of autoincrement field. }
1559  FAutoColumnIndex := 0;
1560  for I := 1 to Metadata.GetColumnCount do
1561  begin
1562  if Metadata.IsAutoIncrement(I) and
1563  (Metadata.GetColumnType(I) in [stByte, stShort, stInteger, stLong]) then
1564  begin
1565  FAutoColumnIndex := I;
1566  Break;
1567  end;
1568  end;
1569 end;
1570 
1571 {**
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.
1575 }
1576 function TZMySQLCachedResolver.FormWhereClause(Columns: TObjectList;
1577  OldRowAccessor: TZRowAccessor): string;
1578 var
1579  I, N: Integer;
1580  Current: TZResolverParameter;
1581 begin
1582  Result := '';
1583  N := Columns.Count - WhereColumns.Count;
1584 
1585  for I := 0 to WhereColumns.Count - 1 do
1586  begin
1587  Current := TZResolverParameter(WhereColumns[I]);
1588  if Result <> '' then
1589  Result := Result + ' AND ';
1590 
1591  Result := Result + IdentifierConvertor.Quote(Current.ColumnName);
1592  if OldRowAccessor.IsNull(Current.ColumnIndex) then
1593  begin
1594  if not (Metadata.IsNullable(Current.ColumnIndex) = ntNullable) then
1595  case OldRowAccessor.GetColumnType(Current.ColumnIndex) of
1596  stDate:
1597  if I > 0 then
1598  begin
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, ''));
1603  end;
1604  stTime:
1605  if I > 0 then
1606  begin
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, ''));
1611  end;
1612  stTimeStamp:
1613  if I > 0 then
1614  begin
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, ''));
1619  end;
1620  else
1621  Result := Result + ' IS NULL';
1622  end
1623  else
1624  Result := Result + ' IS NULL ';
1625  Columns.Delete(N);
1626  end
1627  else
1628  begin
1629  Result := Result + '=?';
1630  Inc(N);
1631  end;
1632  end;
1633 
1634  if Result <> '' then
1635  Result := ' WHERE ' + Result;
1636 end;
1637 {**
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.
1643 }
1644 procedure TZMySQLCachedResolver.PostUpdates(Sender: IZCachedResultSet;
1645  UpdateType: TZRowUpdateType; OldRowAccessor, NewRowAccessor: TZRowAccessor);
1646 begin
1647  inherited PostUpdates(Sender, UpdateType, OldRowAccessor, NewRowAccessor);
1648  if (UpdateType = utInserted) then
1649  begin
1650  UpdateAutoIncrementFields(Sender, UpdateType, OldRowAccessor, NewRowAccessor, Self);
1651  end;
1652 end;
1653 
1654 {**
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.
1660 }
1661 procedure TZMySQLCachedResolver.UpdateAutoIncrementFields(
1662  Sender: IZCachedResultSet; UpdateType: TZRowUpdateType; OldRowAccessor,
1663  NewRowAccessor: TZRowAccessor; Resolver: IZCachedResolver);
1664 var
1665  Plaindriver : IZMysqlPlainDriver;
1666 begin
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
1670  exit;
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))
1675  else}
1676  NewRowAccessor.SetLong(FAutoColumnIndex, PlainDriver.GetLastInsertID(FHandle));
1677 
1678 end;
1679 
1680 {**
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.
1684 }
1685 function TZMySQLCachedResolver.FormCalculateStatement(
1686  Columns: TObjectList): string;
1687 var
1688  I: Integer;
1689  Current: TZResolverParameter;
1690 begin
1691  Result := '';
1692  if Columns.Count = 0 then
1693  Exit;
1694 
1695  for I := 0 to Columns.Count - 1 do
1696  begin
1697  Current := TZResolverParameter(Columns[I]);
1698  if Result <> '' then
1699  Result := Result + ',';
1700  if Current.DefaultValue <> '' then
1701  Result := Result + Current.DefaultValue
1702  else
1703  Result := Result + 'NULL';
1704  end;
1705  Result := 'SELECT ' + Result;
1706 end;
1707 
1708 end.
1709