mirror of
https://github.com/HeidiSQL/HeidiSQL.git
synced 2025-08-14 18:12:05 +08:00
773 lines
18 KiB
ObjectPascal
773 lines
18 KiB
ObjectPascal
unit mysqldataset;
|
|
|
|
{$M+} // Needed to add published properties
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, db, SysUtils, windows, mysql_api;
|
|
|
|
type
|
|
|
|
{TMySQLConnection}
|
|
|
|
TSQLLogCategory = (lcStats, lcSQL, lcError);
|
|
|
|
TSQLLogEvent = procedure (Category: TSQLLogCategory; Msg: String) of object;
|
|
|
|
TMySQLConnection = class(TComponent)
|
|
private
|
|
FHandle: PMYSQL;
|
|
FActive: Boolean;
|
|
FHostname: String;
|
|
FPort: Cardinal;
|
|
FUsername: String;
|
|
FPassword: String;
|
|
FTimeout: Cardinal;
|
|
FCompressed: Boolean;
|
|
FDatabase: String;
|
|
FOnSQLLog: TSQLLogEvent;
|
|
FServerVersionStr: String;
|
|
procedure SetActive( Value: Boolean );
|
|
procedure SetDatabase( Value: String );
|
|
function GetThreadId: Cardinal;
|
|
function GetCharacterSet: String;
|
|
function GetLastError: String;
|
|
function GetServerVersionInt: Integer;
|
|
procedure SQLLog(Category: TSQLLogCategory; Msg: String);
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
property ThreadId: Cardinal read GetThreadId;
|
|
property Handle: PMYSQL read FHandle;
|
|
property CharacterSet: String read GetCharacterSet;
|
|
function Query( SQL: String ): Longint;
|
|
property LastError: String read GetLastError;
|
|
property ServerVersionStr: String read FServerVersionStr;
|
|
property ServerVersionInt: Integer read GetServerVersionInt;
|
|
function EscapeString( Text: String ): String;
|
|
|
|
published
|
|
property Active: Boolean read FActive write SetActive;
|
|
property Hostname: String read FHostname write FHostname;
|
|
property Port: Cardinal read FPort write FPort;
|
|
property Username: String read FUsername write FUsername;
|
|
property Password: String read FPassword write FPassword;
|
|
property Timeout: Cardinal read FTimeout write FTimeout;
|
|
property Compressed: Boolean read FCompressed write FCompressed;
|
|
property Database: String read FDatabase write SetDatabase;
|
|
|
|
// Events
|
|
property OnSQLLog: TSQLLogEvent read FOnSQLLog write FOnSQLLog;
|
|
end;
|
|
|
|
|
|
{TMySQLQuery}
|
|
|
|
TMySQLQuery = class(TDataSet)
|
|
private
|
|
FSQL: TStrings;
|
|
FConnection: TMySQLConnection;
|
|
FRowsAffected: Int64;
|
|
FLastResult: PMYSQL_RES;
|
|
FRecNo: Int64;
|
|
procedure SetQuery(Value: TStrings);
|
|
protected
|
|
function GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override;
|
|
procedure SetFieldData(Field: TField; Buffer: Pointer); override;
|
|
function GetRecordSize: Word; override;
|
|
function GetCanModify: Boolean; override;
|
|
procedure InternalOpen; override;
|
|
procedure InternalClose; override;
|
|
procedure InternalInitFieldDefs; override;
|
|
procedure InternalHandleException; override;
|
|
procedure InternalInitRecord(Buffer: PChar); override;
|
|
function GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; override;
|
|
procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); override;
|
|
procedure GetBookmarkData(Buffer: PChar; Data: Pointer); override;
|
|
procedure InternalSetToRecord(Buffer: PChar); override;
|
|
function IsCursorOpen: Boolean; override;
|
|
procedure InternalFirst; override;
|
|
procedure InternalLast; override;
|
|
procedure InternalEdit; override;
|
|
procedure InternalInsert; override;
|
|
procedure InternalPost; override;
|
|
procedure InternalDelete; override;
|
|
function GetRecNo: Integer; override;
|
|
function GetRecordCount: Integer; override;
|
|
procedure SetRecNo(Value: Integer); override;
|
|
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
procedure ExecSQL;
|
|
property RowsAffected: Int64 read FRowsAffected;
|
|
function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
|
|
published
|
|
property SQL: TStrings read FSQL write SetQuery;
|
|
property Connection: TMySQLConnection read FConnection write FConnection;
|
|
end;
|
|
|
|
// Should be removed when this baby is running
|
|
procedure debug(txt: String);
|
|
|
|
|
|
implementation
|
|
|
|
|
|
procedure debug(txt: String);
|
|
begin
|
|
txt := 'mds '+txt;
|
|
OutputDebugString(PChar(txt));
|
|
end;
|
|
|
|
|
|
|
|
{TMySQLConnection}
|
|
|
|
constructor TMySQLConnection.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
FHostname := LOCAL_HOST;
|
|
FPort := MYSQL_PORT;
|
|
end;
|
|
|
|
|
|
{**
|
|
(Dis-)Connect to/from server
|
|
Parts copied from MySQL-Front or revision 1 of HeidiSQL's childwin.pas
|
|
}
|
|
procedure TMySQLConnection.SetActive( Value: Boolean );
|
|
var
|
|
connected : PMYSQL;
|
|
begin
|
|
FActive := Value;
|
|
|
|
if Value and (FHandle = nil) then
|
|
begin
|
|
// Get handle
|
|
FHandle := mysql_init(nil);
|
|
// timeout
|
|
mysql_options(FHandle, MYSQL_OPT_CONNECT_TIMEOUT, @FTimeout);
|
|
|
|
// read ini-file
|
|
// mysql_options(FHandle, MYSQL_READ_DEFAULT_FILE, pchar(ExtractFilePath(paramstr(0)) + 'my.ini'));
|
|
// read [Client]-section from ini-file
|
|
mysql_options(FHandle, MYSQL_READ_DEFAULT_GROUP, pchar('Client'));
|
|
|
|
// Connect
|
|
connected := mysql_real_connect(FHandle,
|
|
pChar(FHostname),
|
|
pChar(FUsername),
|
|
pChar(FPassword),
|
|
nil,
|
|
FPort,
|
|
nil,
|
|
Integer(FCompressed) * _CLIENT_COMPRESS
|
|
);
|
|
if connected = nil then
|
|
begin
|
|
SQLLog( lcError, GetLastError );
|
|
raise Exception.Create(GetLastError);
|
|
FActive := False;
|
|
FHandle := nil;
|
|
end
|
|
else begin
|
|
FServerVersionStr := mysql_get_server_info(FHandle);
|
|
SQLLog( lcStats, 'Connection established with host "'+FHostname+'" on port '+IntToStr(FPort)+' as user "'+FUsername+'"' );
|
|
SQLLog( lcStats, 'Connection-ID: '+IntToStr(GetThreadId) );
|
|
SQLLog( lcStats, 'Characterset: '+GetCharacterSet );
|
|
SQLLog( lcStats, 'Server version: '+FServerVersionStr+' ('+IntToStr(GetServerVersionInt)+')' );
|
|
SetDatabase( FDatabase );
|
|
end;
|
|
end
|
|
|
|
else if (not Value) and (FHandle <> nil) then
|
|
begin
|
|
mysql_close(FHandle);
|
|
FHandle := nil;
|
|
SQLLog( lcStats, 'Connection closed' );
|
|
end;
|
|
|
|
end;
|
|
|
|
function TMySQLConnection.Query(SQL: string): Longint;
|
|
begin
|
|
if Not FActive then
|
|
SetActive( True );
|
|
SQLLog( lcSQL, Trim(Copy(SQL, 1, 1024)) );
|
|
Result := mysql_real_query(FHandle, pChar(SQL), length(SQL));
|
|
if Result <> 0 then
|
|
begin
|
|
SQLLog( lcError, GetLastError );
|
|
raise Exception.Create(GetLastError);
|
|
end
|
|
else
|
|
begin
|
|
SQLLog( lcStats, IntToStr(mysql_affected_rows( FHandle ))+' rows affected' );
|
|
end;
|
|
end;
|
|
|
|
|
|
{**
|
|
Set "Database" property and select that db if connected
|
|
}
|
|
procedure TMySQLConnection.SetDatabase( Value: String );
|
|
var
|
|
res : Integer;
|
|
begin
|
|
if Value = '' then
|
|
Exit;
|
|
|
|
FDatabase := Value;
|
|
|
|
// Switch to DB if connected.
|
|
// If not connected, SetDatabase() should be called by SetActive()
|
|
if FActive then
|
|
begin
|
|
res := Query( 'USE '+Value );
|
|
if res = 0 then
|
|
SQLLog( lcStats, 'Database "'+Value+'" selected' )
|
|
else begin
|
|
raise Exception.Create(GetLastError);
|
|
FDatabase := '';
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
{**
|
|
Return current thread id
|
|
}
|
|
function TMySQLConnection.GetThreadId: Cardinal;
|
|
begin
|
|
Result := mysql_thread_id( FHandle );
|
|
end;
|
|
|
|
|
|
{**
|
|
Return currently used character set
|
|
}
|
|
function TMySQLConnection.GetCharacterSet: String;
|
|
begin
|
|
Result := mysql_character_set_name( FHandle );
|
|
end;
|
|
|
|
|
|
{**
|
|
Return the last error nicely formatted
|
|
}
|
|
function TMySQLConnection.GetLastError: String;
|
|
begin
|
|
Result := Format('SQL Error (%d): %s', [mysql_errno(FHandle), mysql_error(FHandle)] );
|
|
end;
|
|
|
|
|
|
{**
|
|
Get version string as normalized integer
|
|
"5.1.12-beta-community-123" => 50112
|
|
}
|
|
function TMySQLConnection.GetServerVersionInt: Integer;
|
|
var
|
|
i, dots: Byte;
|
|
v1, v2, v3: String;
|
|
begin
|
|
Result := -1;
|
|
|
|
dots := 0;
|
|
v1 := '';
|
|
v2 := '';
|
|
v3 := '';
|
|
for i := 1 to Length(FServerVersionStr) do
|
|
begin
|
|
if FServerVersionStr[i] = '.' then
|
|
begin
|
|
inc(dots);
|
|
// We expect exactly 2 dots.
|
|
if dots > 2 then
|
|
break;
|
|
end
|
|
else if FServerVersionStr[i] in ['0'..'9'] then
|
|
begin
|
|
if dots = 0 then
|
|
v1 := v1 + FServerVersionStr[i]
|
|
else if dots = 1 then
|
|
v2 := v2 + FServerVersionStr[i]
|
|
else if dots = 2 then
|
|
v3 := v3 + FServerVersionStr[i];
|
|
end
|
|
else // Don't include potential numbers of trailing string
|
|
break;
|
|
end;
|
|
|
|
// Concat tokens
|
|
if (Length(v1)>0) and (Length(v2)>0) and (Length(v3)>0) then
|
|
begin
|
|
Result := StrToIntDef(v1, 0) *10000 +
|
|
StrToIntDef(v2, 0) *100 +
|
|
StrToIntDef(v3, 0);
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
{**
|
|
Call log event if assigned to object
|
|
}
|
|
procedure TMySQLConnection.SQLLog(Category: TSQLLogCategory; Msg: String);
|
|
begin
|
|
if Assigned(FOnSQLLog) then
|
|
FOnSQLLog( Category, Msg);
|
|
end;
|
|
|
|
|
|
{**
|
|
Escapes a string for usage in SQL queries
|
|
}
|
|
function TMySQLConnection.EscapeString( Text: String ): String;
|
|
var
|
|
BufferLen: Integer;
|
|
Buffer: PChar;
|
|
begin
|
|
BufferLen := Length(Text) * 2 + 1;
|
|
GetMem(Buffer, BufferLen);
|
|
BufferLen := mysql_real_escape_string(FHandle, Buffer, PChar(Text), Length(Text));
|
|
SetString(Result, Buffer, BufferLen);
|
|
FreeMem(Buffer);
|
|
end;
|
|
|
|
|
|
|
|
|
|
{ TMySQLQuery }
|
|
|
|
constructor TMySQLQuery.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
FSQL := TStringList.Create;
|
|
FRowsAffected := -1;
|
|
FRecNo := -1;
|
|
end;
|
|
|
|
|
|
{**
|
|
Executes a query without handling the resultset
|
|
}
|
|
procedure TMySQLQuery.ExecSQL;
|
|
begin
|
|
FConnection.Query( FSQL.Text );
|
|
FRowsAffected := mysql_affected_rows( FConnection.Handle );
|
|
end;
|
|
|
|
|
|
{**
|
|
Set SQL TStringList
|
|
}
|
|
procedure TMySQLQuery.SetQuery(Value: TStrings);
|
|
begin
|
|
if FSQL.Text <> Value.Text then
|
|
begin
|
|
FSQL.BeginUpdate;
|
|
try
|
|
FSQL.Assign(Value);
|
|
finally
|
|
FSQL.EndUpdate;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
{**
|
|
The most important method for a TDataset:
|
|
Navigate to and fetch the current, next or prior row
|
|
}
|
|
function TMySQLQuery.GetRecord(Buffer: PChar; GetMode: TGetMode;
|
|
DoCheck: Boolean): TGetResult;
|
|
var
|
|
row: PMYSQL_ROW;
|
|
begin
|
|
Result := grOK;
|
|
|
|
case GetMode of
|
|
gmCurrent:
|
|
begin
|
|
row := mysql_fetch_row( FLastResult );
|
|
Result := grOK;
|
|
end;
|
|
|
|
gmNext:
|
|
if RecNo >= RecordCount then
|
|
Result := grEOF
|
|
else
|
|
begin
|
|
inc(FRecNo);
|
|
row := mysql_fetch_row( FLastResult );
|
|
Result := grOK;
|
|
end;
|
|
|
|
gmPrior:
|
|
if RecNo <= 0 then
|
|
Result := grBOF
|
|
else
|
|
begin
|
|
dec(FRecNo);
|
|
row := mysql_fetch_row( FLastResult );
|
|
Result := grOK;
|
|
end;
|
|
end;
|
|
|
|
if row <> nil then
|
|
Buffer := pointer(row); // Obviously incorrect
|
|
|
|
end;
|
|
|
|
function TMySQLQuery.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
|
|
begin
|
|
{
|
|
The Field parameter is the field for which the value needs to be retrieved.
|
|
The Field parameter is only passed for reference and should never be altered by this routine.
|
|
|
|
The Buffer parameter is where the field value needs to be copied to.
|
|
Looking at the buffer parameter results in a question that doesn't have an
|
|
obvious answer at first glance. That question is "What size is that buffer
|
|
and what needs to be copied into it?". The only way of determining this is
|
|
by looking at the various TField types in DB.pas and examining their GetValue
|
|
and GetDataSize methods.
|
|
|
|
Here is a partial table with some values used in the base dataset we will create later on:
|
|
Field Type Buffer Result
|
|
ftInteger,ftDate,ftTime Integer
|
|
ftBoolean Boolean
|
|
ftDateTime TDateTimeRec
|
|
ftFloat,ftCurrency Double
|
|
ftString PChar
|
|
|
|
As we can see, most types map pretty cleanly with the noteable exception of TDateTime
|
|
which requires some translation into a TDateTimeRec.
|
|
|
|
GetFieldData function returns True if a value was copied into the buffer by the
|
|
method and False if no value was copied.
|
|
|
|
That covers the GetFieldData method.
|
|
}
|
|
Result := True;
|
|
end;
|
|
|
|
|
|
{**
|
|
Tell dataset the contents of a field
|
|
}
|
|
procedure TMySQLQuery.SetFieldData(Field: TField; Buffer: Pointer);
|
|
begin
|
|
{ SetFieldData is the exact reverse operation
|
|
of GetFieldData. It is passed a buffer with some field value in the buffer that
|
|
must then be copied back into your record buffer.
|
|
}
|
|
end;
|
|
|
|
|
|
{**
|
|
??
|
|
}
|
|
function TMySQLQuery.GetRecordSize: Word;
|
|
begin
|
|
Result := 1;
|
|
end;
|
|
|
|
|
|
{**
|
|
Dataset is editable?
|
|
Should be True for simple SELECTs and False for not parsable SELECTs
|
|
}
|
|
function TMySQLQuery.GetCanModify: Boolean;
|
|
begin
|
|
Result := True;
|
|
end;
|
|
|
|
|
|
{**
|
|
Send query and fetch resultset
|
|
}
|
|
procedure TMySQLQuery.InternalOpen;
|
|
begin
|
|
FConnection.Query( FSQL.Text );
|
|
FLastResult := mysql_store_result( FConnection.Handle );
|
|
FieldDefs.Clear;
|
|
FieldDefs.Update; // Calls InternalInitFieldDefs
|
|
|
|
if DefaultFields then CreateFields;
|
|
BindFields(True);
|
|
end;
|
|
|
|
|
|
{**
|
|
Close resultset
|
|
}
|
|
procedure TMySQLQuery.InternalClose;
|
|
begin
|
|
mysql_free_result(FLastResult);
|
|
end;
|
|
|
|
|
|
{**
|
|
Fetch field types of recent resultset
|
|
}
|
|
procedure TMySQLQuery.InternalInitFieldDefs;
|
|
var
|
|
def: TFieldDef;
|
|
i, numfields: Cardinal;
|
|
field: PMYSQL_FIELD;
|
|
fType: TFieldType;
|
|
|
|
// Detect signed flag of a field
|
|
function Signed: Boolean;
|
|
begin
|
|
Result := (UNSIGNED_FLAG and field.flags) = 0;
|
|
end;
|
|
|
|
begin
|
|
numfields := mysql_num_fields(FLastResult);
|
|
|
|
for i := 0 to numfields-1 do
|
|
begin
|
|
field := mysql_fetch_field_direct(FLastResult, i);
|
|
|
|
// Create a new field
|
|
def := FieldDefs.AddFieldDef;
|
|
def.FieldNo := i;
|
|
def.Name := field.name;
|
|
|
|
// Map field type to delphi-types
|
|
// see TFieldType in DB.pas
|
|
case field._type of
|
|
FIELD_TYPE_TINY:
|
|
fType := ftSmallint;
|
|
|
|
FIELD_TYPE_YEAR, FIELD_TYPE_SHORT:
|
|
fType := ftInteger;
|
|
|
|
FIELD_TYPE_INT24, FIELD_TYPE_LONG:
|
|
begin
|
|
if Signed then
|
|
fType := ftInteger
|
|
else
|
|
fType := ftFloat;
|
|
end;
|
|
|
|
FIELD_TYPE_LONGLONG:
|
|
fType := ftString;
|
|
|
|
FIELD_TYPE_FLOAT:
|
|
fType := ftFloat;
|
|
|
|
FIELD_TYPE_DECIMAL, FIELD_TYPE_NEWDECIMAL:
|
|
begin
|
|
if (field.decimals = 0) and (field.length < 11) then
|
|
fType := ftInteger
|
|
else
|
|
fType := ftFloat;
|
|
end;
|
|
|
|
FIELD_TYPE_DOUBLE:
|
|
fType := ftFloat;
|
|
|
|
FIELD_TYPE_DATE:
|
|
fType := ftString;
|
|
|
|
FIELD_TYPE_TIME:
|
|
fType := ftString;
|
|
|
|
FIELD_TYPE_DATETIME, FIELD_TYPE_TIMESTAMP:
|
|
fType := ftString;
|
|
|
|
FIELD_TYPE_TINY_BLOB, FIELD_TYPE_MEDIUM_BLOB,
|
|
FIELD_TYPE_LONG_BLOB, FIELD_TYPE_BLOB:
|
|
if (field.flags and BINARY_FLAG) = 0 then
|
|
fType := ftMemo
|
|
else
|
|
fType := ftBlob;
|
|
|
|
FIELD_TYPE_BIT:
|
|
fType := ftBlob;
|
|
|
|
FIELD_TYPE_VARCHAR:
|
|
fType := ftString;
|
|
|
|
FIELD_TYPE_VAR_STRING:
|
|
fType := ftString;
|
|
|
|
FIELD_TYPE_STRING:
|
|
fType := ftString;
|
|
|
|
FIELD_TYPE_ENUM:
|
|
fType := ftString;
|
|
|
|
FIELD_TYPE_SET:
|
|
fType := ftString;
|
|
|
|
FIELD_TYPE_NULL:
|
|
// Example: SELECT NULL FROM DUAL
|
|
// Todo: Find out if it is possible to get real data in a
|
|
// TYPE_NULL field, perhaps adjust to binary or some such?
|
|
fType := ftString;
|
|
|
|
FIELD_TYPE_GEOMETRY:
|
|
// Todo: Would be nice to show as WKT.
|
|
fType := ftBlob;
|
|
|
|
else
|
|
raise Exception.Create('Unknown MySQL data type!'+IntToStr(field._type));
|
|
end;
|
|
|
|
def.DataType := fType;
|
|
if fType in [ftString, ftWidestring, ftBytes] then
|
|
def.Size := field.length
|
|
else
|
|
def.Size := 0;
|
|
|
|
def.Required := (field.flags and NOT_NULL_FLAG) = NOT_NULL_FLAG;
|
|
def.Precision := field.length;
|
|
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TMySQLQuery.InternalHandleException;
|
|
begin
|
|
// Application.HandleException(Self); ?
|
|
end;
|
|
|
|
|
|
{**
|
|
??
|
|
}
|
|
procedure TMySQLQuery.InternalInitRecord(Buffer: PChar);
|
|
begin
|
|
end;
|
|
|
|
|
|
{**
|
|
??
|
|
}
|
|
function TMySQLQuery.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
|
|
begin
|
|
end;
|
|
|
|
|
|
{**
|
|
??
|
|
}
|
|
procedure TMySQLQuery.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
|
|
begin
|
|
end;
|
|
|
|
|
|
{**
|
|
??
|
|
}
|
|
procedure TMySQLQuery.GetBookmarkData(Buffer: PChar; Data: Pointer);
|
|
begin
|
|
end;
|
|
|
|
|
|
{**
|
|
??
|
|
}
|
|
procedure TMySQLQuery.InternalSetToRecord(Buffer: PChar);
|
|
begin
|
|
end;
|
|
|
|
|
|
{**
|
|
??
|
|
}
|
|
function TMySQLQuery.IsCursorOpen: Boolean;
|
|
begin
|
|
// Result := Handle <> nil; ?
|
|
end;
|
|
|
|
|
|
{**
|
|
Called by DataSet.First
|
|
}
|
|
procedure TMySQLQuery.InternalFirst;
|
|
begin
|
|
FRecNo := 0;
|
|
mysql_data_seek( FLastResult, FRecNo );
|
|
end;
|
|
|
|
|
|
{**
|
|
Called by DataSet.Last
|
|
}
|
|
procedure TMySQLQuery.InternalLast;
|
|
begin
|
|
FRecNo := mysql_num_rows( FLastResult )-1;
|
|
mysql_data_seek( FLastResult, FRecNo );
|
|
end;
|
|
|
|
|
|
{**
|
|
??
|
|
}
|
|
procedure TMySQLQuery.InternalEdit;
|
|
begin
|
|
end;
|
|
|
|
|
|
{**
|
|
Fill default values?
|
|
}
|
|
procedure TMySQLQuery.InternalInsert;
|
|
begin
|
|
end;
|
|
|
|
|
|
{**
|
|
Generate UPDATE or INSERT statement?
|
|
}
|
|
procedure TMySQLQuery.InternalPost;
|
|
begin
|
|
inherited;
|
|
end;
|
|
|
|
|
|
{**
|
|
Generate DELETE statement?
|
|
}
|
|
procedure TMySQLQuery.InternalDelete;
|
|
begin
|
|
end;
|
|
|
|
|
|
{**
|
|
Called by DataSet.RecNo
|
|
}
|
|
function TMySQLQuery.GetRecNo: Integer;
|
|
begin
|
|
Result := FRecNo;
|
|
end;
|
|
|
|
|
|
{**
|
|
Called by DataSet.RecordCount
|
|
}
|
|
function TMySQLQuery.GetRecordCount: Integer;
|
|
begin
|
|
Result := mysql_num_rows( FLastResult );
|
|
end;
|
|
|
|
|
|
{**
|
|
Navigate to record
|
|
}
|
|
procedure TMySQLQuery.SetRecNo(Value: Integer);
|
|
begin
|
|
if Value > GetRecordCount then
|
|
Value := GetRecordCount-1;
|
|
FRecNo := Value;
|
|
mysql_data_seek( FLastResult, FRecNo );
|
|
end;
|
|
|
|
|
|
|
|
end.
|