mysql4conn.pas 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  1. unit mysql4conn;
  2. {$mode objfpc}{$H+}
  3. {$Define LinkDynamically}
  4. interface
  5. uses
  6. Classes, SysUtils,sqldb,db,
  7. {$IfDef LinkDynamically}
  8. mysql4dyn,mysql4_comdyn;
  9. {$Else}
  10. mysql4,mysql4_com;
  11. {$EndIf}
  12. Type
  13. TMySQLTransaction = Class(TSQLHandle)
  14. protected
  15. end;
  16. TMySQLCursor = Class(TSQLHandle)
  17. protected
  18. FQMySQL : PMySQL;
  19. FRes: PMYSQL_RES; { Record pointer }
  20. FNeedData : Boolean;
  21. FStatement : String;
  22. Row : TMYSQL_ROW;
  23. RowsAffected : Int64;
  24. LastInsertID : Int64;
  25. end;
  26. TMySQLConnection = class (TSQLConnection)
  27. private
  28. FDialect: integer;
  29. FHostInfo: String;
  30. FServerInfo: String;
  31. FMySQL : PMySQL;
  32. function GetClientInfo: string;
  33. function GetServerStatus: String;
  34. procedure ConnectMySQL(var HMySQL : PMySQL;H,U,P : pchar);
  35. protected
  36. function StrToStatementType(s : string) : TStatementType; override;
  37. Procedure ConnectToServer; virtual;
  38. Procedure SelectDatabase; virtual;
  39. function MySQLDataType(AType: enum_field_types; ASize: Integer; var NewType: TFieldType; var NewSize: Integer): Boolean;
  40. function MySQLWriteData(AType: enum_field_types; ASize: Integer; Source, Dest: PChar): Integer;
  41. // SQLConnection methods
  42. procedure DoInternalConnect; override;
  43. procedure DoInternalDisconnect; override;
  44. function GetHandle : pointer; override;
  45. Function AllocateCursorHandle : TSQLHandle; override;
  46. Function AllocateTransactionHandle : TSQLHandle; override;
  47. procedure FreeStatement(cursor : TSQLHandle); override;
  48. procedure PrepareStatement(cursor: TSQLHandle;ATransaction : TSQLTransaction;buf : string); override;
  49. procedure FreeFldBuffers(cursor : TSQLHandle); override;
  50. procedure Execute(cursor: TSQLHandle;atransaction:tSQLtransaction); override;
  51. procedure AddFieldDefs(cursor: TSQLHandle; FieldDefs : TfieldDefs); override;
  52. function Fetch(cursor : TSQLHandle) : boolean; override;
  53. function LoadField(cursor : TSQLHandle;FieldDef : TfieldDef;buffer : pointer) : boolean; override;
  54. function GetTransactionHandle(trans : TSQLHandle): pointer; override;
  55. function Commit(trans : TSQLHandle) : boolean; override;
  56. function RollBack(trans : TSQLHandle) : boolean; override;
  57. function StartdbTransaction(trans : TSQLHandle) : boolean; override;
  58. procedure CommitRetaining(trans : TSQLHandle); override;
  59. procedure RollBackRetaining(trans : TSQLHandle); override;
  60. procedure UpdateIndexDefs(var IndexDefs : TIndexDefs;TableName : string); override;
  61. Public
  62. Property ServerInfo : String Read FServerInfo;
  63. Property HostInfo : String Read FHostInfo;
  64. property ClientInfo: string read GetClientInfo;
  65. property ServerStatus : String read GetServerStatus;
  66. published
  67. property Dialect : integer read FDialect write FDialect;
  68. property DatabaseName;
  69. property HostName;
  70. property KeepConnection;
  71. property LoginPrompt;
  72. property Params;
  73. property OnLogin;
  74. end;
  75. EMySQLError = Class(Exception);
  76. implementation
  77. uses dbconst;
  78. { TMySQLConnection }
  79. Resourcestring
  80. SErrServerConnectFailed = 'Server connect failed.';
  81. SErrDatabaseSelectFailed = 'failed to select database: %s';
  82. SErrDatabaseCreate = 'Failed to create database: %s';
  83. SErrDatabaseDrop = 'Failed to drop database: %s';
  84. SErrNoData = 'No data for record';
  85. SErrExecuting = 'Error executing query: %s';
  86. SErrFetchingdata = 'Error fetching row data: %s';
  87. SErrGettingResult = 'Error getting result set: %s';
  88. SErrNoQueryResult = 'No result from query.';
  89. Procedure MySQlError(R : PMySQL;Msg: String;Comp : TComponent);
  90. Var
  91. MySQLMsg : String;
  92. begin
  93. If (R<>Nil) then
  94. begin
  95. MySQLMsg:=Strpas(mysql_error(R));
  96. DatabaseErrorFmt(Msg,[MySQLMsg],Comp);
  97. end
  98. else
  99. DatabaseError(Msg,Comp);
  100. end;
  101. function TMySQLConnection.StrToStatementType(s : string) : TStatementType;
  102. begin
  103. S:=Lowercase(s);
  104. if s = 'show' then exit(stSelect);
  105. result := inherited StrToStatementType(s);
  106. end;
  107. function TMySQLConnection.GetClientInfo: string;
  108. begin
  109. {$IfDef LinkDynamically}
  110. // To make it possible to call this if there's no connection yet
  111. InitialiseMysql4;
  112. {$EndIf}
  113. Result:=strpas(mysql_get_client_info());
  114. {$IfDef LinkDynamically}
  115. ReleaseMysql4;
  116. {$EndIf}
  117. end;
  118. function TMySQLConnection.GetServerStatus: String;
  119. begin
  120. CheckConnected;
  121. Result := mysql_stat(FMYSQL);
  122. end;
  123. procedure TMySQLConnection.ConnectMySQL(var HMySQL : PMySQL;H,U,P : pchar);
  124. begin
  125. if (HMySQL=Nil) then
  126. New(HMySQL);
  127. mysql_init(HMySQL);
  128. HMySQL:=mysql_real_connect(HMySQL,PChar(H),PChar(U),Pchar(P),Nil,0,Nil,0);
  129. If (HMySQL=Nil) then
  130. MySQlError(Nil,SErrServerConnectFailed,Self);
  131. end;
  132. procedure TMySQLConnection.ConnectToServer;
  133. Var
  134. H,U,P : String;
  135. begin
  136. H:=HostName;
  137. U:=UserName;
  138. P:=Password;
  139. ConnectMySQL(FMySQL,pchar(H),pchar(U),pchar(P));
  140. FServerInfo := strpas(mysql_get_server_info(FMYSQL));
  141. FHostInfo := strpas(mysql_get_host_info(FMYSQL));
  142. end;
  143. procedure TMySQLConnection.SelectDatabase;
  144. begin
  145. if mysql_select_db(FMySQL,pchar(DatabaseName))<>0 then
  146. MySQLError(FMySQL,SErrDatabaseSelectFailed,Self);
  147. end;
  148. procedure TMySQLConnection.DoInternalConnect;
  149. begin
  150. {$IfDef LinkDynamically}
  151. InitialiseMysql4;
  152. {$EndIf}
  153. inherited DoInternalConnect;
  154. ConnectToServer;
  155. SelectDatabase;
  156. end;
  157. procedure TMySQLConnection.DoInternalDisconnect;
  158. begin
  159. inherited DoInternalDisconnect;
  160. mysql_close(FMySQL);
  161. FMySQL:=Nil;
  162. {$IfDef LinkDynamically}
  163. ReleaseMysql4;
  164. {$EndIf}
  165. end;
  166. function TMySQLConnection.GetHandle: pointer;
  167. begin
  168. Result:=FMySQL;
  169. end;
  170. function TMySQLConnection.AllocateCursorHandle: TSQLHandle;
  171. begin
  172. Result:=TMySQLCursor.Create;
  173. end;
  174. function TMySQLConnection.AllocateTransactionHandle: TSQLHandle;
  175. begin
  176. Result:=TMySQLTransaction.Create;
  177. end;
  178. procedure TMySQLConnection.FreeStatement(cursor: TSQLHandle);
  179. Var
  180. C : TMySQLCursor;
  181. begin
  182. C:=Cursor as TMysqlCursor;
  183. if c.StatementType=stSelect then
  184. c.FNeedData:=False;
  185. If (C.FRes<>Nil) then
  186. begin
  187. C.FRes:=Nil;
  188. end;
  189. if (c.FQMySQL <> Nil) then
  190. begin
  191. mysql_close(c.FQMySQL);
  192. c.FQMySQL:=Nil;
  193. end;
  194. end;
  195. procedure TMySQLConnection.PrepareStatement(cursor: TSQLHandle;
  196. ATransaction: TSQLTransaction; buf: string);
  197. begin
  198. With Cursor as TMysqlCursor do
  199. begin
  200. FStatement:=Buf;
  201. if StatementType=stSelect then
  202. FNeedData:=True;
  203. ConnectMySQL(FQMySQL,FMySQL^.host,FMySQL^.user,FMySQL^.passwd);
  204. if mysql_select_db(FQMySQL,pchar(DatabaseName))<>0 then
  205. MySQLError(FQMySQL,SErrDatabaseSelectFailed,Self);
  206. end
  207. end;
  208. procedure TMySQLConnection.FreeFldBuffers(cursor: TSQLHandle);
  209. Var
  210. C : TMySQLCursor;
  211. begin
  212. C:=Cursor as TMysqlCursor;
  213. If (C.FRes<>Nil) then
  214. begin
  215. Mysql_free_result(C.FRes);
  216. C.FRes:=Nil;
  217. end;
  218. end;
  219. procedure TMySQLConnection.Execute(cursor: TSQLHandle;
  220. atransaction: tSQLtransaction);
  221. Var
  222. C : TMySQLCursor;
  223. begin
  224. C:=Cursor as TMysqlCursor;
  225. If (C.FRes=Nil) then
  226. begin
  227. if mysql_query(c.FQMySQL,Pchar(C.FStatement))<>0 then
  228. MySQLError(c.FQMYSQL,Format(SErrExecuting,[StrPas(mysql_error(c.FQMySQL))]),Self)
  229. else
  230. begin
  231. C.RowsAffected := mysql_affected_rows(c.FQMYSQL);
  232. C.LastInsertID := mysql_insert_id(c.FQMYSQL);
  233. if C.FNeedData then
  234. C.FRes:=mysql_use_result(c.FQMySQL);
  235. end;
  236. end;
  237. end;
  238. function TMySQLConnection.MySQLDataType(AType: enum_field_types; ASize: Integer;
  239. var NewType: TFieldType; var NewSize: Integer): Boolean;
  240. begin
  241. Result := True;
  242. case AType of
  243. FIELD_TYPE_TINY, FIELD_TYPE_SHORT, FIELD_TYPE_LONG, FIELD_TYPE_LONGLONG,
  244. FIELD_TYPE_INT24:
  245. begin
  246. NewType := ftInteger;
  247. NewSize := 0;
  248. end;
  249. FIELD_TYPE_DECIMAL, FIELD_TYPE_FLOAT, FIELD_TYPE_DOUBLE:
  250. begin
  251. NewType := ftFloat;
  252. NewSize := 0;
  253. end;
  254. FIELD_TYPE_TIMESTAMP, FIELD_TYPE_DATETIME:
  255. begin
  256. NewType := ftDateTime;
  257. NewSize := 0;
  258. end;
  259. FIELD_TYPE_DATE:
  260. begin
  261. NewType := ftDate;
  262. NewSize := 0;
  263. end;
  264. FIELD_TYPE_TIME:
  265. begin
  266. NewType := ftTime;
  267. NewSize := 0;
  268. end;
  269. FIELD_TYPE_VAR_STRING, FIELD_TYPE_STRING, FIELD_TYPE_ENUM, FIELD_TYPE_SET:
  270. begin
  271. NewType := ftString;
  272. NewSize := ASize;
  273. end;
  274. else
  275. Result := False;
  276. end;
  277. end;
  278. procedure TMySQLConnection.AddFieldDefs(cursor: TSQLHandle;
  279. FieldDefs: TfieldDefs);
  280. var
  281. C : TMySQLCursor;
  282. I, FC: Integer;
  283. field: PMYSQL_FIELD;
  284. DFT: TFieldType;
  285. DFS: Integer;
  286. begin
  287. // Writeln('MySQL: Adding fielddefs');
  288. C:=(Cursor as TMySQLCursor);
  289. If (C.FRes=Nil) then
  290. begin
  291. // Writeln('res is nil');
  292. MySQLError(c.FQMySQL,SErrNoQueryResult,Self);
  293. end;
  294. // Writeln('MySQL: have result');
  295. FC:=mysql_num_fields(C.FRes);
  296. For I:= 0 to FC-1 do
  297. begin
  298. field := mysql_fetch_field_direct(C.FRES, I);
  299. // Writeln('MySQL: creating fielddef ',I+1);
  300. if MySQLDataType(field^.ftype, field^.length, DFT, DFS) then
  301. TFieldDef.Create(FieldDefs, field^.name, DFT, DFS, False, I+1);
  302. end;
  303. // Writeln('MySQL: Finished adding fielddefs');
  304. end;
  305. function TMySQLConnection.Fetch(cursor: TSQLHandle): boolean;
  306. Var
  307. C : TMySQLCursor;
  308. begin
  309. C:=Cursor as TMySQLCursor;
  310. C.Row:=MySQL_Fetch_row(C.FRes);
  311. Result:=(C.Row<>Nil);
  312. end;
  313. function TMySQLConnection.LoadField(cursor : TSQLHandle;
  314. FieldDef : TfieldDef;buffer : pointer) : boolean;
  315. var
  316. I, FC, CT: Integer;
  317. field: PMYSQL_FIELD;
  318. row : TMYSQL_ROW;
  319. C : TMySQLCursor;
  320. begin
  321. // Writeln('LoadFieldsFromBuffer');
  322. C:=Cursor as TMySQLCursor;
  323. if C.Row=nil then
  324. begin
  325. // Writeln('LoadFieldsFromBuffer: row=nil');
  326. MySQLError(c.FQMySQL,SErrFetchingData,Self);
  327. end;
  328. Row:=C.Row;
  329. FC := mysql_num_fields(C.FRES);
  330. for I := 0 to FC-1 do
  331. begin
  332. field := mysql_fetch_field_direct(C.FRES, I);
  333. if field^.name=FieldDef.name then break;
  334. Inc(Row);
  335. end;
  336. CT := MySQLWriteData(field^.ftype, field^.length, Row^, Buffer);
  337. result := true;
  338. end;
  339. function InternalStrToFloat(S: string): Extended;
  340. var
  341. I: Integer;
  342. Tmp: string;
  343. begin
  344. Tmp := '';
  345. for I := 1 to Length(S) do
  346. begin
  347. if not (S[I] in ['0'..'9', '+', '-', 'E', 'e']) then
  348. Tmp := Tmp + DecimalSeparator
  349. else
  350. Tmp := Tmp + S[I];
  351. end;
  352. Result := StrToFloat(Tmp);
  353. end;
  354. function InternalStrToDate(S: string): TDateTime;
  355. var
  356. EY, EM, ED: Word;
  357. begin
  358. EY := StrToInt(Copy(S,1,4));
  359. EM := StrToInt(Copy(S,6,2));
  360. ED := StrToInt(Copy(S,9,2));
  361. if (EY = 0) or (EM = 0) or (ED = 0) then
  362. Result:=0
  363. else
  364. Result:=EncodeDate(EY, EM, ED);
  365. end;
  366. function InternalStrToDateTime(S: string): TDateTime;
  367. var
  368. EY, EM, ED: Word;
  369. EH, EN, ES: Word;
  370. begin
  371. EY := StrToInt(Copy(S, 1, 4));
  372. EM := StrToInt(Copy(S, 6, 2));
  373. ED := StrToInt(Copy(S, 9, 2));
  374. EH := StrToInt(Copy(S, 11, 2));
  375. EN := StrToInt(Copy(S, 14, 2));
  376. ES := StrToInt(Copy(S, 17, 2));
  377. if (EY = 0) or (EM = 0) or (ED = 0) then
  378. Result := 0
  379. else
  380. Result := EncodeDate(EY, EM, ED);
  381. Result := Result + EncodeTime(EH, EN, ES, 0);
  382. end;
  383. function InternalStrToTime(S: string): TDateTime;
  384. var
  385. EH, EM, ES: Word;
  386. begin
  387. EH := StrToInt(Copy(S, 1, 2));
  388. EM := StrToInt(Copy(S, 4, 2));
  389. ES := StrToInt(Copy(S, 7, 2));
  390. Result := EncodeTime(EH, EM, ES, 0);
  391. end;
  392. function InternalStrToTimeStamp(S: string): TDateTime;
  393. var
  394. EY, EM, ED: Word;
  395. EH, EN, ES: Word;
  396. begin
  397. EY := StrToInt(Copy(S, 1, 4));
  398. EM := StrToInt(Copy(S, 5, 2));
  399. ED := StrToInt(Copy(S, 7, 2));
  400. EH := StrToInt(Copy(S, 9, 2));
  401. EN := StrToInt(Copy(S, 11, 2));
  402. ES := StrToInt(Copy(S, 13, 2));
  403. if (EY = 0) or (EM = 0) or (ED = 0) then
  404. Result := 0
  405. else
  406. Result := EncodeDate(EY, EM, ED);
  407. Result := Result + EncodeTime(EH, EN, ES, 0);;
  408. end;
  409. function TMySQLConnection.MySQLWriteData(AType: enum_field_types;ASize: Integer; Source, Dest: PChar): Integer;
  410. var
  411. VI: Integer;
  412. VF: Double;
  413. VD: TDateTime;
  414. Src : String;
  415. begin
  416. Result := 0;
  417. If (Source<>Nil) Then
  418. Src:=StrPas(Source)
  419. else
  420. Src:='';
  421. case AType of
  422. FIELD_TYPE_TINY, FIELD_TYPE_SHORT, FIELD_TYPE_LONG, FIELD_TYPE_LONGLONG,
  423. FIELD_TYPE_INT24:
  424. begin
  425. Result:=SizeOf(Integer);
  426. if (Src<>'') then
  427. VI := StrToInt(Src)
  428. else
  429. VI := 0;
  430. Move(VI, Dest^, Result);
  431. end;
  432. FIELD_TYPE_DECIMAL, FIELD_TYPE_FLOAT, FIELD_TYPE_DOUBLE:
  433. begin
  434. Result := SizeOf(Double);
  435. if Src <> '' then
  436. VF := InternalStrToFloat(Src)
  437. else
  438. VF := 0;
  439. Move(VF, Dest^, Result);
  440. end;
  441. FIELD_TYPE_TIMESTAMP:
  442. begin
  443. Result := SizeOf(TDateTime);
  444. if Src <> '' then
  445. VD := InternalStrToTimeStamp(Src)
  446. else
  447. VD := 0;
  448. Move(VD, Dest^, Result);
  449. end;
  450. FIELD_TYPE_DATETIME:
  451. begin
  452. Result := SizeOf(TDateTime);
  453. if Src <> '' then
  454. VD := InternalStrToDateTime(Src)
  455. else
  456. VD := 0;
  457. Move(VD, Dest^, Result);
  458. end;
  459. FIELD_TYPE_DATE:
  460. begin
  461. Result := SizeOf(TDateTime);
  462. if Src <> '' then
  463. VD := InternalStrToDate(Src)
  464. else
  465. VD := 0;
  466. Move(VD, Dest^, Result);
  467. end;
  468. FIELD_TYPE_TIME:
  469. begin
  470. Result := SizeOf(TDateTime);
  471. if Src <> '' then
  472. VD := InternalStrToTime(Src)
  473. else
  474. VD := 0;
  475. Move(VD, Dest^, Result);
  476. end;
  477. FIELD_TYPE_VAR_STRING, FIELD_TYPE_STRING, FIELD_TYPE_ENUM, FIELD_TYPE_SET:
  478. begin
  479. Result := ASize;
  480. { Write('Moving string of size ',asize,' : ');
  481. P:=Source;
  482. If (P<>nil) then
  483. While P[0]<>#0 do
  484. begin
  485. Write(p[0]);
  486. inc(p);
  487. end;
  488. Writeln;
  489. } if Src<> '' then
  490. Move(Source^, Dest^, Result)
  491. else
  492. Dest^ := #0;
  493. end;
  494. end;
  495. end;
  496. procedure TMySQLConnection.UpdateIndexDefs(var IndexDefs : TIndexDefs;TableName : string);
  497. var qry : TSQLQuery;
  498. begin
  499. if not assigned(Transaction) then
  500. DatabaseError(SErrConnTransactionnSet);
  501. qry := tsqlquery.Create(nil);
  502. qry.transaction := Transaction;
  503. qry.database := Self;
  504. with qry do
  505. begin
  506. ReadOnly := True;
  507. sql.clear;
  508. sql.add('show index from ' + TableName);
  509. open;
  510. end;
  511. while not qry.eof do with IndexDefs.AddIndexDef do
  512. begin
  513. Name := trim(qry.fieldbyname('Key_name').asstring);
  514. Fields := trim(qry.fieldbyname('Column_name').asstring);
  515. If Name = 'PRIMARY' then options := options + [ixPrimary];
  516. If qry.fieldbyname('Non_unique').asinteger = 0 then options := options + [ixUnique];
  517. qry.next;
  518. { while (name = qry.fields[0].asstring) and (not qry.eof) do
  519. begin
  520. Fields := Fields + ';' + trim(qry.Fields[2].asstring);
  521. qry.next;
  522. end;}
  523. end;
  524. qry.close;
  525. qry.free;
  526. end;
  527. function TMySQLConnection.GetTransactionHandle(trans: TSQLHandle): pointer;
  528. begin
  529. Result:=Nil;
  530. end;
  531. function TMySQLConnection.Commit(trans: TSQLHandle): boolean;
  532. begin
  533. // Do nothing.
  534. end;
  535. function TMySQLConnection.RollBack(trans: TSQLHandle): boolean;
  536. begin
  537. // Do nothing
  538. end;
  539. function TMySQLConnection.StartdbTransaction(trans: TSQLHandle): boolean;
  540. begin
  541. // Do nothing
  542. end;
  543. procedure TMySQLConnection.CommitRetaining(trans: TSQLHandle);
  544. begin
  545. // Do nothing
  546. end;
  547. procedure TMySQLConnection.RollBackRetaining(trans: TSQLHandle);
  548. begin
  549. // Do nothing
  550. end;
  551. end.