mysql4conn.pas 14 KB

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