zeoslib  UNKNOWN
 All Files
ZDbcDbLibResultSet.pas
Go to the documentation of this file.
1 {*********************************************************}
2 { }
3 { Zeos Database Objects }
4 { DBLib Resultset common functionality }
5 { }
6 { Originally written by Janos Fegyverneki }
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 ZDbcDbLibResultSet;
53 
54 interface
55 
56 {$I ZDbc.inc}
57 
58 uses
59 {$IFNDEF FPC}
60  DateUtils,
61 {$ENDIF}
62  {$IFDEF WITH_TOBJECTLIST_INLINE}System.Types, System.Contnrs{$ELSE}Types{$ENDIF},
63  Classes, {$IFDEF MSEgui}mclasses,{$ENDIF} SysUtils,
64  ZDbcIntfs, ZDbcResultSet, ZCompatibility, ZDbcResultsetMetadata,
65  ZDbcGenericResolver, ZDbcCachedResultSet, ZDbcCache, ZDbcDBLib,
66  ZPlainDbLibConstants, ZPlainDBLibDriver;
67 
68 type
69  {** Implements DBLib ResultSet. }
70  TZDBLibResultSet = class(TZAbstractResultSet)
71  private
72  FSQL: string;
73  FHandle: PDBPROCESS;
74  DBLibColTypeCache: TSmallIntDynArray;
75  DBLibColumnCount: Integer;
76  procedure CheckColumnIndex(ColumnIndex: Integer);
77  protected
78  FDBLibConnection: IZDBLibConnection;
79  FPlainDriver: IZDBLibPlainDriver;
80  procedure Open; override;
81  function InternalGetString(ColumnIndex: Integer): RawByteString; override;
82  public
83  constructor Create(Statement: IZStatement; SQL: string);
84  destructor Destroy; override;
85 
86  procedure Close; override;
87 
88  function IsNull(ColumnIndex: Integer): Boolean; override;
89  function GetString(ColumnIndex: Integer): String; override;
90  function GetUnicodeString(ColumnIndex: Integer): WideString; override;
91  function GetBoolean(ColumnIndex: Integer): Boolean; override;
92  function GetByte(ColumnIndex: Integer): Byte; override;
93  function GetShort(ColumnIndex: Integer): SmallInt; override;
94  function GetInt(ColumnIndex: Integer): Integer; override;
95  function GetLong(ColumnIndex: Integer): Int64; override;
96  function GetFloat(ColumnIndex: Integer): Single; override;
97  function GetDouble(ColumnIndex: Integer): Double; override;
98  function GetBigDecimal(ColumnIndex: Integer): Extended; override;
99  function GetBytes(ColumnIndex: Integer): TByteDynArray; override;
100  function GetDate(ColumnIndex: Integer): TDateTime; override;
101  function GetTime(ColumnIndex: Integer): TDateTime; override;
102  function GetTimestamp(ColumnIndex: Integer): TDateTime; override;
103  function GetBlob(ColumnIndex: Integer): IZBlob; override;
104 
105  function MoveAbsolute(Row: Integer): Boolean; override;
106  function Next: Boolean; override;
107  end;
108 
109  {** Implements a cached resolver with mssql and sybase specific functionality. }
110  TZDBLibCachedResolver = class (TZGenericCachedResolver, IZCachedResolver)
111  private
112  FAutoColumnIndex: Integer;
113  public
114  constructor Create(Statement: IZStatement; Metadata: IZResultSetMetadata);
115 
116  procedure PostUpdates(Sender: IZCachedResultSet; UpdateType: TZRowUpdateType;
117  OldRowAccessor, NewRowAccessor: TZRowAccessor); override;
118  end;
119 
120 implementation
121 
122 uses ZMessages, ZDbcLogging, ZDbcDBLibUtils, ZEncoding
123  {$IFDEF WITH_UNITANSISTRINGS}, AnsiStrings{$ENDIF}
124  {$IFDEF WITH_WIDESTRUTILS}, WideStrUtils {$ENDIF}
125 ;
126 
127 { TZDBLibResultSet }
128 
129 {**
130  Constructs this object, assignes main properties and
131  opens the record set.
132  @param Statement a related SQL statement object.
133  @param Handle a DBLib specific query handle.
134 }
135 constructor TZDBLibResultSet.Create(Statement: IZStatement; SQL: string);
136 begin
137  inherited Create(Statement, SQL, nil, Statement.GetConnection.GetConSettings);
138  Statement.GetConnection.QueryInterface(IZDBLibConnection, FDBLibConnection);
139  FPlainDriver := FDBLibConnection.GetPlainDriver;
140  FHandle := FDBLibConnection.GetConnectionHandle;
141  FSQL := SQL;
142 
143  Open;
144 end;
145 
146 {**
147  Destroys this object and cleanups the memory.
148 }
149 destructor TZDBLibResultSet.Destroy;
150 begin
151 { TODO -ofjanos -cGeneral : Does it need close here? }
152  Close;
153  inherited Destroy;
154 end;
155 
156 {**
157  Opens this recordset.
158 }
159 procedure TZDBLibResultSet.Open;
160 var
161  I: Integer;
162  ColumnInfo: TZColumnInfo;
163  ColName: string;
164  ColType: Integer;
165 begin
166 //Check if the current statement can return rows
167  if FPlainDriver.dbCmdRow(FHandle) <> DBSUCCEED then
168  raise EZSQLException.Create(SCanNotRetrieveResultSetData);
169 
170  { Fills the column info }
171  ColumnsInfo.Clear;
172  DBLibColumnCount := FPlainDriver.dbnumcols(FHandle);
173  SetLength(DBLibColTypeCache, DBLibColumnCount + 1);
174  for I := 1 to DBLibColumnCount do
175  begin
176  ColName := FPlainDriver.ZDbcString(FPlainDriver.dbColName(FHandle, I),
177  FDBLibConnection.GetConSettings);
178  ColType := FPlainDriver.dbColtype(FHandle, I);
179  ColumnInfo := TZColumnInfo.Create;
180 
181  ColumnInfo.ColumnLabel := ColName;
182  ColumnInfo.ColumnName := ColName;
183  if Self.FDBLibConnection.FreeTDS then
184  ColumnInfo.ColumnType := ConvertFreeTDSToSqlType(ColType, ConSettings.CPType)
185  else
186  ColumnInfo.ColumnType := ConvertDBLibToSqlType(ColType, ConSettings.CPType);
187  ColumnInfo.Currency := (ColType = FPlainDriver.GetVariables.datatypes[Z_SQLMONEY]) or
188  (ColType = FPlainDriver.GetVariables.datatypes[Z_SQLMONEY4]) or
189  (ColType = FPlainDriver.GetVariables.datatypes[Z_SQLMONEYN]);;
190  ColumnInfo.Precision := FPlainDriver.dbCollen(FHandle, I);
191  ColumnInfo.Scale := 0;
192  if ColType = FPlainDriver.GetVariables.datatypes[Z_SQLINT1] then
193  ColumnInfo.Signed := False
194  else
195  ColumnInfo.Signed := True;
196 
197  ColumnsInfo.Add(ColumnInfo);
198 
199  DBLibColTypeCache[I] := ColType;
200  end;
201  inherited Open;
202 end;
203 
204 {**
205  Releases this <code>ResultSet</code> object's database and
206  JDBC resources immediately instead of waiting for
207  this to happen when it is automatically closed.
208 
209  <P><B>Note:</B> A <code>ResultSet</code> object
210  is automatically closed by the
211  <code>Statement</code> object that generated it when
212  that <code>Statement</code> object is closed,
213  re-executed, or is used to retrieve the next result from a
214  sequence of multiple results. A <code>ResultSet</code> object
215  is also automatically closed when it is garbage collected.
216 }
217 procedure TZDBLibResultSet.Close;
218 begin
219 { TODO -ofjanos -cGeneral : Maybe it needs a dbcanquery here. }
220 // if Assigned(FHandle) then
221 // if not FPlainDriver.dbDead(FHandle) then
222 // if FPlainDriver.dbCanQuery(FHandle) <> DBSUCCEED then
223 // FDBLibConnection.CheckDBLibError(lcDisconnect, 'CLOSE QUERY');
224  FHandle := nil;
225  SetLength(DBLibColTypeCache, 0);
226  inherited Close;
227 end;
228 
229 {**
230  Checks if the columnindex is in the proper range.
231  An exception is generated if somthing is not ok.
232 
233  @param columnIndex the first column is 1, the second is 2, ...
234 }
235 procedure TZDBLibResultSet.CheckColumnIndex(ColumnIndex: Integer);
236 begin
237  if (ColumnIndex > DBLibColumnCount) or (ColumnIndex < 1) then
238  begin
239  raise EZSQLException.Create(
240  Format(SColumnIsNotAccessable, [ColumnIndex]));
241  end;
242 end;
243 
244 {**
245  Indicates if the value of the designated column in the current row
246  of this <code>ResultSet</code> object is Null.
247 
248  @param columnIndex the first column is 1, the second is 2, ...
249  @return if the value is SQL <code>NULL</code>, the
250  value returned is <code>true</code>. <code>false</code> otherwise.
251 }
252 function TZDBLibResultSet.IsNull(ColumnIndex: Integer): Boolean;
253 begin
254  CheckClosed;
255  CheckColumnIndex(ColumnIndex);
256  Result := FPlainDriver.dbData(FHandle, ColumnIndex) = nil;
257 end;
258 
259 {**
260  Gets the value of the designated column in the current row
261  of this <code>ResultSet</code> object as
262  a <code>String</code> in the Java programming language.
263 
264  @param columnIndex the first column is 1, the second is 2, ...
265  @return the column value; if the value is SQL <code>NULL</code>, the
266  value returned is <code>null</code>
267 }
268 function TZDBLibResultSet.GetString(ColumnIndex: Integer): String;
269 var Tmp: RawByteString;
270 begin
271  if TZColumnInfo(ColumnsInfo[ColumnIndex-1]).ColumnType in [stString, stUnicodeString, stAsciiStream, stUnicodeStream] then
272  if TZColumnInfo(ColumnsInfo[ColumnIndex-1]).InternalColumnType = stUnknown then
273  begin
274  Tmp := InternalGetString(ColumnIndex);
275  case DetectUTF8Encoding(Tmp) of
276  etUTF8:
277  begin
278  if TZColumnInfo(ColumnsInfo[ColumnIndex-1]).ColumnType in [stString, stUnicodeString] then
279  TZColumnInfo(ColumnsInfo[ColumnIndex-1]).InternalColumnType := stUnicodeString
280  else
281  TZColumnInfo(ColumnsInfo[ColumnIndex-1]).InternalColumnType := stUnicodeStream;
282  Result := ZDbcString(Tmp, zCP_UTF8)
283  end;
284  etAnsi:
285  begin
286  if TZColumnInfo(ColumnsInfo[ColumnIndex-1]).ColumnType in [stString, stUnicodeString] then
287  TZColumnInfo(ColumnsInfo[ColumnIndex-1]).InternalColumnType := stString
288  else
289  TZColumnInfo(ColumnsInfo[ColumnIndex-1]).InternalColumnType := stAsciiStream;
290  Result := ZDbcString(tmp, ConSettings^.ClientCodePage^.CP)
291  end;
292  else
293  Result := ZDbcString(tmp);
294  end;
295  end
296  else
297  if TZColumnInfo(ColumnsInfo[ColumnIndex-1]).InternalColumnType in [stUnicodeString, stUnicodeStream] then
298  Result := ZDbcString(InternalGetString(ColumnIndex), zCP_UTF8)
299  else
300  Result := ZDbcString(InternalGetString(ColumnIndex), ConSettings^.ClientCodePage^.CP)
301  else
302  Result := String(InternalGetString(ColumnIndex));
303 end;
304 
305 {**
306  Gets the value of the designated column in the current row
307  of this <code>ResultSet</code> object as
308  a <code>WideString</code> in the Delphi programming language.
309 
310  @param columnIndex the first column is 1, the second is 2, ...
311  @return the column value; if the value is SQL <code>NULL</code>, the
312  value returned is <code>null</code>
313 }
314 function TZDBLibResultSet.GetUnicodeString(ColumnIndex: Integer): WideString;
315 var Tmp: RawByteString;
316 begin
317  if TZColumnInfo(ColumnsInfo[ColumnIndex-1]).ColumnType in [stString, stUnicodeString, stAsciiStream, stUnicodeStream] then
318  if TZColumnInfo(ColumnsInfo[ColumnIndex-1]).InternalColumnType = stUnknown then
319  begin
320  Tmp := InternalGetString(ColumnIndex);
321  case DetectUTF8Encoding(Tmp) of
322  etUTF8:
323  begin
324  if TZColumnInfo(ColumnsInfo[ColumnIndex-1]).ColumnType in [stString, stUnicodeString] then
325  TZColumnInfo(ColumnsInfo[ColumnIndex-1]).InternalColumnType := stUnicodeString
326  else
327  TZColumnInfo(ColumnsInfo[ColumnIndex-1]).InternalColumnType := stUnicodeStream;
328  Result := ZDbcUnicodeString(Tmp, zCP_UTF8)
329  end;
330  etAnsi:
331  begin
332  if TZColumnInfo(ColumnsInfo[ColumnIndex-1]).ColumnType in [stString, stUnicodeString] then
333  TZColumnInfo(ColumnsInfo[ColumnIndex-1]).InternalColumnType := stString
334  else
335  TZColumnInfo(ColumnsInfo[ColumnIndex-1]).InternalColumnType := stAsciiStream;
336  Result := ZDbcUnicodeString(tmp, ConSettings^.ClientCodePage^.CP)
337  end;
338  else
339  Result := ZDbcUnicodeString(tmp);
340  end;
341  end
342  else
343  if TZColumnInfo(ColumnsInfo[ColumnIndex-1]).InternalColumnType in [stUnicodeString, stUnicodeStream] then
344  Result := ZDbcUnicodeString(InternalGetString(ColumnIndex), zCP_UTF8)
345  else
346  Result := ZDbcUnicodeString(InternalGetString(ColumnIndex), ConSettings^.ClientCodePage^.CP)
347  else
348  Result := WideString(InternalGetString(ColumnIndex));
349 end;
350 
351 {**
352  Gets the value of the designated column in the current row
353  of this <code>ResultSet</code> object as
354  a <code>String</code> in the Java programming language.
355 
356  @param columnIndex the first column is 1, the second is 2, ...
357  @return the column value; if the value is SQL <code>NULL</code>, the
358  value returned is <code>null</code>
359 }
360 function TZDBLibResultSet.InternalGetString(ColumnIndex: Integer): RawByteString;
361 var
362  DL: Integer;
363  Data: Pointer;
364  DT: Integer;
365 begin
366  CheckClosed;
367  CheckColumnIndex(ColumnIndex);
368 
369  DL := FPlainDriver.dbDatLen(FHandle, ColumnIndex);
370  Data := FPlainDriver.dbdata(FHandle, ColumnIndex);
371  DT := DBLibColTypeCache[ColumnIndex];
372  LastWasNull := Data = nil;
373 
374  Result := '';
375  if Assigned(Data) then
376  begin
377  if (DT = FPlainDriver.GetVariables.datatypes[Z_SQLCHAR]) or
378  (DT = FPlainDriver.GetVariables.datatypes[Z_SQLTEXT]) then
379  begin
380  while (DL > 0) and (PAnsiChar(NativeUint(Data) + NativeUint(DL - 1))^ = ' ') do
381  Dec(DL);
382  if DL > 0 then
383  begin
384  SetLength(Result, DL);
385  Move(Data^, PAnsiChar(Result)^, DL);
386  end;
387  end else
388  if (DT = FPlainDriver.GetVariables.datatypes[Z_SQLIMAGE]) then
389  begin
390  SetLength(Result, DL);
391  Move(Data^, PAnsiChar(Result)^, DL);
392  end else
393  begin
394  SetLength(Result, 4001);
395  DL := FPlainDriver.dbconvert(FHandle, DT, Data, DL,
396  FPlainDriver.GetVariables.datatypes[Z_SQLCHAR], Pointer(PAnsiChar(Result)), Length(Result));
397  while (DL > 0) and (Result[DL] = ' ') do
398  Dec(DL);
399  SetLength(Result, DL);
400  end;
401  end;
402  //else
403 
404  FDBLibConnection.CheckDBLibError(lcOther, 'GETSTRING');
405 end;
406 
407 {**
408  Gets the value of the designated column in the current row
409  of this <code>ResultSet</code> object as
410  a <code>boolean</code> in the Java programming language.
411 
412  @param columnIndex the first column is 1, the second is 2, ...
413  @return the column value; if the value is SQL <code>NULL</code>, the
414  value returned is <code>false</code>
415 }
416 function TZDBLibResultSet.GetBoolean(ColumnIndex: Integer): Boolean;
417 var
418  DL: Integer;
419  Data: Pointer;
420  DT: Integer;
421 begin
422  CheckClosed;
423  CheckColumnIndex(ColumnIndex);
424 
425  DL := FPlainDriver.dbdatlen(FHandle, ColumnIndex);
426  Data := FPlainDriver.dbdata(FHandle, ColumnIndex);
427  DT := DBLibColTypeCache[ColumnIndex];
428  LastWasNull := Data = nil;
429 
430  Result := False;
431  if Assigned(Data) then
432  begin
433  if DT = FPlainDriver.GetVariables.datatypes[Z_SQLBIT] then
434  Result := PBoolean(Data)^
435  else
436  begin
437  FPlainDriver.dbconvert(FHandle, DT, Data, DL, FPlainDriver.GetVariables.datatypes[Z_SQLBIT],
438  @Result, SizeOf(Result));
439  end;
440  end;
441  FDBLibConnection.CheckDBLibError(lcOther, 'GETBOOLEAN');
442 end;
443 
444 {**
445  Gets the value of the designated column in the current row
446  of this <code>ResultSet</code> object as
447  a <code>byte</code> in the Java programming language.
448 
449  @param columnIndex the first column is 1, the second is 2, ...
450  @return the column value; if the value is SQL <code>NULL</code>, the
451  value returned is <code>0</code>
452 }
453 function TZDBLibResultSet.GetByte(ColumnIndex: Integer): Byte;
454 var
455  DL: Integer;
456  Data: Pointer;
457  DT: Integer;
458 begin
459  CheckClosed;
460  CheckColumnIndex(ColumnIndex);
461 
462  DL := FPlainDriver.dbdatlen(FHandle, ColumnIndex);
463  Data := FPlainDriver.dbdata(FHandle, ColumnIndex);
464  DT := DBLibColTypeCache[ColumnIndex];
465  LastWasNull := Data = nil;
466 
467  Result := 0;
468  if Assigned(Data) then
469  begin
470  if DT = FPlainDriver.GetVariables.datatypes[Z_SQLINT1] then
471  Result := PByte(Data)^
472  else
473  begin
474  FPlainDriver.dbconvert(FHandle, DT, Data, DL, FPlainDriver.GetVariables.datatypes[Z_SQLINT1],
475  @Result, SizeOf(Result));
476  end;
477  end;
478  FDBLibConnection.CheckDBLibError(lcOther, 'GETBYTE');
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  a <code>short</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 TZDBLibResultSet.GetShort(ColumnIndex: Integer): SmallInt;
491 var
492  DL: Integer;
493  Data: Pointer;
494  DT: Integer;
495 begin
496  CheckClosed;
497  CheckColumnIndex(ColumnIndex);
498 
499  DL := FPlainDriver.dbdatlen(FHandle, ColumnIndex);
500  Data := FPlainDriver.dbdata(FHandle, ColumnIndex);
501  DT := DBLibColTypeCache[ColumnIndex];
502  LastWasNull := Data = nil;
503 
504  Result := 0;
505  if Assigned(Data) then
506  begin
507  if DT = FPlainDriver.GetVariables.datatypes[Z_SQLINT2] then
508  Result := PSmallInt(Data)^
509  else
510  begin
511  FPlainDriver.dbconvert(FHandle, DT, Data, DL, FPlainDriver.GetVariables.datatypes[Z_SQLINT2],
512  @Result, SizeOf(Result));
513  end;
514  end;
515  FDBLibConnection.CheckDBLibError(lcOther, 'GETSHORT');
516 end;
517 
518 {**
519  Gets the value of the designated column in the current row
520  of this <code>ResultSet</code> object as
521  an <code>int</code> in the Java programming language.
522 
523  @param columnIndex the first column is 1, the second is 2, ...
524  @return the column value; if the value is SQL <code>NULL</code>, the
525  value returned is <code>0</code>
526 }
527 function TZDBLibResultSet.GetInt(ColumnIndex: Integer): Integer;
528 var
529  DL: Integer;
530  Data: Pointer;
531  DT: Integer;
532 begin
533  CheckClosed;
534  CheckColumnIndex(ColumnIndex);
535 
536  DL := FPlainDriver.dbdatlen(FHandle, ColumnIndex);
537  Data := FPlainDriver.dbdata(FHandle, ColumnIndex);
538  DT := DBLibColTypeCache[ColumnIndex];
539  LastWasNull := Data = nil;
540 
541  Result := 0;
542  if Assigned(Data) then
543  begin
544  if DT = FPlainDriver.GetVariables.datatypes[Z_SQLINT4] then
545  Result := PLongint(Data)^
546  else
547  begin
548  FPlainDriver.dbconvert(FHandle, DT, Data, DL, FPlainDriver.GetVariables.datatypes[Z_SQLINT4],
549  @Result, SizeOf(Result));
550  end;
551  end;
552  FDBLibConnection.CheckDBLibError(lcOther, 'GETINT');
553 end;
554 
555 {**
556  Gets the value of the designated column in the current row
557  of this <code>ResultSet</code> object as
558  a <code>long</code> in the Java programming language.
559 
560  @param columnIndex the first column is 1, the second is 2, ...
561  @return the column value; if the value is SQL <code>NULL</code>, the
562  value returned is <code>0</code>
563 }
564 function TZDBLibResultSet.GetLong(ColumnIndex: Integer): Int64;
565 begin
566  Result := GetInt(ColumnIndex);
567 end;
568 
569 {**
570  Gets the value of the designated column in the current row
571  of this <code>ResultSet</code> object as
572  a <code>float</code> in the Java programming language.
573 
574  @param columnIndex the first column is 1, the second is 2, ...
575  @return the column value; if the value is SQL <code>NULL</code>, the
576  value returned is <code>0</code>
577 }
578 function TZDBLibResultSet.GetFloat(ColumnIndex: Integer): Single;
579 var
580  DL: Integer;
581  Data: Pointer;
582  DT: Integer;
583 begin
584  CheckClosed;
585  CheckColumnIndex(ColumnIndex);
586 
587  DL := FPlainDriver.dbdatlen(FHandle, ColumnIndex);
588  Data := FPlainDriver.dbdata(FHandle, ColumnIndex);
589  DT := DBLibColTypeCache[ColumnIndex];
590  LastWasNull := Data = nil;
591 
592  Result := 0;
593  if Assigned(Data) then
594  begin
595  if DT = FPlainDriver.GetVariables.datatypes[Z_SQLFLT4] then
596  Result := PSingle(Data)^
597  else
598  begin
599  FPlainDriver.dbconvert(FHandle, DT, Data, DL, FPlainDriver.GetVariables.datatypes[Z_SQLFLT4],
600  @Result, SizeOf(Result));
601  end;
602  end;
603  FDBLibConnection.CheckDBLibError(lcOther, 'GETFLOAT');
604 end;
605 
606 {**
607  Gets the value of the designated column in the current row
608  of this <code>ResultSet</code> object as
609  a <code>double</code> in the Java programming language.
610 
611  @param columnIndex the first column is 1, the second is 2, ...
612  @return the column value; if the value is SQL <code>NULL</code>, the
613  value returned is <code>0</code>
614 }
615 function TZDBLibResultSet.GetDouble(ColumnIndex: Integer): Double;
616 var
617  DL: Integer;
618  Data: Pointer;
619  DT: Integer;
620 begin
621  CheckClosed;
622  CheckColumnIndex(ColumnIndex);
623 
624  DL := FPlainDriver.dbdatlen(FHandle, ColumnIndex);
625  Data := FPlainDriver.dbdata(FHandle, ColumnIndex);
626  DT := DBLibColTypeCache[ColumnIndex];
627  LastWasNull := Data = nil;
628 
629  Result := 0;
630  if Assigned(Data) then
631  begin
632  if DT = FPlainDriver.GetVariables.datatypes[Z_SQLFLT8] then
633  Result := PDouble(Data)^
634  else
635  begin
636  FPlainDriver.dbconvert(FHandle, DT, Data, DL, FPlainDriver.GetVariables.datatypes[Z_SQLFLT8],
637  @Result, SizeOf(Result));
638  end;
639  end;
640  FDBLibConnection.CheckDBLibError(lcOther, 'GETDOUBLE');
641 end;
642 
643 {**
644  Gets the value of the designated column in the current row
645  of this <code>ResultSet</code> object as
646  a <code>java.sql.BigDecimal</code> in the Java programming language.
647 
648  @param columnIndex the first column is 1, the second is 2, ...
649  @param scale the number of digits to the right of the decimal point
650  @return the column value; if the value is SQL <code>NULL</code>, the
651  value returned is <code>null</code>
652 }
653 function TZDBLibResultSet.GetBigDecimal(ColumnIndex: Integer): Extended;
654 begin
655  Result := GetDouble(ColumnIndex);
656 end;
657 
658 {**
659  Gets the value of the designated column in the current row
660  of this <code>ResultSet</code> object as
661  a <code>byte</code> array in the Java programming language.
662  The bytes represent the raw values returned by the driver.
663 
664  @param columnIndex the first column is 1, the second is 2, ...
665  @return the column value; if the value is SQL <code>NULL</code>, the
666  value returned is <code>null</code>
667 }
668 function TZDBLibResultSet.GetBytes(ColumnIndex: Integer): TByteDynArray;
669 var
670  DL: Integer;
671  Data: Pointer;
672 begin
673  CheckClosed;
674  CheckColumnIndex(ColumnIndex);
675 
676  DL := FPlainDriver.dbdatlen(FHandle, ColumnIndex);
677  Data := FPlainDriver.dbdata(FHandle, ColumnIndex);
678  FDBLibConnection.CheckDBLibError(lcOther, 'GETBYTES');
679  LastWasNull := Data = nil;
680 
681  SetLength(Result, DL);
682  if Assigned(Data) then
683  Move(PAnsiChar(Data)^, Result[0], DL);
684 end;
685 
686 {**
687  Gets the value of the designated column in the current row
688  of this <code>ResultSet</code> object as
689  a <code>java.sql.Date</code> object in the Java programming language.
690 
691  @param columnIndex the first column is 1, the second is 2, ...
692  @return the column value; if the value is SQL <code>NULL</code>, the
693  value returned is <code>null</code>
694 }
695 function TZDBLibResultSet.GetDate(ColumnIndex: Integer): TDateTime;
696 begin
697  Result := System.Int(GetTimestamp(ColumnIndex));
698 end;
699 
700 {**
701  Gets the value of the designated column in the current row
702  of this <code>ResultSet</code> object as
703  a <code>java.sql.Time</code> object in the Java programming language.
704 
705  @param columnIndex the first column is 1, the second is 2, ...
706  @return the column value; if the value is SQL <code>NULL</code>, the
707  value returned is <code>null</code>
708 }
709 function TZDBLibResultSet.GetTime(ColumnIndex: Integer): TDateTime;
710 begin
711  Result := Frac(GetTimestamp(ColumnIndex));
712 end;
713 
714 {**
715  Gets the value of the designated column in the current row
716  of this <code>ResultSet</code> object as
717  a <code>java.sql.Timestamp</code> object in the Java programming language.
718 
719  @param columnIndex the first column is 1, the second is 2, ...
720  @return the column value; if the value is SQL <code>NULL</code>, the
721  value returned is <code>null</code>
722  @exception SQLException if a database access error occurs
723 }
724 function TZDBLibResultSet.GetTimestamp(ColumnIndex: Integer): TDateTime;
725 var
726  DL: Integer;
727  Data: Pointer;
728  DT: Integer;
729  TempDate: DBDATETIME;
730  tdsTempDate: TTDSDBDATETIME;
731 begin
732  CheckClosed;
733  CheckColumnIndex(ColumnIndex);
734 
735  DL := FPlainDriver.dbdatlen(FHandle, ColumnIndex);
736  Data := FPlainDriver.dbdata(FHandle, ColumnIndex);
737  DT := DBLibColTypeCache[ColumnIndex];
738  LastWasNull := Data = nil;
739 
740  Result := 0;
741  if Assigned(Data) then
742  begin
743  if DT = FPlainDriver.GetVariables.datatypes[Z_SQLDATETIME] then
744  if FDBLibConnection.FreeTDS then //type diff
745  Result := PTDSDBDATETIME(Data)^.dtdays + 2 + (PTDSDBDATETIME(Data)^.dttime / 25920000)
746  else
747  Result := PDBDATETIME(Data)^.dtdays + 2 + (PDBDATETIME(Data)^.dttime / 25920000)
748  else
749  if FDBLibConnection.FreeTDS then //type diff
750  begin
751  FPlainDriver.dbconvert(FHandle, DT, Data, DL, FPlainDriver.GetVariables.datatypes[Z_SQLDATETIME],
752  @tdsTempDate, SizeOf(tdsTempDate));
753  Result := tdsTempDate.dtdays + 2 + (tdsTempDate.dttime / 25920000);
754  end
755  else
756  begin
757  FPlainDriver.dbconvert(FHandle, DT, Data, DL, FPlainDriver.GetVariables.datatypes[Z_SQLDATETIME],
758  @TempDate, SizeOf(TempDate));
759  Result := TempDate.dtdays + 2 + (TempDate.dttime / 25920000);
760  end;
761  end;
762  FDBLibConnection.CheckDBLibError(lcOther, 'GETTIMESTAMP');
763 end;
764 
765 {**
766  Returns the value of the designated column in the current row
767  of this <code>ResultSet</code> object as a <code>Blob</code> object
768  in the Java programming language.
769 
770  @param ColumnIndex the first column is 1, the second is 2, ...
771  @return a <code>Blob</code> object representing the SQL <code>BLOB</code> value in
772  the specified column
773 }
774 function TZDBLibResultSet.GetBlob(ColumnIndex: Integer): IZBlob;
775 var
776  DL: Integer;
777  Data: Pointer;
778  TempStream: TStream;
779  TempAnsi: RawByteString;
780 begin
781  CheckClosed;
782  CheckColumnIndex(ColumnIndex);
783  CheckBlobColumn(ColumnIndex);
784 
785  DL := FPlainDriver.dbdatlen(FHandle, ColumnIndex);
786  Data := FPlainDriver.dbdata(FHandle, ColumnIndex);
787  LastWasNull := Data = nil;
788  Result := TZAbstractBlob.CreateWithData(Data, DL, FDBLibConnection);
789  if (GetMetaData.GetColumnType(ColumnIndex) in [stAsciiStream, stUnicodeStream]) then
790  begin
791  TempAnsi := Result.GetString;
792  if ( Length(TempAnsi) = 1) and (TempAnsi[1] = ' ') then
793  TempAnsi := ''
794  else
795  TempAnsi := {$IFDEF WITH_UNITANSISTRINGS}AnsiStrings.{$ENDIF}StringReplace(TempAnsi, #0, '', [rfReplaceAll]);
796  if (GetMetaData.GetColumnType(ColumnIndex) = stAsciiStream ) then
797  Result.SetString(ZEncoding.GetValidatedAnsiString(TempAnsi, ConSettings, True))
798  else
799  begin
800  if TempAnsi = '' then
801  TempStream := TMemoryStream.Create
802  else
803  TempStream := ZEncoding.GetValidatedUnicodeStream(TempAnsi, ConSettings, True);
804  Result.SetStream(TempStream, True);
805  TempStream.Free;
806  end;
807  end;
808 end;
809 
810 {**
811  Moves the cursor to the given row number in
812  this <code>ResultSet</code> object.
813 
814  <p>If the row number is positive, the cursor moves to
815  the given row number with respect to the
816  beginning of the result set. The first row is row 1, the second
817  is row 2, and so on.
818 
819  <p>If the given row number is negative, the cursor moves to
820  an absolute row position with respect to
821  the end of the result set. For example, calling the method
822  <code>absolute(-1)</code> positions the
823  cursor on the last row; calling the method <code>absolute(-2)</code>
824  moves the cursor to the next-to-last row, and so on.
825 
826  <p>An attempt to position the cursor beyond the first/last row in
827  the result set leaves the cursor before the first row or after
828  the last row.
829 
830  <p><B>Note:</B> Calling <code>absolute(1)</code> is the same
831  as calling <code>first()</code>. Calling <code>absolute(-1)</code>
832  is the same as calling <code>last()</code>.
833 
834  @return <code>true</code> if the cursor is on the result set;
835  <code>false</code> otherwise
836 }
837 function TZDBLibResultSet.MoveAbsolute(Row: Integer): Boolean;
838 begin
839  Result := False;
840  RaiseUnsupportedException;
841 end;
842 
843 {**
844  Moves the cursor down one row from its current position.
845  A <code>ResultSet</code> cursor is initially positioned
846  before the first row; the first call to the method
847  <code>next</code> makes the first row the current row; the
848  second call makes the second row the current row, and so on.
849 
850  <P>If an input stream is open for the current row, a call
851  to the method <code>next</code> will
852  implicitly close it. A <code>ResultSet</code> object's
853  warning chain is cleared when a new row is read.
854 
855  @return <code>true</code> if the new current row is valid;
856  <code>false</code> if there are no more rows
857 }
858 function TZDBLibResultSet.Next: Boolean;
859 begin
860  Result := False;
861  if FPlainDriver.GetProtocol = 'mssql' then
862  if FPlainDriver.dbDead(FHandle) then
863  Exit;
864 //!!! maybe an error message other than dbconnection is dead should be raised
865  case FPlainDriver.dbnextrow(FHandle) of
866  REG_ROW: Result := True;
867  NO_MORE_ROWS: ;
868  DBFAIL: FDBLibConnection.CheckDBLibError(lcOther, 'NEXT');
869  BUF_FULL: ;//should not happen because we are not using dblibc buffering.
870  else
871  // If a compute row is read, the computeid of the row is returned
872  Result := False;
873  end;
874 end;
875 
876 
877 { TZDBLibCachedResolver }
878 
879 {**
880  Creates a DBLib specific cached resolver object.
881  @param PlainDriver a native DBLib plain driver.
882  @param Handle a DBLib specific query handle.
883  @param Statement a related SQL statement object.
884  @param Metadata a resultset metadata reference.
885 }
886 constructor TZDBLibCachedResolver.Create(Statement: IZStatement;
887  Metadata: IZResultSetMetadata);
888 begin
889  inherited Create(Statement, Metadata);
890 
891  { Defines an index of autoincrement field. }
892  FAutoColumnIndex := -1;
893 end;
894 
895 {**
896  Posts updates to database.
897  @param Sender a cached result set object.
898  @param UpdateType a type of updates.
899  @param OldRowAccessor an accessor object to old column values.
900  @param NewRowAccessor an accessor object to new column values.
901 }
902 procedure TZDBLibCachedResolver.PostUpdates(Sender: IZCachedResultSet;
903  UpdateType: TZRowUpdateType; OldRowAccessor, NewRowAccessor: TZRowAccessor);
904 var
905  Statement: IZStatement;
906  ResultSet: IZResultSet;
907  I: Integer;
908 begin
909  inherited PostUpdates(Sender, UpdateType, OldRowAccessor, NewRowAccessor);
910 
911  { Defines an index of autoincrement field. }
912  if FAutoColumnIndex = -1 then
913  begin
914  FAutoColumnIndex := 0;
915  for I := 1 to Metadata.GetColumnCount do
916  begin
917  if Metadata.IsAutoIncrement(I) then
918  begin
919  FAutoColumnIndex := I;
920  Break;
921  end;
922  end;
923  end;
924 
925  if (UpdateType = utInserted) and (FAutoColumnIndex > 0)
926  and OldRowAccessor.IsNull(FAutoColumnIndex) then
927  begin
928  Statement := Connection.CreateStatement;
929  ResultSet := Statement.ExecuteQuery('SELECT @@IDENTITY');
930  try
931  if ResultSet.Next then
932  NewRowAccessor.SetLong(FAutoColumnIndex, ResultSet.GetLong(1));
933  finally
934  ResultSet.Close;
935  Statement.Close;
936  end;
937  end;
938 end;
939 
940 end.
941