00001 {*********************************************************}
00002 { }
00003 { Zeos Database Objects }
00004 { MySQL Database Connectivity Classes }
00005 { }
00006 { Originally written by Sergey Seroukhov }
00007 { }
00008 {*********************************************************}
00009
00010 {@********************************************************}
00011 { Copyright (c) 1999-2006 Zeos Development Group }
00012 { }
00013 { License Agreement: }
00014 { }
00015 { This library is distributed in the hope that it will be }
00016 { useful, but WITHOUT ANY WARRANTY; without even the }
00017 { implied warranty of MERCHANTABILITY or FITNESS FOR }
00018 { A PARTICULAR PURPOSE. See the GNU Lesser General }
00019 { Public License for more details. }
00020 { }
00021 { The source code of the ZEOS Libraries and packages are }
00022 { distributed under the Library GNU General Public }
00023 { License (see the file COPYING / COPYING.ZEOS) }
00024 { with the following modification: }
00025 { As a special exception, the copyright holders of this }
00026 { library give you permission to link this library with }
00027 { independent modules to produce an executable, }
00028 { regardless of the license terms of these independent }
00029 { modules, and to copy and distribute the resulting }
00030 { executable under terms of your choice, provided that }
00031 { you also meet, for each linked independent module, }
00032 { the terms and conditions of the license of that module. }
00033 { An independent module is a module which is not derived }
00034 { from or based on this library. If you modify this }
00035 { library, you may extend this exception to your version }
00036 { of the library, but you are not obligated to do so. }
00037 { If you do not wish to do so, delete this exception }
00038 { statement from your version. }
00039 { }
00040 { }
00041 { The project web site is located on: }
00042 { http:
00043 { http:
00044 { svn:
00045 { }
00046 { http:
00047 { http:
00048 { }
00049 { }
00050 { }
00051 { Zeos Development Group. }
00052 {********************************************************@}
00053
00054 unit ZDbcMySqlResultSet;
00055
00056 interface
00057
00058 {$I ZDbc.inc}
00059
00060 uses
00061 Classes, SysUtils, ZSysUtils, ZDbcIntfs,
00062 Contnrs, ZDbcResultSet, ZDbcResultSetMetadata,
00063 ZCompatibility, ZDbcCache, ZDbcCachedResultSet, ZDbcGenericResolver,
00064 ZDbcMysqlStatement, ZPlainMySqlDriver, ZPlainMySqlConstants;
00065
00066 type
00067
00068 {** Implements MySQL ResultSet Metadata. }
00069 TZMySQLResultSetMetadata = class(TZAbstractResultSetMetadata)
00070 public
00071 function GetColumnType(Column: Integer): TZSQLType; override;
00072 function IsNullable(Column: Integer): TZColumnNullableType; override;
00073 end;
00074
00075 {** Implements MySQL ResultSet. }
00076 TZMySQLResultSet = class(TZAbstractResultSet)
00077 private
00078 FHandle: PZMySQLConnect;
00079 FQueryHandle: PZMySQLResult;
00080 FRowHandle: PZMySQLRow;
00081 FPlainDriver: IZMySQLPlainDriver;
00082 FUseResult: Boolean;
00083 protected
00084 procedure Open; override;
00085 public
00086 constructor Create(PlainDriver: IZMySQLPlainDriver; Statement: IZStatement;
00087 SQL: string; Handle: PZMySQLConnect; UseResult: Boolean);
00088 destructor Destroy; override;
00089
00090 procedure Close; override;
00091
00092 function IsNull(ColumnIndex: Integer): Boolean; override;
00093 function GetPChar(ColumnIndex: Integer): PChar; override;
00094 function GetString(ColumnIndex: Integer): string; override;
00095 function GetBoolean(ColumnIndex: Integer): Boolean; override;
00096 function GetByte(ColumnIndex: Integer): ShortInt; override;
00097 function GetShort(ColumnIndex: Integer): SmallInt; override;
00098 function GetInt(ColumnIndex: Integer): Integer; override;
00099 function GetLong(ColumnIndex: Integer): Int64; override;
00100 function GetFloat(ColumnIndex: Integer): Single; override;
00101 function GetDouble(ColumnIndex: Integer): Double; override;
00102 function GetBigDecimal(ColumnIndex: Integer): Extended; override;
00103 function GetBytes(ColumnIndex: Integer): TByteDynArray; override;
00104 function GetDate(ColumnIndex: Integer): TDateTime; override;
00105 function GetTime(ColumnIndex: Integer): TDateTime; override;
00106 function GetTimestamp(ColumnIndex: Integer): TDateTime; override;
00107 function GetAsciiStream(ColumnIndex: Integer): TStream; override;
00108 function GetUnicodeStream(ColumnIndex: Integer): TStream; override;
00109 function GetBinaryStream(ColumnIndex: Integer): TStream; override;
00110 function GetBlob(ColumnIndex: Integer): IZBlob; override;
00111
00112 function MoveAbsolute(Row: Integer): Boolean; override;
00113 function Next: Boolean; override;
00114 end;
00115
00116 {** Implements a cached resolver with MySQL specific functionality. }
00117 TZMySQLCachedResolver = class (TZGenericCachedResolver, IZCachedResolver)
00118 private
00119 FHandle: PZMySQLConnect;
00120 FPlainDriver: IZMySQLPlainDriver;
00121 FAutoColumnIndex: Integer;
00122 public
00123 constructor Create(PlainDriver: IZMySQLPlainDriver; Handle: PZMySQLConnect;
00124 Statement: IZStatement; Metadata: IZResultSetMetadata);
00125
00126 procedure PostUpdates(Sender: IZCachedResultSet; UpdateType: TZRowUpdateType;
00127 OldRowAccessor, NewRowAccessor: TZRowAccessor); override;
00128
00129
00130 function FormCalculateStatement(Columns: TObjectList): string; override;
00131
00132 {BEGIN of PATCH [1185969]: Do tasks after posting updates. ie: Updating AutoInc fields in MySQL }
00133 procedure UpdateAutoIncrementFields(Sender: IZCachedResultSet; UpdateType: TZRowUpdateType;
00134 OldRowAccessor, NewRowAccessor: TZRowAccessor; Resolver: IZCachedResolver); override;
00135 {END of PATCH [1185969]: Do tasks after posting updates. ie: Updating AutoInc fields in MySQL }
00136 end;
00137
00138 implementation
00139
00140 uses
00141 Math, ZMessages, ZDbcMySqlUtils, ZMatchPattern, ZDbcMysql;
00142
00143 { TZMySQLResultSetMetadata }
00144
00145 {**
00146 Retrieves the designated column's SQL type.
00147 @param column the first column is 1, the second is 2, ...
00148 @return SQL type from java.sql.Types
00149 }
00150 function TZMySQLResultSetMetadata.GetColumnType(Column: Integer): TZSQLType;
00151 begin
00152 if not Loaded then LoadColumns;
00153 Result := TZColumnInfo(ResultSet.ColumnsInfo[Column - 1]).ColumnType;
00154 end;
00155
00156 {**
00157 Indicates the nullability of values in the designated column.
00158 @param column the first column is 1, the second is 2, ...
00159 @return the nullability status of the given column; one of <code>columnNoNulls</code>,
00160 <code>columnNullable</code> or <code>columnNullableUnknown</code>
00161 }
00162 function TZMySQLResultSetMetadata.IsNullable(Column: Integer):
00163 TZColumnNullableType;
00164 begin
00165 {$IFNDEF FPC}
00166 {$IFDEF VER130BELOW}
00167 // Delphi 5 checks required fields (= not nullable) during post, where we can't avoid it. So we
00168
00169
00170
00171 if IsAutoIncrement(Column) then
00172 Result := ntNullable
00173 else
00174 {$ENDIF}
00175 {$ENDIF}
00176 Result := inherited IsNullable(Column);
00177 end;
00178
00179 { TZMySQLResultSet }
00180
00181 {**
00182 Constructs this object, assignes main properties and
00183 opens the record set.
00184 @param PlainDriver a native MySQL plain driver.
00185 @param Statement a related SQL statement object.
00186 @param Handle a MySQL specific query handle.
00187 @param UseResult <code>True</code> to use results,
00188 <code>False</code> to store result.
00189 }
00190 constructor TZMySQLResultSet.Create(PlainDriver: IZMySQLPlainDriver;
00191 Statement: IZStatement; SQL: string; Handle: PZMySQLConnect;
00192 UseResult: Boolean);
00193 begin
00194 inherited Create(Statement, SQL, TZMySQLResultSetMetadata.Create(
00195 Statement.GetConnection.GetMetadata, SQL, Self));
00196
00197 FHandle := Handle;
00198 FQueryHandle := nil;
00199 FRowHandle := nil;
00200 FPlainDriver := PlainDriver;
00201 ResultSetConcurrency := rcReadOnly;
00202 FUseResult := UseResult;
00203
00204 Open;
00205 end;
00206
00207 {**
00208 Destroys this object and cleanups the memory.
00209 }
00210 destructor TZMySQLResultSet.Destroy;
00211 begin
00212 inherited Destroy;
00213 end;
00214
00215 {**
00216 Opens this recordset.
00217 }
00218 procedure TZMySQLResultSet.Open;
00219 var
00220 I: Integer;
00221 ColumnInfo: TZColumnInfo;
00222 FieldHandle: PZMySQLField;
00223 FieldFlags: Integer;
00224 begin
00225 if ResultSetConcurrency = rcUpdatable then
00226 raise EZSQLException.Create(SLiveResultSetsAreNotSupported);
00227
00228 if FUseResult then
00229 begin
00230 FQueryHandle := FPlainDriver.UseResult(FHandle);
00231 LastRowNo := 0;
00232 end
00233 else
00234 begin
00235 FQueryHandle := FPlainDriver.StoreResult(FHandle);
00236 if Assigned(FQueryHandle) then
00237 LastRowNo := FPlainDriver.GetRowCount(FQueryHandle)
00238 else LastRowNo := 0;
00239 end;
00240
00241 if not Assigned(FQueryHandle) then
00242 raise EZSQLException.Create(SCanNotRetrieveResultSetData);
00243
00244 { Fills the column info. }
00245 ColumnsInfo.Clear;
00246 for I := 0 to FPlainDriver.GetFieldCount(FQueryHandle) - 1 do
00247 begin
00248 FPlainDriver.SeekField(FQueryHandle, I);
00249 FieldHandle := FPlainDriver.FetchField(FQueryHandle);
00250 if FieldHandle = nil then
00251 Break;
00252
00253 ColumnInfo := TZColumnInfo.Create;
00254 with ColumnInfo do
00255 begin
00256 FieldFlags := FPlainDriver.GetFieldFlags(FieldHandle);
00257
00258 ColumnLabel := FPlainDriver.GetFieldName(FieldHandle);
00259 TableName := FPlainDriver.GetFieldTable(FieldHandle);
00260 ReadOnly := (FPlainDriver.GetFieldTable(FieldHandle) = '');
00261 ColumnType := ConvertMySQLHandleToSQLType(FPlainDriver,
00262 FieldHandle, FieldFlags);
00263 ColumnDisplaySize := FPlainDriver.GetFieldLength(FieldHandle);
00264 Precision := Max(FPlainDriver.GetFieldMaxLength(FieldHandle),
00265 FPlainDriver.GetFieldLength(FieldHandle));
00266 Scale := FPlainDriver.GetFieldDecimals(FieldHandle);
00267 if (AUTO_INCREMENT_FLAG and FieldFlags <> 0)
00268 or (TIMESTAMP_FLAG and FieldFlags <> 0) then
00269 AutoIncrement := True;
00270 if UNSIGNED_FLAG and FieldFlags <> 0 then
00271 Signed := False
00272 else Signed := True;
00273 if NOT_NULL_FLAG and FieldFlags <> 0 then
00274 Nullable := ntNoNulls
00275 else Nullable := ntNullable;
00276
00277 end;
00278
00279 ColumnsInfo.Add(ColumnInfo);
00280 end;
00281
00282 inherited Open;
00283 end;
00284
00285 {**
00286 Releases this <code>ResultSet</code> object's database and
00287 JDBC resources immediately instead of waiting for
00288 this to happen when it is automatically closed.
00289
00290 <P><B>Note:</B> A <code>ResultSet</code> object
00291 is automatically closed by the
00292 <code>Statement</code> object that generated it when
00293 that <code>Statement</code> object is closed,
00294 re-executed, or is used to retrieve the next result from a
00295 sequence of multiple results. A <code>ResultSet</code> object
00296 is also automatically closed when it is garbage collected.
00297 }
00298 procedure TZMySQLResultSet.Close;
00299 begin
00300 if FQueryHandle <> nil then
00301 FPlainDriver.FreeResult(FQueryHandle);
00302 FQueryHandle := nil;
00303 FRowHandle := nil;
00304 inherited Close;
00305 end;
00306
00307 {**
00308 Indicates if the value of the designated column in the current row
00309 of this <code>ResultSet</code> object is Null.
00310
00311 @param columnIndex the first column is 1, the second is 2, ...
00312 @return if the value is SQL <code>NULL</code>, the
00313 value returned is <code>true</code>. <code>false</code> otherwise.
00314 }
00315 function TZMySQLResultSet.IsNull(ColumnIndex: Integer): Boolean;
00316 var
00317 Temp: PChar;
00318 begin
00319 {$IFNDEF DISABLE_CHECKING}
00320 CheckClosed;
00321 if FRowHandle = nil then
00322 raise EZSQLException.Create(SRowDataIsNotAvailable);
00323 {$ENDIF}
00324
00325 Temp := FPlainDriver.GetFieldData(FRowHandle, ColumnIndex - 1);
00326 Result := (Temp = nil);
00327 if not Result and (TZAbstractResultSetMetadata(Metadata).
00328 GetColumnType(ColumnIndex) in [stDate, stTimestamp]) then
00329 begin
00330 Result := (AnsiSQLDateToDateTime(Temp) = 0)
00331 and (TimestampStrToDateTime(Temp) = 0);
00332 end;
00333 end;
00334
00335 {**
00336 Gets the value of the designated column in the current row
00337 of this <code>ResultSet</code> object as
00338 a <code>PChar</code> in the Delphi programming language.
00339
00340 @param columnIndex the first column is 1, the second is 2, ...
00341 @return the column value; if the value is SQL <code>NULL</code>, the
00342 value returned is <code>null</code>
00343 }
00344 function TZMySQLResultSet.GetPChar(ColumnIndex: Integer): PChar;
00345 begin
00346 {$IFNDEF DISABLE_CHECKING}
00347 CheckClosed;
00348 if FRowHandle = nil then
00349 raise EZSQLException.Create(SRowDataIsNotAvailable);
00350 {$ENDIF}
00351
00352 Result := FPlainDriver.GetFieldData(FRowHandle, ColumnIndex - 1);
00353 LastWasNull := Result = nil;
00354 end;
00355
00356 {**
00357 Gets the value of the designated column in the current row
00358 of this <code>ResultSet</code> object as
00359 a <code>String</code> in the Java programming language.
00360
00361 @param columnIndex the first column is 1, the second is 2, ...
00362 @return the column value; if the value is SQL <code>NULL</code>, the
00363 value returned is <code>null</code>
00364 }
00365 function TZMySQLResultSet.GetString(ColumnIndex: Integer): string;
00366 var
00367 LengthPointer: PLongInt;
00368 Length: LongInt;
00369 Buffer: PChar;
00370 begin
00371 {$IFNDEF DISABLE_CHECKING}
00372 CheckClosed;
00373 if FRowHandle = nil then
00374 raise EZSQLException.Create(SRowDataIsNotAvailable);
00375 {$ENDIF}
00376
00377 ColumnIndex := ColumnIndex - 1;
00378 LengthPointer := FPlainDriver.FetchLengths(FQueryHandle);
00379 if LengthPointer <> nil then
00380 Length := PLongInt(LongInt(LengthPointer) + ColumnIndex * SizeOf(LongInt))^
00381 else Length := 0;
00382 Buffer := FPlainDriver.GetFieldData(FRowHandle, ColumnIndex);
00383 LastWasNull := Buffer = nil;
00384 Result := '';
00385 if not LastWasNull then
00386 SetString(Result, Buffer, Length);
00387 end;
00388
00389 {**
00390 Gets the value of the designated column in the current row
00391 of this <code>ResultSet</code> object as
00392 a <code>boolean</code> in the Java programming language.
00393
00394 @param columnIndex the first column is 1, the second is 2, ...
00395 @return the column value; if the value is SQL <code>NULL</code>, the
00396 value returned is <code>false</code>
00397 }
00398 function TZMySQLResultSet.GetBoolean(ColumnIndex: Integer): Boolean;
00399 var
00400 Temp: string;
00401 begin
00402 {$IFNDEF DISABLE_CHECKING}
00403 CheckColumnConvertion(ColumnIndex, stBoolean);
00404 {$ENDIF}
00405 Temp := UpperCase(GetPChar(ColumnIndex));
00406 Result := (Temp = 'Y') or (Temp = 'YES') or (Temp = 'T') or
00407 (Temp = 'TRUE') or (StrToIntDef(Temp, 0) <> 0);
00408 end;
00409
00410 {**
00411 Gets the value of the designated column in the current row
00412 of this <code>ResultSet</code> object as
00413 a <code>byte</code> in the Java programming language.
00414
00415 @param columnIndex the first column is 1, the second is 2, ...
00416 @return the column value; if the value is SQL <code>NULL</code>, the
00417 value returned is <code>0</code>
00418 }
00419 function TZMySQLResultSet.GetByte(ColumnIndex: Integer): ShortInt;
00420 begin
00421 {$IFNDEF DISABLE_CHECKING}
00422 CheckColumnConvertion(ColumnIndex, stByte);
00423 {$ENDIF}
00424 Result := ShortInt(StrToIntDef(GetPChar(ColumnIndex), 0));
00425 end;
00426
00427 {**
00428 Gets the value of the designated column in the current row
00429 of this <code>ResultSet</code> object as
00430 a <code>short</code> in the Java programming language.
00431
00432 @param columnIndex the first column is 1, the second is 2, ...
00433 @return the column value; if the value is SQL <code>NULL</code>, the
00434 value returned is <code>0</code>
00435 }
00436 function TZMySQLResultSet.GetShort(ColumnIndex: Integer): SmallInt;
00437 begin
00438 {$IFNDEF DISABLE_CHECKING}
00439 CheckColumnConvertion(ColumnIndex, stShort);
00440 {$ENDIF}
00441 Result := SmallInt(StrToIntDef(GetPChar(ColumnIndex), 0));
00442 end;
00443
00444 {**
00445 Gets the value of the designated column in the current row
00446 of this <code>ResultSet</code> object as
00447 an <code>int</code> in the Java programming language.
00448
00449 @param columnIndex the first column is 1, the second is 2, ...
00450 @return the column value; if the value is SQL <code>NULL</code>, the
00451 value returned is <code>0</code>
00452 }
00453 function TZMySQLResultSet.GetInt(ColumnIndex: Integer): Integer;
00454 begin
00455 {$IFNDEF DISABLE_CHECKING}
00456 CheckColumnConvertion(ColumnIndex, stInteger);
00457 {$ENDIF}
00458 Result := StrToIntDef(GetPChar(ColumnIndex), 0);
00459 end;
00460
00461 {**
00462 Gets the value of the designated column in the current row
00463 of this <code>ResultSet</code> object as
00464 a <code>long</code> in the Java programming language.
00465
00466 @param columnIndex the first column is 1, the second is 2, ...
00467 @return the column value; if the value is SQL <code>NULL</code>, the
00468 value returned is <code>0</code>
00469 }
00470 function TZMySQLResultSet.GetLong(ColumnIndex: Integer): Int64;
00471 begin
00472 {$IFNDEF DISABLE_CHECKING}
00473 CheckColumnConvertion(ColumnIndex, stLong);
00474 {$ENDIF}
00475 Result := StrToInt64Def(GetPChar(ColumnIndex), 0);
00476 end;
00477
00478 {**
00479 Gets the value of the designated column in the current row
00480 of this <code>ResultSet</code> object as
00481 a <code>float</code> in the Java programming language.
00482
00483 @param columnIndex the first column is 1, the second is 2, ...
00484 @return the column value; if the value is SQL <code>NULL</code>, the
00485 value returned is <code>0</code>
00486 }
00487 function TZMySQLResultSet.GetFloat(ColumnIndex: Integer): Single;
00488 begin
00489 {$IFNDEF DISABLE_CHECKING}
00490 CheckColumnConvertion(ColumnIndex, stFloat);
00491 {$ENDIF}
00492 Result := SQLStrToFloatDef(GetPChar(ColumnIndex), 0);
00493 end;
00494
00495 {**
00496 Gets the value of the designated column in the current row
00497 of this <code>ResultSet</code> object as
00498 a <code>double</code> in the Java programming language.
00499
00500 @param columnIndex the first column is 1, the second is 2, ...
00501 @return the column value; if the value is SQL <code>NULL</code>, the
00502 value returned is <code>0</code>
00503 }
00504 function TZMySQLResultSet.GetDouble(ColumnIndex: Integer): Double;
00505 begin
00506 {$IFNDEF DISABLE_CHECKING}
00507 CheckColumnConvertion(ColumnIndex, stDouble);
00508 {$ENDIF}
00509 Result := SQLStrToFloatDef(GetPChar(ColumnIndex), 0);
00510 end;
00511
00512 {**
00513 Gets the value of the designated column in the current row
00514 of this <code>ResultSet</code> object as
00515 a <code>java.sql.BigDecimal</code> in the Java programming language.
00516
00517 @param columnIndex the first column is 1, the second is 2, ...
00518 @param scale the number of digits to the right of the decimal point
00519 @return the column value; if the value is SQL <code>NULL</code>, the
00520 value returned is <code>null</code>
00521 }
00522 function TZMySQLResultSet.GetBigDecimal(ColumnIndex: Integer): Extended;
00523 begin
00524 {$IFNDEF DISABLE_CHECKING}
00525 CheckColumnConvertion(ColumnIndex, stBigDecimal);
00526 {$ENDIF}
00527 Result := SQLStrToFloatDef(GetPChar(ColumnIndex), 0);
00528 end;
00529
00530 {**
00531 Gets the value of the designated column in the current row
00532 of this <code>ResultSet</code> object as
00533 a <code>byte</code> array in the Java programming language.
00534 The bytes represent the raw values returned by the driver.
00535
00536 @param columnIndex the first column is 1, the second is 2, ...
00537 @return the column value; if the value is SQL <code>NULL</code>, the
00538 value returned is <code>null</code>
00539 }
00540 function TZMySQLResultSet.GetBytes(ColumnIndex: Integer): TByteDynArray;
00541 begin
00542 {$IFNDEF DISABLE_CHECKING}
00543 CheckColumnConvertion(ColumnIndex, stBytes);
00544 {$ENDIF}
00545 Result := StrToBytes(GetString(ColumnIndex));
00546 end;
00547
00548 {**
00549 Gets the value of the designated column in the current row
00550 of this <code>ResultSet</code> object as
00551 a <code>java.sql.Date</code> object in the Java programming language.
00552
00553 @param columnIndex the first column is 1, the second is 2, ...
00554 @return the column value; if the value is SQL <code>NULL</code>, the
00555 value returned is <code>null</code>
00556 }
00557 function TZMySQLResultSet.GetDate(ColumnIndex: Integer): TDateTime;
00558 var
00559 Value: string;
00560 begin
00561 {$IFNDEF DISABLE_CHECKING}
00562 CheckColumnConvertion(ColumnIndex, stDate);
00563 {$ENDIF}
00564 Value := GetPChar(ColumnIndex);
00565
00566 LastWasNull := (LastWasNull or (Copy(Value, 1, 10)='0000-00-00'));
00567 if LastWasNull then
00568 begin
00569 Result := 0;
00570 Exit;
00571 end;
00572
00573 if IsMatch('????-??-??*', Value) then
00574 Result := Trunc(AnsiSQLDateToDateTime(Value))
00575 else Result := Trunc(TimestampStrToDateTime(Value));
00576 LastWasNull := Result = 0;
00577 end;
00578
00579 {**
00580 Gets the value of the designated column in the current row
00581 of this <code>ResultSet</code> object as
00582 a <code>java.sql.Time</code> object in the Java programming language.
00583
00584 @param columnIndex the first column is 1, the second is 2, ...
00585 @return the column value; if the value is SQL <code>NULL</code>, the
00586 value returned is <code>null</code>
00587 }
00588 function TZMySQLResultSet.GetTime(ColumnIndex: Integer): TDateTime;
00589 var
00590 Value: string;
00591 begin
00592 {$IFNDEF DISABLE_CHECKING}
00593 CheckColumnConvertion(ColumnIndex, stTime);
00594 {$ENDIF}
00595 Value := GetPChar(ColumnIndex);
00596
00597 LastWasNull := (LastWasNull or (Copy(Value, 1, 8)='00:00:00'));
00598 if LastWasNull then
00599 begin
00600 Result := 0;
00601 Exit;
00602 end;
00603
00604 if IsMatch('*??:??:??*', Value) then
00605 Result := Frac(AnsiSQLDateToDateTime(Value))
00606 else Result := Frac(TimestampStrToDateTime(Value));
00607 end;
00608
00609 {**
00610 Gets the value of the designated column in the current row
00611 of this <code>ResultSet</code> object as
00612 a <code>java.sql.Timestamp</code> object in the Java programming language.
00613
00614 @param columnIndex the first column is 1, the second is 2, ...
00615 @return the column value; if the value is SQL <code>NULL</code>, the
00616 value returned is <code>null</code>
00617 @exception SQLException if a database access error occurs
00618 }
00619 function TZMySQLResultSet.GetTimestamp(ColumnIndex: Integer): TDateTime;
00620 var
00621 Temp: string;
00622 begin
00623 {$IFNDEF DISABLE_CHECKING}
00624 CheckColumnConvertion(ColumnIndex, stTimestamp);
00625 {$ENDIF}
00626 Temp := GetPChar(ColumnIndex);
00627
00628 if LastWasNull then
00629 begin
00630 Result := 0;
00631 Exit;
00632 end;
00633
00634 if IsMatch('????-??-??*', Temp) then
00635 Result := AnsiSQLDateToDateTime(Temp)
00636 else Result := TimestampStrToDateTime(Temp);
00637 LastWasNull := Result = 0;
00638 end;
00639
00640 {**
00641 Gets the value of the designated column in the current row
00642 of this <code>ResultSet</code> object as
00643 a stream of ASCII characters. The value can then be read in chunks from the
00644 stream. This method is particularly
00645 suitable for retrieving large <char>LONGVARCHAR</char> values.
00646 The JDBC driver will
00647 do any necessary conversion from the database format into ASCII.
00648
00649 <P><B>Note:</B> All the data in the returned stream must be
00650 read prior to getting the value of any other column. The next
00651 call to a <code>getXXX</code> method implicitly closes the stream. Also, a
00652 stream may return <code>0</code> when the method
00653 <code>InputStream.available</code>
00654 is called whether there is data available or not.
00655
00656 @param columnIndex the first column is 1, the second is 2, ...
00657 @return a Java input stream that delivers the database column value
00658 as a stream of one-byte ASCII characters; if the value is SQL
00659 <code>NULL</code>, the value returned is <code>null</code>
00660 }
00661 function TZMySQLResultSet.GetAsciiStream(ColumnIndex: Integer): TStream;
00662 begin
00663 {$IFNDEF DISABLE_CHECKING}
00664 CheckColumnConvertion(ColumnIndex, stAsciiStream);
00665 {$ENDIF}
00666 Result := TStringStream.Create(GetString(ColumnIndex));
00667 end;
00668
00669 {**
00670 Gets the value of a column in the current row as a stream of
00671 Gets the value of the designated column in the current row
00672 of this <code>ResultSet</code> object as
00673 as a stream of Unicode characters.
00674 The value can then be read in chunks from the
00675 stream. This method is particularly
00676 suitable for retrieving large<code>LONGVARCHAR</code>values. The JDBC driver will
00677 do any necessary conversion from the database format into Unicode.
00678 The byte format of the Unicode stream must be Java UTF-8,
00679 as specified in the Java virtual machine specification.
00680
00681 <P><B>Note:</B> All the data in the returned stream must be
00682 read prior to getting the value of any other column. The next
00683 call to a <code>getXXX</code> method implicitly closes the stream. Also, a
00684 stream may return <code>0</code> when the method
00685 <code>InputStream.available</code>
00686 is called whether there is data available or not.
00687
00688 @param columnIndex the first column is 1, the second is 2, ...
00689 @return a Java input stream that delivers the database column value
00690 as a stream in Java UTF-8 byte format; if the value is SQL
00691 <code>NULL</code>, the value returned is <code>null</code>
00692 }
00693 function TZMySQLResultSet.GetUnicodeStream(ColumnIndex: Integer): TStream;
00694 begin
00695 {$IFNDEF DISABLE_CHECKING}
00696 CheckColumnConvertion(ColumnIndex, stUnicodeStream);
00697 {$ENDIF}
00698 Result := nil;
00699 end;
00700
00701 {**
00702 Gets the value of a column in the current row as a stream of
00703 Gets the value of the designated column in the current row
00704 of this <code>ResultSet</code> object as a binary stream of
00705 uninterpreted bytes. The value can then be read in chunks from the
00706 stream. This method is particularly
00707 suitable for retrieving large <code>LONGVARBINARY</code> values.
00708
00709 <P><B>Note:</B> All the data in the returned stream must be
00710 read prior to getting the value of any other column. The next
00711 call to a <code>getXXX</code> method implicitly closes the stream. Also, a
00712 stream may return <code>0</code> when the method
00713 <code>InputStream.available</code>
00714 is called whether there is data available or not.
00715
00716 @param columnIndex the first column is 1, the second is 2, ...
00717 @return a Java input stream that delivers the database column value
00718 as a stream of uninterpreted bytes;
00719 if the value is SQL <code>NULL</code>, the value returned is <code>null</code>
00720 }
00721 function TZMySQLResultSet.GetBinaryStream(ColumnIndex: Integer): TStream;
00722 begin
00723 {$IFNDEF DISABLE_CHECKING}
00724 CheckColumnConvertion(ColumnIndex, stBinaryStream);
00725 {$ENDIF}
00726 Result := TStringStream.Create(GetString(ColumnIndex));
00727 end;
00728
00729 {**
00730 Returns the value of the designated column in the current row
00731 of this <code>ResultSet</code> object as a <code>Blob</code> object
00732 in the Java programming language.
00733
00734 @param ColumnIndex the first column is 1, the second is 2, ...
00735 @return a <code>Blob</code> object representing the SQL <code>BLOB</code> value in
00736 the specified column
00737 }
00738 function TZMySQLResultSet.GetBlob(ColumnIndex: Integer): IZBlob;
00739 var
00740 Stream: TStream;
00741 begin
00742 {$IFNDEF DISABLE_CHECKING}
00743 CheckBlobColumn(ColumnIndex);
00744 {$ENDIF}
00745 Stream := nil;
00746 try
00747 if not IsNull(ColumnIndex) then
00748 begin
00749 Stream := TStringStream.Create(GetString(ColumnIndex));
00750 Result := TZAbstractBlob.CreateWithStream(Stream)
00751 end else
00752 Result := TZAbstractBlob.CreateWithStream(nil);
00753 finally
00754 if Assigned(Stream) then
00755 Stream.Free;
00756 end;
00757 end;
00758
00759 {**
00760 Moves the cursor to the given row number in
00761 this <code>ResultSet</code> object.
00762
00763 <p>If the row number is positive, the cursor moves to
00764 the given row number with respect to the
00765 beginning of the result set. The first row is row 1, the second
00766 is row 2, and so on.
00767
00768 <p>If the given row number is negative, the cursor moves to
00769 an absolute row position with respect to
00770 the end of the result set. For example, calling the method
00771 <code>absolute(-1)</code> positions the
00772 cursor on the last row; calling the method <code>absolute(-2)</code>
00773 moves the cursor to the next-to-last row, and so on.
00774
00775 <p>An attempt to position the cursor beyond the first/last row in
00776 the result set leaves the cursor before the first row or after
00777 the last row.
00778
00779 <p><B>Note:</B> Calling <code>absolute(1)</code> is the same
00780 as calling <code>first()</code>. Calling <code>absolute(-1)</code>
00781 is the same as calling <code>last()</code>.
00782
00783 @return <code>true</code> if the cursor is on the result set;
00784 <code>false</code> otherwise
00785 }
00786 function TZMySQLResultSet.MoveAbsolute(Row: Integer): Boolean;
00787 begin
00788 CheckClosed;
00789
00790 { Checks for maximum row. }
00791 Result := False;
00792 if (MaxRows > 0) and (Row > MaxRows) then
00793 Exit;
00794
00795 if not FUseResult then
00796 begin
00797 { Process negative rows. }
00798 if Row < 0 then
00799 begin
00800 Row := LastRowNo - Row + 1;
00801 if Row < 0 then Row := 0;
00802 end;
00803
00804 if (Row >= 0) and (Row <= LastRowNo + 1) then
00805 begin
00806 RowNo := Row;
00807 if (Row >= 1) and (Row <= LastRowNo) then
00808 begin
00809 FPlainDriver.SeekData(FQueryHandle, RowNo - 1);
00810 FRowHandle := FPlainDriver.FetchRow(FQueryHandle);
00811 end else
00812 FRowHandle := nil;
00813 end;
00814 Result := FRowHandle <> nil;
00815 end else
00816 RaiseForwardOnlyException;
00817 end;
00818
00819 {**
00820 Moves the cursor down one row from its current position.
00821 A <code>ResultSet</code> cursor is initially positioned
00822 before the first row; the first call to the method
00823 <code>next</code> makes the first row the current row; the
00824 second call makes the second row the current row, and so on.
00825
00826 <P>If an input stream is open for the current row, a call
00827 to the method <code>next</code> will
00828 implicitly close it. A <code>ResultSet</code> object's
00829 warning chain is cleared when a new row is read.
00830
00831 @return <code>true</code> if the new current row is valid;
00832 <code>false</code> if there are no more rows
00833 }
00834 function TZMySQLResultSet.Next: Boolean;
00835 begin
00836 { Checks for maximum row. }
00837 Result := False;
00838 if (MaxRows > 0) and (RowNo >= MaxRows) then
00839 Exit;
00840
00841 FRowHandle := FPlainDriver.FetchRow(FQueryHandle);
00842 if FRowHandle <> nil then
00843 begin
00844 RowNo := RowNo + 1;
00845 if LastRowNo < RowNo then
00846 LastRowNo := RowNo;
00847 Result := True;
00848 end
00849 else
00850 begin
00851 if RowNo <= LastRowNo then
00852 RowNo := LastRowNo + 1;
00853 Result := False;
00854 end;
00855 end;
00856
00857 { TZMySQLCachedResolver }
00858
00859 {**
00860 Creates a MySQL specific cached resolver object.
00861 @param PlainDriver a native MySQL plain driver.
00862 @param Handle a MySQL specific query handle.
00863 @param Statement a related SQL statement object.
00864 @param Metadata a resultset metadata reference.
00865 }
00866 constructor TZMySQLCachedResolver.Create(PlainDriver: IZMySQLPlainDriver;
00867 Handle: PZMySQLConnect; Statement: IZStatement; Metadata: IZResultSetMetadata);
00868 var
00869 I: Integer;
00870 begin
00871 inherited Create(Statement, Metadata);
00872 FPlainDriver := PlainDriver;
00873 FHandle := Handle;
00874
00875 { Defines an index of autoincrement field. }
00876 FAutoColumnIndex := 0;
00877 for I := 1 to Metadata.GetColumnCount do
00878 begin
00879 if Metadata.IsAutoIncrement(I) and
00880 (Metadata.GetColumnType(I) in [stByte, stShort, stInteger, stLong]) then
00881 begin
00882 FAutoColumnIndex := I;
00883 Break;
00884 end;
00885 end;
00886 end;
00887
00888 {**
00889 Posts updates to database.
00890 @param Sender a cached result set object.
00891 @param UpdateType a type of updates.
00892 @param OldRowAccessor an accessor object to old column values.
00893 @param NewRowAccessor an accessor object to new column values.
00894 }
00895 procedure TZMySQLCachedResolver.PostUpdates(Sender: IZCachedResultSet;
00896 UpdateType: TZRowUpdateType; OldRowAccessor, NewRowAccessor: TZRowAccessor);
00897 {BEGIN of PATCH [1185969]: Do tasks after posting updates. ie: Updating AutoInc fields in MySQL}
00898 {
00899 var
00900 Statement: IZStatement;
00901 ResultSet: IZResultSet;
00902 }
00903 {END of PATCH [1185969]: Do tasks after posting updates. ie: Updating AutoInc fields in MySQL }
00904 begin
00905 inherited PostUpdates(Sender, UpdateType, OldRowAccessor, NewRowAccessor);
00906 {BEGIN of PATCH [1185969]: Do tasks after posting updates. ie: Updating AutoInc fields in MySQL }
00907 if (UpdateType = utInserted) then
00908 begin
00909 UpdateAutoIncrementFields(Sender, UpdateType, OldRowAccessor, NewRowAccessor, Self);
00910 end;
00911 { commented, below code moved to 'CalculateDefaultsAfterUpdates' methods
00912 if (UpdateType = utInserted) and (FAutoColumnIndex > 0)
00913 and OldRowAccessor.IsNull(FAutoColumnIndex) then
00914 begin
00915 Statement := Connection.CreateStatement;
00916 ResultSet := Statement.ExecuteQuery('SELECT LAST_INSERT_ID()');
00917 try
00918 if ResultSet.Next then
00919 NewRowAccessor.SetLong(FAutoColumnIndex, ResultSet.GetLong(1));
00920 finally
00921 ResultSet.Close;
00922 Statement.Close;
00923 end;
00924 end;
00925 }
00926 {END of PATCH [1185969]: Do tasks after posting updates. ie: Updating AutoInc fields in MySQL }
00927 end;
00928
00929 {BEGIN of PATCH [1185969]: Do tasks after posting updates. ie: Updating AutoInc fields in MySQL }
00930 {**
00931 Do Tasks after Post updates to database.
00932 @param Sender a cached result set object.
00933 @param UpdateType a type of updates.
00934 @param OldRowAccessor an accessor object to old column values.
00935 @param NewRowAccessor an accessor object to new column values.
00936 }
00937 procedure TZMySQLCachedResolver.UpdateAutoIncrementFields(
00938 Sender: IZCachedResultSet; UpdateType: TZRowUpdateType; OldRowAccessor,
00939 NewRowAccessor: TZRowAccessor; Resolver: IZCachedResolver);
00940 var
00941 Plaindriver : IZMysqlPlainDriver;
00942 begin
00943 inherited UpdateAutoIncrementFields(Sender, UpdateType, OldRowAccessor, NewRowAccessor, Resolver);
00944 if not ((FAutoColumnIndex > 0) and
00945 (OldRowAccessor.IsNull(FAutoColumnIndex) or (OldRowAccessor.GetValue(FAutoColumnIndex).VInteger=0))) then
00946 exit;
00947 Plaindriver := (Connection as IZMysqlConnection).GetPlainDriver;
00948 NewRowAccessor.SetLong(FAutoColumnIndex, PlainDriver.GetLastInsertID(FHandle));
00949 end;
00950 {END of PATCH [1185969]: Do tasks after posting updates. ie: Updating AutoInc fields in MySQL }
00951
00952
00953 {**
00954 Forms a where clause for SELECT statements to calculate default values.
00955 @param Columns a collection of key columns.
00956 @param OldRowAccessor an accessor object to old column values.
00957 }
00958 function TZMySQLCachedResolver.FormCalculateStatement(
00959 Columns: TObjectList): string;
00960 var
00961 I: Integer;
00962 Current: TZResolverParameter;
00963 begin
00964 Result := '';
00965 if Columns.Count = 0 then Exit;
00966
00967 for I := 0 to Columns.Count - 1 do
00968 begin
00969 Current := TZResolverParameter(Columns[I]);
00970 if Result <> '' then
00971 Result := Result + ',';
00972 if Current.DefaultValue <> '' then
00973 Result := Result + Current.DefaultValue
00974 else Result := Result + 'NULL';
00975 end;
00976 Result := 'SELECT ' + Result;
00977 end;
00978
00979
00980 end.