mysqlconn.inc 16 KB

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