mysql4conn.pas 15 KB

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