8
SysUtils, Classes, db, mysql4,mysql4_com;
11
PMySQLDatasetBookmark = ^TMySQLDatasetBookmark;
12
TMySQLDatasetBookmark = record
13
BookmarkData: Integer;
14
BookmarkFlag: TBookmarkFlag;
19
TMySQLDatabase = class(TDatabase)
24
function GetHostName: String;
25
Function GetUserName : String;
26
procedure SetHostName(const AValue: String);
27
Procedure SetUserName (Value : String);
28
Procedure SetPassword (Value : String);
29
Function GetPassword : String;
30
Function GetClientInfo : String;
32
Procedure ConnectToServer;
33
Procedure SelectDatabase;
34
Procedure DoInternalConnect; override;
35
Procedure DoInternalDisConnect; override;
36
procedure StartTransaction; override;
37
procedure EndTransaction; override;
38
function GetServerStatus: string;
40
Procedure CreateDatabase;
41
Procedure DropDatabase;
42
Property ServerInfo : String Read FServerInfo;
43
Property HostInfo : String Read FHostInfo;
44
property ClientInfo: string read GetClientInfo;
45
property ServerStatus : String read GetServerStatus;
47
Property UserName : String Read GetUserName Write SetUserName;
48
Property HostName : String Read GetHostName Write SetHostName;
49
Property Password : String Read GetPassword Write SetPassword;
52
TMySQLDataset = class(TDBDataSet)
58
FMYSQLRES: PMYSQL_RES;
59
FCurrentRecord: Integer; { Record pointer }
61
FLastInsertID: Integer;
62
FLoadingFieldDefs: Boolean;
66
procedure DoGetResult;
68
procedure CalculateSizes;
69
procedure LoadBufferFromData(Buffer: PChar);
71
Function FMySQL : PMySQL;
72
procedure SetSQL(const Value: TStrings);
73
function InternalStrToFloat(S: string): Extended;
74
function InternalStrToDate(S: string): TDateTime;
75
function InternalStrToTime(S: string): TDateTime;
76
function InternalStrToDateTime(S: string): TDateTime;
77
function InternalStrToTimeStamp(S: string): TDateTime;
79
function MySQLFieldToFieldType(AType: enum_field_types; ASize: Integer;
80
var NewType: TFieldType; var NewSize: Integer): Boolean;
81
function MySQLDataSize(AType: enum_field_types; ASize: Integer): Integer;
82
function MySQLWriteFieldData(AType: enum_field_types; ASize: Integer; Source: PChar;
83
Dest: PChar): Integer;
86
function GetCanModify: Boolean; override;
87
{ Mandatory overrides }
88
// Record buffer methods:
89
function AllocRecordBuffer: PChar; override;
90
procedure FreeRecordBuffer(var Buffer: PChar); override;
91
procedure InternalInitRecord(Buffer: PChar); override;
92
function GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override;
93
function GetRecordSize: Word; override;
94
procedure SetFieldData(Field: TField; Buffer: Pointer); override;
96
procedure GetBookmarkData(Buffer: PChar; Data: Pointer); override;
97
function GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; override;
98
procedure InternalGotoBookmark(ABookmark: Pointer); override;
99
procedure InternalSetToRecord(Buffer: PChar); override;
100
procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); override;
101
procedure SetBookmarkData(Buffer: PChar; Data: Pointer); override;
102
// Navigational methods:
103
procedure InternalFirst; override;
104
procedure InternalLast; override;
106
procedure InternalAddRecord(Buffer: Pointer; DoAppend: Boolean); override;
107
procedure InternalDelete; override;
108
procedure InternalPost; override;
110
procedure InternalClose; override;
111
procedure InternalHandleException; override;
112
procedure InternalInitFieldDefs; override;
113
procedure InternalOpen; override;
114
function IsCursorOpen: Boolean; override;
115
{ Optional overrides }
116
function GetRecordCount: Integer; override;
117
function GetRecNo: Integer; override;
118
procedure SetRecNo(Value: Integer); override;
120
constructor Create(AOwner: TComponent); override;
121
destructor Destroy; override;
126
function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
128
property AffectedRows: QWord read FAffectedRows;
129
property LastInsertID: Integer read FLastInsertID;
133
property SQL: TStrings read FSQL write SetSQL;
136
property BeforeClose;
138
property BeforeInsert;
139
property AfterInsert;
144
property BeforeCancel;
145
property AfterCancel;
146
property BeforeDelete;
147
property AfterDelete;
148
property BeforeScroll;
149
property AfterScroll;
150
property OnDeleteError;
151
property OnEditError;
154
EMySQLError = Class(Exception);
159
SErrServerConnectFailed = 'Server connect failed.';
160
SErrDatabaseSelectFailed = 'failed to select database: %s';
161
SErrDatabaseCreate = 'Failed to create database: %s';
162
SErrDatabaseDrop = 'Failed to drop database: %s';
163
SErrNoData = 'No data for record';
164
SErrExecuting = 'Error executing query: %s';
165
SErrFetchingdata = 'Error fetching row data: %s';
166
SErrGettingResult = 'Error getting result set: %s';
168
Procedure MySQlError(R : PMySQL;Msg: String;Comp : TComponent);
176
MySQLMsg:=Strpas(mysql_error(R));
177
DatabaseErrorFmt(Msg,[MySQLMsg],Comp);
180
DatabaseError(Msg,Comp);
185
constructor TMySQLDataset.Create(AOwner: TComponent);
187
inherited Create(AOwner);
188
FSQL := TStringList.Create;
191
FCurrentRecord := -1;
192
FLoadingFieldDefs := False;
198
destructor TMySQLDataset.Destroy;
205
function TMySQLDataset.AllocRecordBuffer: PChar;
207
Result := AllocMem(FBufferSize);
210
procedure TMySQLDataset.FreeRecordBuffer(var Buffer: PChar);
212
If (@Buffer<>nil) then
216
procedure TMySQLDataset.GetBookmarkData(Buffer: PChar; Data: Pointer);
218
PInteger(Data)^ := PMySQLDatasetBookmark(Buffer + FRecordSize)^.BookmarkData;
221
function TMySQLDataset.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
223
Result:=PMySQLDatasetBookmark(Buffer + FRecordSize)^.BookmarkFlag;
226
function TMySQLDataset.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
235
CurBuf := ActiveBuffer;
236
FC := mysql_num_fields(FMYSQLRES);
237
for I := 0 to FC-1 do
239
fld := mysql_fetch_field_direct(FMYSQLRES, I);
240
if Field.FieldName = fld^.name then
242
Move(CurBuf^, PChar(Buffer)^, MySQLDataSize(fld^.ftype, fld^.length));
243
if Field.DataType in [ftString{, ftWideString}] then
245
Result := PChar(buffer)^ <> #0;
247
// Terminate string (necessary for enum fields)
248
PChar(buffer)[fld^.length] := #0;
255
Inc(CurBuf, MySQLDataSize(fld^.ftype, fld^.length));
259
function TMySQLDataset.GetRecNo: Integer;
262
if (FCurrentRecord=-1) and (RecordCount > 0) then
265
Result:=FCurrentRecord+1;
268
function TMySQLDataset.GetRecord(Buffer: PChar; GetMode: TGetMode;
269
DoCheck: Boolean): TGetResult;
271
if RecordCount < 1 then
278
if FCurrentRecord <= 0 then
281
FCurrentRecord := -1;
286
if (FCurrentRecord<0) or (FCurrentRecord>=RecordCount) then
289
if FCurrentRecord>=RecordCount-1 then
294
if (Result=grOK) then
296
LoadBufferFromData(Buffer);
297
with PMySQLDatasetBookmark(Buffer + FRecordSize)^ do
299
BookmarkData := FCurrentRecord;
300
BookmarkFlag := bfCurrent;
304
if (Result=grError) and (DoCheck) then
305
DatabaseError(SerrNoData,Self);
309
function TMySQLDataset.GetRecordCount: Integer;
311
Result:=mysql_num_rows(FMYSQLRES);
314
function TMySQLDataset.GetRecordSize: Word;
319
procedure TMySQLDataset.InternalAddRecord(Buffer: Pointer; DoAppend: Boolean);
324
procedure TMySQLDataset.InternalClose;
326
FCurrentRecord := -1;
328
if DefaultFields then
332
procedure TMySQLDataset.InternalDelete;
337
procedure TMySQLDataset.InternalFirst;
339
FCurrentRecord := -1;
342
procedure TMySQLDataset.InternalGotoBookmark(ABookmark: Pointer);
344
FCurrentRecord := PInteger(ABookmark)^;
347
procedure TMySQLDataset.InternalHandleException;
349
// Application.HandleException(self);
352
procedure TMySQLDataset.InternalInitFieldDefs;
362
if FLoadingFieldDefs then Exit;
363
FLoadingFieldDefs := True;
365
WasClosed := not IsCursorOpen;
373
FC := mysql_num_fields(FMYSQLRES);
374
for I := 0 to FC-1 do
376
field := mysql_fetch_field_direct(FMYSQLRES, I);
377
if MySQLFieldToFieldType(field^.ftype, field^.length, DFT, DFS) then
378
TFieldDef.Create(FieldDefs, field^.name, DFT, DFS, False, I+1);
385
FLoadingFieldDefs := False;
389
procedure TMySQLDataset.InternalInitRecord(Buffer: PChar);
391
FillChar(Buffer^, FBufferSize, 0);
394
procedure TMySQLDataset.InternalLast;
396
FCurrentRecord := RecordCount;
399
procedure TMySQLDataset.InternalOpen;
406
FCurrentRecord := -1;
407
InternalInitFieldDefs;
408
if DefaultFields then
416
BookMarkSize:=SizeOf(Longint);
419
procedure TMySQLDataset.InternalSetToRecord(Buffer: PChar);
421
FCurrentRecord := PMySQLDatasetBookmark(Buffer + FRecordSize)^.BookmarkData;
424
function TMySQLDataset.IsCursorOpen: Boolean;
426
Result:=(FMYSQLRES<>nil);
429
procedure TMySQLDataset.SetBookmarkData(Buffer: PChar; Data: Pointer);
431
PMySQLDatasetBookmark(Buffer + FRecordSize)^.BookmarkData := PInteger(Data)^;
434
procedure TMySQLDataset.SetBookmarkFlag(Buffer: PChar;
435
Value: TBookmarkFlag);
437
PMySQLDatasetBookmark(Buffer + FRecordSize)^.BookmarkFlag := Value;
440
procedure TMySQLDataset.SetFieldData(Field: TField; Buffer: Pointer);
445
procedure TMySQLDataset.SetRecNo(Value: Integer);
447
if (Value >= 0) and (Value <= RecordCount-1) then
449
FCurrentRecord := Value-1;
454
procedure TMySQLDataset.SetSQL(const Value: TStrings);
460
procedure TMySQLDataset.ExecSQL;
470
procedure TMySQLDataset.InternalPost;
475
function TMySQLDataset.MySQLFieldToFieldType(AType: enum_field_types; ASize: Integer;
476
var NewType: TFieldType; var NewSize: Integer): Boolean;
480
FIELD_TYPE_TINY, FIELD_TYPE_SHORT, FIELD_TYPE_LONG, FIELD_TYPE_LONGLONG,
483
NewType := ftInteger;
486
FIELD_TYPE_DECIMAL, FIELD_TYPE_FLOAT, FIELD_TYPE_DOUBLE:
491
FIELD_TYPE_TIMESTAMP, FIELD_TYPE_DATETIME:
493
NewType := ftDateTime;
506
FIELD_TYPE_VAR_STRING, FIELD_TYPE_STRING, FIELD_TYPE_ENUM, FIELD_TYPE_SET:
516
procedure TMySQLDataset.CalculateSizes;
522
FC := mysql_num_fields(FMYSQLRES);
523
for I := 0 to FC-1 do
525
field := mysql_fetch_field_direct(FMYSQLRES, I);
526
FRecordSize := FRecordSize + MySQLDataSize(field^.ftype, field^.length);
528
FBufferSize := FRecordSize + SizeOf(TMySQLDatasetBookmark);
531
procedure TMySQLDataset.LoadBufferFromData(Buffer: PChar);
539
mysql_data_seek(FMYSQLRES, FCurrentRecord);
540
row := mysql_fetch_row(FMYSQLRES);
542
MySQLError(FMySQL,SErrFetchingData,Self);
543
FC := mysql_num_fields(FMYSQLRES);
544
for I := 0 to FC-1 do
546
field := mysql_fetch_field_direct(FMYSQLRES, I);
547
CT := MySQLWriteFieldData(field^.ftype, field^.length, row^, Buffer);
554
function TMySQLDataset.MySQLDataSize(AType: enum_field_types;
555
ASize: Integer): Integer;
559
FIELD_TYPE_TINY, FIELD_TYPE_SHORT, FIELD_TYPE_LONG, FIELD_TYPE_LONGLONG,
562
Result := SizeOf(Integer);
564
FIELD_TYPE_DECIMAL, FIELD_TYPE_FLOAT, FIELD_TYPE_DOUBLE:
566
Result := SizeOf(Double);
568
FIELD_TYPE_TIMESTAMP, FIELD_TYPE_DATE, FIELD_TYPE_TIME, FIELD_TYPE_DATETIME:
570
Result := SizeOf(TDateTime);
572
FIELD_TYPE_VAR_STRING, FIELD_TYPE_STRING, FIELD_TYPE_ENUM, FIELD_TYPE_SET:
579
function TMySQLDataset.MySQLWriteFieldData(AType: enum_field_types;
580
ASize: Integer; Source, Dest: PChar): Integer;
590
FIELD_TYPE_TINY, FIELD_TYPE_SHORT, FIELD_TYPE_LONG, FIELD_TYPE_LONGLONG,
593
Result := SizeOf(Integer);
595
VI := StrToInt(Source)
598
Move(VI, Dest^, Result);
600
FIELD_TYPE_DECIMAL, FIELD_TYPE_FLOAT, FIELD_TYPE_DOUBLE:
602
Result := SizeOf(Double);
604
VF := InternalStrToFloat(Source)
607
Move(VF, Dest^, Result);
609
FIELD_TYPE_TIMESTAMP:
611
Result := SizeOf(TDateTime);
613
VD := InternalStrToTimeStamp(Source)
616
Move(VD, Dest^, Result);
620
Result := SizeOf(TDateTime);
622
VD := InternalStrToDateTime(Source)
625
Move(VD, Dest^, Result);
629
Result := SizeOf(TDateTime);
631
VD := InternalStrToDate(Source)
634
Move(VD, Dest^, Result);
638
Result := SizeOf(TDateTime);
640
VD := InternalStrToTime(Source)
643
Move(VD, Dest^, Result);
645
FIELD_TYPE_VAR_STRING, FIELD_TYPE_STRING, FIELD_TYPE_ENUM, FIELD_TYPE_SET:
649
Move(Source^, Dest^, Result)
656
function TMySQLDataset.InternalStrToFloat(S: string): Extended;
664
for I := 1 to Length(S) do
666
if not (S[I] in ['0'..'9', '+', '-', 'E', 'e']) then
667
Tmp := Tmp + DecimalSeparator
671
Result := StrToFloat(Tmp);
674
function TMySQLDataset.InternalStrToDate(S: string): TDateTime;
680
EY := StrToInt(Copy(S,1,4));
681
EM := StrToInt(Copy(S,6,2));
682
ED := StrToInt(Copy(S,9,2));
683
if (EY = 0) or (EM = 0) or (ED = 0) then
686
Result:=EncodeDate(EY, EM, ED);
689
function TMySQLDataset.InternalStrToDateTime(S: string): TDateTime;
696
EY := StrToInt(Copy(S, 1, 4));
697
EM := StrToInt(Copy(S, 6, 2));
698
ED := StrToInt(Copy(S, 9, 2));
699
EH := StrToInt(Copy(S, 11, 2));
700
EN := StrToInt(Copy(S, 14, 2));
701
ES := StrToInt(Copy(S, 17, 2));
702
if (EY = 0) or (EM = 0) or (ED = 0) then
705
Result := EncodeDate(EY, EM, ED);
706
Result := Result + EncodeTime(EH, EN, ES, 0);
709
function TMySQLDataset.InternalStrToTime(S: string): TDateTime;
715
EH := StrToInt(Copy(S, 1, 2));
716
EM := StrToInt(Copy(S, 4, 2));
717
ES := StrToInt(Copy(S, 7, 2));
718
Result := EncodeTime(EH, EM, ES, 0);
721
function TMySQLDataset.InternalStrToTimeStamp(S: string): TDateTime;
728
EY := StrToInt(Copy(S, 1, 4));
729
EM := StrToInt(Copy(S, 5, 2));
730
ED := StrToInt(Copy(S, 7, 2));
731
EH := StrToInt(Copy(S, 9, 2));
732
EN := StrToInt(Copy(S, 11, 2));
733
ES := StrToInt(Copy(S, 13, 2));
734
if (EY = 0) or (EM = 0) or (ED = 0) then
737
Result := EncodeDate(EY, EM, ED);
738
Result := Result + EncodeTime(EH, EN, ES, 0);;
741
procedure TMySQLDataset.DoClose;
744
if FMYSQLRES <> nil then
745
mysql_free_result(FMYSQLRES);
751
procedure TMySQLDataset.DoQuery;
756
Query := FSQL.GetText;
758
if mysql_query(FMySQL,Query) <> 0 then
759
MySQLError(FMYSQL,SErrExecuting,Self);
763
FAffectedRows := mysql_affected_rows(FMYSQL);
764
FLastInsertID := mysql_insert_id(FMYSQL);
767
function TMySQLDataset.GetCanModify: Boolean;
772
procedure TMySQLDataset.DoGetResult;
774
FMYSQLRES := mysql_store_result(FMYSQL);
775
if (FMYSQLRES=nil) then
776
MySQLError(FMYSQL,SErrGettingResult,Self);
777
FAffectedRows := mysql_affected_rows(FMYSQL);
780
function TMySQLDataset.FMySQL: PMySQL;
782
Result:=(Database as TMySQLDatabase).FMySQL;
787
function TMySQLDatabase.GetUserName: String;
789
result:=Params.values['UserName'];
792
function TMySQLDatabase.GetHostName: String;
794
Result:=Params.Values['HostName'];
797
procedure TMySQLDatabase.SetHostName(const AValue: String);
799
Params.Values['HostName']:=AValue;
802
procedure TMySQLDatabase.SetUserName(Value: String);
804
Params.Values['UserName']:=Value;
807
procedure TMySQLDatabase.SetPassword(Value: String);
809
Params.Values['Password']:=Value;
812
function TMySQLDatabase.GetPassword: String;
814
Result:=Params.Values['Password'];
817
function TMySQLDatabase.GetClientInfo: String;
819
Result:=strpas(mysql_get_client_info);
822
procedure TMySQLDatabase.ConnectToServer;
833
FMySQL:=mysql_real_connect(FMySQL,PChar(H),PChar(U),Pchar(P),Nil,0,Nil,0);
835
MySQlError(Nil,SErrServerConnectFailed,Self);
836
FServerInfo := strpas(mysql_get_server_info(FMYSQL));
837
FHostInfo := strpas(mysql_get_host_info(FMYSQL));
841
procedure TMySQLDatabase.SelectDatabase;
843
if mysql_select_db(FMySQL,pchar(DatabaseName))<>0 then
844
MySQLError(FMySQL,SErrDatabaseSelectFailed,Self);
847
procedure TMySQLDatabase.DoInternalConnect;
849
if (FMySQL<>nil) then
850
DoInternalDisconnect;
855
procedure TMySQLDatabase.DoInternalDisConnect;
863
procedure TMySQLDatabase.StartTransaction;
868
procedure TMySQLDatabase.EndTransaction;
873
procedure TMySQLDatabase.CreateDatabase;
876
Disconnect : Boolean;
879
Disconnect:=(FMySQL=Nil);
883
{if mysql_create_db(FMySQL,Pchar(DatabaseName))<>0 then
884
MySQLError(FMySQL,SErrDatabaseCreate,Self);}
887
DoInternalDisconnect;
891
procedure TMySQLDatabase.DropDatabase;
894
Disconnect : Boolean;
897
Disconnect:=(FMySQL=Nil);
904
if mysql_drop_db(FMySQL,Pchar(DatabaseName))<>0 then
905
MySQLError(FMySQL,SErrDatabaseDrop,Self);
909
DoInternalDisconnect;
913
function TMySQLDatabase.GetServerStatus: string;
916
Result := mysql_stat(FMYSQL);
923
$Log: mysqldb4.pp,v $
924
Revision 1.2 2005/02/14 17:13:12 peter