mysqlconn.inc 27 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043
  1. {$mode objfpc}{$H+}
  2. interface
  3. uses
  4. Classes, SysUtils,bufdataset,sqldb,db,dynlibs,
  5. {$IfDef mysql50}
  6. mysql50dyn;
  7. {$ELSE}
  8. {$IfDef mysql41}
  9. mysql41dyn;
  10. {$ELSE}
  11. {$IFDEF mysql4} // temporary backwards compatibility for Lazarus
  12. mysql40dyn;
  13. {$ELSE}
  14. mysql40dyn;
  15. {$EndIf}
  16. {$EndIf}
  17. {$EndIf}
  18. Const
  19. {$IfDef mysql50}
  20. MySQLVersion = '5.0';
  21. {$ELSE}
  22. {$IfDef mysql41}
  23. MySQLVersion = '4.1';
  24. {$ELSE}
  25. {$IFDEF mysql4} // temporary backwards compatibility for Lazarus
  26. MySQLVersion = '4.0';
  27. {$ELSE}
  28. MySQLVersion = '4.0';
  29. {$EndIf}
  30. {$EndIf}
  31. {$EndIf}
  32. Type
  33. TTransactionName = Class(TSQLHandle)
  34. protected
  35. end;
  36. { TCursorName }
  37. TCursorName = Class(TSQLCursor)
  38. protected
  39. FRes: PMYSQL_RES; { Record pointer }
  40. FNeedData : Boolean;
  41. FStatement : String;
  42. Row : MYSQL_ROW;
  43. RowsAffected : QWord;
  44. LastInsertID : QWord;
  45. ParamBinding : TParamBinding;
  46. ParamReplaceString : String;
  47. MapDSRowToMSQLRow : array of integer;
  48. end;
  49. { TConnectionName }
  50. TConnectionName = class (TSQLConnection)
  51. private
  52. FHostInfo: String;
  53. FServerInfo: String;
  54. FMySQL : PMySQL;
  55. function GetClientInfo: string;
  56. function GetServerStatus: String;
  57. procedure ConnectMySQL(var HMySQL : PMySQL;H,U,P : pchar);
  58. procedure ExecuteDirectMySQL(const query : string);
  59. function EscapeString(const Str : string) : string;
  60. protected
  61. function StrToStatementType(s : string) : TStatementType; override;
  62. Procedure ConnectToServer; virtual;
  63. Procedure SelectDatabase; virtual;
  64. function MySQLDataType(AType: enum_field_types; ASize, ADecimals: Integer; var NewType: TFieldType; var NewSize: Integer): Boolean;
  65. function MySQLWriteData(AType: enum_field_types;ASize: Integer; AFieldType: TFieldType; Source, Dest: PChar; out CreateBlob : boolean): Boolean;
  66. // SQLConnection methods
  67. procedure DoInternalConnect; override;
  68. procedure DoInternalDisconnect; override;
  69. function GetHandle : pointer; override;
  70. function GetAsSQLText(Field : TField) : string; overload; override;
  71. function GetAsSQLText(Param : TParam) : string; overload; override;
  72. Function AllocateCursorHandle : TSQLCursor; override;
  73. Procedure DeAllocateCursorHandle(var cursor : TSQLCursor); override;
  74. Function AllocateTransactionHandle : TSQLHandle; override;
  75. procedure PrepareStatement(cursor: TSQLCursor;ATransaction : TSQLTransaction;buf : string; AParams : TParams); override;
  76. procedure UnPrepareStatement(cursor:TSQLCursor); override;
  77. procedure FreeFldBuffers(cursor : TSQLCursor); override;
  78. procedure Execute(cursor: TSQLCursor;atransaction:tSQLtransaction;AParams : TParams); override;
  79. procedure AddFieldDefs(cursor: TSQLCursor; FieldDefs : TfieldDefs); override;
  80. function Fetch(cursor : TSQLCursor) : boolean; override;
  81. function LoadField(cursor : TSQLCursor;FieldDef : TfieldDef;buffer : pointer; out CreateBlob : boolean) : boolean; override;
  82. procedure LoadBlobIntoBuffer(FieldDef: TFieldDef;ABlobBuf: PBufBlobField; cursor: TSQLCursor;ATransaction : TSQLTransaction); override;
  83. function GetTransactionHandle(trans : TSQLHandle): pointer; override;
  84. function Commit(trans : TSQLHandle) : boolean; override;
  85. function RollBack(trans : TSQLHandle) : boolean; override;
  86. function StartdbTransaction(trans : TSQLHandle; AParams : string) : boolean; override;
  87. procedure CommitRetaining(trans : TSQLHandle); override;
  88. procedure RollBackRetaining(trans : TSQLHandle); override;
  89. function GetSchemaInfoSQL(SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string) : string; override;
  90. procedure UpdateIndexDefs(IndexDefs : TIndexDefs;TableName : string); override;
  91. function RowsAffected(cursor: TSQLCursor): TRowsCount; override;
  92. Public
  93. constructor Create(AOwner : TComponent); override;
  94. procedure GetFieldNames(const TableName : string; List : TStrings); override;
  95. procedure GetTableNames(List : TStrings; SystemTables : Boolean = false); override;
  96. procedure CreateDB; override;
  97. procedure DropDB; override;
  98. Property ServerInfo : String Read FServerInfo;
  99. Property HostInfo : String Read FHostInfo;
  100. property ClientInfo: string read GetClientInfo;
  101. property ServerStatus : String read GetServerStatus;
  102. published
  103. property DatabaseName;
  104. property HostName;
  105. property KeepConnection;
  106. property LoginPrompt;
  107. property Params;
  108. property Port stored false;
  109. property OnLogin;
  110. end;
  111. { TMySQLConnectionDef }
  112. TMySQLConnectionDef = Class(TConnectionDef)
  113. Class Function TypeName : String; override;
  114. Class Function ConnectionClass : TSQLConnectionClass; override;
  115. Class Function Description : String; override;
  116. end;
  117. EMySQLError = Class(Exception);
  118. {$IfDef mysql50}
  119. TMySQL50Connection = Class(TConnectionName);
  120. TMySQL50ConnectionDef = Class(TMySQLConnectionDef);
  121. TMySQL50Transaction = Class(TTransactionName);
  122. TMySQL50Cursor = Class(TCursorName);
  123. {$ELSE}
  124. {$IfDef mysql41}
  125. TMySQL41Connection = Class(TConnectionName);
  126. TMySQL41ConnectionDef = Class(TMySQLConnectionDef);
  127. TMySQL41Transaction = Class(TTransactionName);
  128. TMySQL41Cursor = Class(TCursorName);
  129. {$ELSE}
  130. {$IFDEF mysql4} // temporary backwards compatibility for Lazarus
  131. TMySQLConnection = Class(TConnectionName);
  132. TMySQL40ConnectionDef = Class(TMySQLConnectionDef);
  133. TMySQLTransaction = Class(TTransactionName);
  134. TMySQLCursor = Class(TCursorName);
  135. {$ELSE}
  136. TMySQL40Connection = Class(TConnectionName);
  137. TMySQL40ConnectionDef = Class(TMySQLConnectionDef);
  138. TMySQL40Transaction = Class(TTransactionName);
  139. TMySQL40Cursor = Class(TCursorName);
  140. {$EndIf}
  141. {$EndIf}
  142. {$EndIf}
  143. implementation
  144. uses dbconst,ctypes,strutils;
  145. { TConnectionName }
  146. Resourcestring
  147. SErrServerConnectFailed = 'Server connect failed.';
  148. SErrDatabaseSelectFailed = 'failed to select database: %s';
  149. SErrDatabaseCreate = 'Failed to create database: %s';
  150. SErrDatabaseDrop = 'Failed to drop database: %s';
  151. SErrNoData = 'No data for record';
  152. SErrExecuting = 'Error executing query: %s';
  153. SErrFetchingdata = 'Error fetching row data: %s';
  154. SErrGettingResult = 'Error getting result set: %s';
  155. SErrNoQueryResult = 'No result from query.';
  156. SErrNotversion50 = 'TMySQL50Connection can not work with the installed MySQL client version (%s).';
  157. SErrNotversion41 = 'TMySQL41Connection can not work with the installed MySQL client version (%s).';
  158. SErrNotversion40 = 'TMySQL40Connection can not work with the installed MySQL client version (%s).';
  159. Procedure MySQlError(R : PMySQL;Msg: String;Comp : TComponent);
  160. Var
  161. MySQLMsg : String;
  162. begin
  163. If (R<>Nil) then
  164. begin
  165. MySQLMsg:=Strpas(mysql_error(R));
  166. DatabaseErrorFmt(Msg,[MySQLMsg],Comp);
  167. end
  168. else
  169. DatabaseError(Msg,Comp);
  170. end;
  171. function TConnectionName.StrToStatementType(s : string) : TStatementType;
  172. begin
  173. S:=Lowercase(s);
  174. if s = 'show' then exit(stSelect);
  175. result := inherited StrToStatementType(s);
  176. end;
  177. function TConnectionName.GetClientInfo: string;
  178. begin
  179. // To make it possible to call this if there's no connection yet
  180. InitialiseMysql;
  181. Try
  182. Result:=strpas(mysql_get_client_info());
  183. Finally
  184. ReleaseMysql;
  185. end;
  186. end;
  187. function TConnectionName.GetServerStatus: String;
  188. begin
  189. CheckConnected;
  190. Result := mysql_stat(FMYSQL);
  191. end;
  192. procedure TConnectionName.ConnectMySQL(var HMySQL : PMySQL;H,U,P : pchar);
  193. Var
  194. APort : Cardinal;
  195. begin
  196. HMySQL := mysql_init(HMySQL);
  197. APort:=Abs(StrToIntDef(Params.Values['Port'],0));
  198. HMySQL:=mysql_real_connect(HMySQL,PChar(H),PChar(U),Pchar(P),Nil,APort,Nil,0);
  199. If (HMySQL=Nil) then
  200. MySQlError(Nil,SErrServerConnectFailed,Self);
  201. // MySQL _Server_ version 4.1 and later
  202. // major_version*10000 + minor_version *100 + sub_version
  203. if (trim(CharSet) <> '') and (4*10000 + 1*100 <= mysql_get_server_version(HMySQL)) then
  204. begin
  205. // Only available for mysql 5.0.7 and later...
  206. // if (mysql_set_character_set(HMySQL, PChar(CharSet)) <> 0) then
  207. if mysql_query(FMySQL,PChar('SET CHARACTER SET ''' + EscapeString(CharSet) +''''))<>0 then
  208. MySQLError(HMySQL,Format(SErrExecuting,[StrPas(mysql_error(HMySQL))]),Self);
  209. end;
  210. end;
  211. function TConnectionName.GetAsSQLText(Field : TField) : string;
  212. var esc_str : pchar;
  213. begin
  214. if (not assigned(field)) or field.IsNull then Result := 'Null'
  215. else if field.DataType = ftString then
  216. Result := '''' + EscapeString(field.AsString) + ''''
  217. else Result := inherited GetAsSqlText(field);
  218. end;
  219. function TConnectionName.GetAsSQLText(Param: TParam) : string;
  220. var esc_str : pchar;
  221. begin
  222. if (not assigned(param)) or param.IsNull then Result := 'Null'
  223. else if param.DataType in [ftString,ftBlob,ftMemo] then
  224. Result := '''' + EscapeString(Param.AsString) + ''''
  225. else Result := inherited GetAsSqlText(Param);
  226. end;
  227. procedure TConnectionName.ConnectToServer;
  228. Var
  229. H,U,P : String;
  230. begin
  231. H:=HostName;
  232. U:=UserName;
  233. P:=Password;
  234. ConnectMySQL(FMySQL,pchar(H),pchar(U),pchar(P));
  235. FServerInfo := strpas(mysql_get_server_info(FMYSQL));
  236. FHostInfo := strpas(mysql_get_host_info(FMYSQL));
  237. end;
  238. procedure TConnectionName.SelectDatabase;
  239. begin
  240. if mysql_select_db(FMySQL,pchar(DatabaseName))<>0 then
  241. MySQLError(FMySQL,SErrDatabaseSelectFailed,Self);
  242. end;
  243. procedure TConnectionName.CreateDB;
  244. begin
  245. ExecuteDirectMySQL('CREATE DATABASE ' +DatabaseName);
  246. end;
  247. procedure TConnectionName.DropDB;
  248. begin
  249. ExecuteDirectMySQL('DROP DATABASE ' +DatabaseName);
  250. end;
  251. procedure TConnectionName.ExecuteDirectMySQL(const query : string);
  252. var H,U,P : String;
  253. AMySQL : PMySQL;
  254. begin
  255. CheckDisConnected;
  256. InitialiseMysql;
  257. try
  258. H:=HostName;
  259. U:=UserName;
  260. P:=Password;
  261. AMySQL := nil;
  262. ConnectMySQL(AMySQL,pchar(H),pchar(U),pchar(P));
  263. try
  264. if mysql_query(AMySQL,pchar(query))<>0 then
  265. MySQLError(AMySQL,Format(SErrExecuting,[StrPas(mysql_error(AMySQL))]),Self);
  266. finally
  267. mysql_close(AMySQL);
  268. end;
  269. finally
  270. ReleaseMysql;
  271. end;
  272. end;
  273. function TConnectionName.EscapeString(const Str: string): string;
  274. var Len : integer;
  275. begin
  276. SetLength(result,length(str)*2+1);
  277. Len := mysql_real_escape_string(FMySQL,pchar(Result),pchar(Str),length(Str));
  278. SetLength(result,Len);
  279. end;
  280. procedure TConnectionName.DoInternalConnect;
  281. var ClientVerStr: string;
  282. begin
  283. InitialiseMysql;
  284. ClientVerStr := copy(strpas(mysql_get_client_info()),1,3);
  285. {$IFDEF mysql50}
  286. if (ClientVerStr<>'5.0') and (ClientVerStr<>'5.1') then
  287. Raise EInOutError.CreateFmt(SErrNotversion50,[strpas(mysql_get_client_info())]);
  288. {$ELSE}
  289. {$IFDEF mysql41}
  290. if ClientVerStr<>'4.1' then
  291. Raise EInOutError.CreateFmt(SErrNotversion41,[strpas(mysql_get_client_info())]);
  292. {$ELSE}
  293. if ClientVerStr<>'4.0' then
  294. Raise EInOutError.CreateFmt(SErrNotversion40,[strpas(mysql_get_client_info())]);
  295. {$ENDIF}
  296. {$ENDIF}
  297. inherited DoInternalConnect;
  298. ConnectToServer;
  299. SelectDatabase;
  300. end;
  301. procedure TConnectionName.DoInternalDisconnect;
  302. begin
  303. inherited DoInternalDisconnect;
  304. mysql_close(FMySQL);
  305. FMySQL:=Nil;
  306. ReleaseMysql;
  307. end;
  308. function TConnectionName.GetHandle: pointer;
  309. begin
  310. Result:=FMySQL;
  311. end;
  312. function TConnectionName.AllocateCursorHandle: TSQLCursor;
  313. begin
  314. {$IfDef mysql50}
  315. Result:=TMySQL50Cursor.Create;
  316. {$ELSE}
  317. {$IfDef mysql41}
  318. Result:=TMySQL41Cursor.Create;
  319. {$ELSE}
  320. {$IFDEF mysql4} // temporary backwards compatibility for Lazarus
  321. Result:=TMySQLCursor.Create;
  322. {$ELSE}
  323. Result:=TMySQL40Cursor.Create;
  324. {$EndIf}
  325. {$EndIf}
  326. {$EndIf}
  327. end;
  328. Procedure TConnectionName.DeAllocateCursorHandle(var cursor : TSQLCursor);
  329. begin
  330. FreeAndNil(cursor);
  331. end;
  332. function TConnectionName.AllocateTransactionHandle: TSQLHandle;
  333. begin
  334. // Result:=TTransactionName.Create;
  335. Result := nil;
  336. end;
  337. procedure TConnectionName.PrepareStatement(cursor: TSQLCursor;
  338. ATransaction: TSQLTransaction; buf: string;AParams : TParams);
  339. begin
  340. // if assigned(AParams) and (AParams.count > 0) then
  341. // DatabaseError('Parameters (not) yet supported for the MySQL SqlDB connection.',self);
  342. With Cursor as TCursorName do
  343. begin
  344. FStatement:=Buf;
  345. if assigned(AParams) and (AParams.count > 0) then
  346. FStatement := AParams.ParseSQL(FStatement,false,sqEscapeSlash in ConnOptions, sqEscapeRepeat in ConnOptions,psSimulated,paramBinding,ParamReplaceString);
  347. if FStatementType=stSelect then
  348. FNeedData:=True;
  349. end
  350. end;
  351. procedure TConnectionName.UnPrepareStatement(cursor: TSQLCursor);
  352. begin
  353. // do nothing
  354. end;
  355. procedure TConnectionName.FreeFldBuffers(cursor: TSQLCursor);
  356. Var
  357. C : TCursorName;
  358. begin
  359. C:=Cursor as TCursorName;
  360. if c.FStatementType=stSelect then
  361. c.FNeedData:=False;
  362. If (C.FRes<>Nil) then
  363. begin
  364. Mysql_free_result(C.FRes);
  365. C.FRes:=Nil;
  366. end;
  367. SetLength(c.MapDSRowToMSQLRow,0);
  368. inherited;
  369. end;
  370. procedure TConnectionName.Execute(cursor: TSQLCursor;
  371. atransaction: tSQLtransaction;AParams : TParams);
  372. Var
  373. C : TCursorName;
  374. i : integer;
  375. ParamNames,ParamValues : array of string;
  376. begin
  377. C:=Cursor as TCursorName;
  378. If (C.FRes=Nil) then
  379. begin
  380. if Assigned(AParams) and (AParams.count > 0) then
  381. begin
  382. setlength(ParamNames,AParams.Count);
  383. setlength(ParamValues,AParams.Count);
  384. for i := 0 to AParams.count -1 do
  385. begin
  386. ParamNames[AParams.count-i-1] := C.ParamReplaceString+inttostr(AParams[i].Index+1);
  387. ParamValues[AParams.count-i-1] := GetAsSQLText(AParams[i]);
  388. end;
  389. // paramreplacestring kan een probleem geven bij postgres als hij niet meer gewoon $ is?
  390. C.FStatement := stringsreplace(C.FStatement,ParamNames,ParamValues,[rfReplaceAll]);
  391. end;
  392. if mysql_query(FMySQL,Pchar(C.FStatement))<>0 then
  393. MySQLError(FMYSQL,Format(SErrExecuting,[StrPas(mysql_error(FMySQL))]),Self)
  394. else
  395. begin
  396. C.RowsAffected := mysql_affected_rows(FMYSQL);
  397. C.LastInsertID := mysql_insert_id(FMYSQL);
  398. if C.FNeedData then
  399. C.FRes:=mysql_store_result(FMySQL);
  400. end;
  401. end;
  402. end;
  403. function TConnectionName.MySQLDataType(AType: enum_field_types; ASize, ADecimals: Integer;
  404. var NewType: TFieldType; var NewSize: Integer): Boolean;
  405. begin
  406. Result := True;
  407. case AType of
  408. FIELD_TYPE_LONGLONG:
  409. begin
  410. NewType := ftLargeint;
  411. NewSize := 0;
  412. end;
  413. FIELD_TYPE_TINY, FIELD_TYPE_SHORT:
  414. begin
  415. NewType := ftSmallint;
  416. NewSize := 0;
  417. end;
  418. FIELD_TYPE_LONG, FIELD_TYPE_INT24:
  419. begin
  420. NewType := ftInteger;
  421. NewSize := 0;
  422. end;
  423. {$ifdef mysql50}
  424. FIELD_TYPE_NEWDECIMAL,
  425. {$endif}
  426. FIELD_TYPE_DECIMAL: if ADecimals < 5 then
  427. begin
  428. NewType := ftBCD;
  429. NewSize := ADecimals;
  430. end
  431. else
  432. begin
  433. NewType := ftFloat;
  434. NewSize := 0;
  435. end;
  436. FIELD_TYPE_FLOAT, FIELD_TYPE_DOUBLE:
  437. begin
  438. NewType := ftFloat;
  439. NewSize := 0;
  440. end;
  441. FIELD_TYPE_TIMESTAMP, FIELD_TYPE_DATETIME:
  442. begin
  443. NewType := ftDateTime;
  444. NewSize := 0;
  445. end;
  446. FIELD_TYPE_DATE:
  447. begin
  448. NewType := ftDate;
  449. NewSize := 0;
  450. end;
  451. FIELD_TYPE_TIME:
  452. begin
  453. NewType := ftTime;
  454. NewSize := 0;
  455. end;
  456. FIELD_TYPE_VAR_STRING, FIELD_TYPE_STRING, FIELD_TYPE_ENUM, FIELD_TYPE_SET:
  457. begin
  458. // Since mysql server version 5.0.3 string-fields with a length of more
  459. // then 256 characters are suported
  460. if ASize>dsMaxStringSize then
  461. begin
  462. NewType := ftMemo;
  463. NewSize := 0;
  464. end
  465. else
  466. begin
  467. NewType := ftString;
  468. NewSize := ASize;
  469. end;
  470. end;
  471. FIELD_TYPE_BLOB:
  472. begin
  473. NewType := ftBlob;
  474. NewSize := 0;
  475. end
  476. else
  477. Result := False;
  478. end;
  479. end;
  480. procedure TConnectionName.AddFieldDefs(cursor: TSQLCursor;
  481. FieldDefs: TfieldDefs);
  482. var
  483. C : TCursorName;
  484. I, TF, FC: Integer;
  485. field: PMYSQL_FIELD;
  486. DFT: TFieldType;
  487. DFS: Integer;
  488. begin
  489. // Writeln('MySQL: Adding fielddefs');
  490. C:=(Cursor as TCursorName);
  491. If (C.FRes=Nil) then
  492. begin
  493. // Writeln('res is nil');
  494. MySQLError(FMySQL,SErrNoQueryResult,Self);
  495. end;
  496. // Writeln('MySQL: have result');
  497. FC:=mysql_num_fields(C.FRes);
  498. SetLength(c.MapDSRowToMSQLRow,FC);
  499. TF := 1;
  500. For I:= 0 to FC-1 do
  501. begin
  502. field := mysql_fetch_field_direct(C.FRES, I);
  503. // Writeln('MySQL: creating fielddef ',I+1);
  504. if MySQLDataType(field^.ftype, field^.length, field^.decimals, DFT, DFS) then
  505. begin
  506. TFieldDef.Create(FieldDefs, FieldDefs.MakeNameUnique(field^.name), DFT, DFS, False, TF);
  507. c.MapDSRowToMSQLRow[TF-1] := I;
  508. inc(TF);
  509. end
  510. end;
  511. // Writeln('MySQL: Finished adding fielddefs');
  512. end;
  513. function TConnectionName.Fetch(cursor: TSQLCursor): boolean;
  514. Var
  515. C : TCursorName;
  516. begin
  517. C:=Cursor as TCursorName;
  518. C.Row:=MySQL_Fetch_row(C.FRes);
  519. Result:=(C.Row<>Nil);
  520. end;
  521. function TConnectionName.LoadField(cursor : TSQLCursor;
  522. FieldDef : TfieldDef;buffer : pointer; out CreateBlob : boolean) : boolean;
  523. var
  524. field: PMYSQL_FIELD;
  525. row : MYSQL_ROW;
  526. C : TCursorName;
  527. begin
  528. // Writeln('LoadFieldsFromBuffer');
  529. C:=Cursor as TCursorName;
  530. if C.Row=nil then
  531. begin
  532. // Writeln('LoadFieldsFromBuffer: row=nil');
  533. MySQLError(FMySQL,SErrFetchingData,Self);
  534. end;
  535. Row:=C.Row;
  536. inc(Row,c.MapDSRowToMSQLRow[FieldDef.FieldNo-1]);
  537. field := mysql_fetch_field_direct(C.FRES, c.MapDSRowToMSQLRow[FieldDef.FieldNo-1]);
  538. Result := MySQLWriteData(field^.ftype, field^.length, FieldDef.DataType, Row^, Buffer, CreateBlob);
  539. end;
  540. procedure TConnectionName.LoadBlobIntoBuffer(FieldDef: TFieldDef;
  541. ABlobBuf: PBufBlobField; cursor: TSQLCursor; ATransaction: TSQLTransaction);
  542. var
  543. row : MYSQL_ROW;
  544. C : TCursorName;
  545. li : longint;
  546. Lengths : pculong;
  547. begin
  548. C:=Cursor as TCursorName;
  549. if C.Row=nil then
  550. MySQLError(FMySQL,SErrFetchingData,Self);
  551. Row:=C.Row;
  552. inc(Row,c.MapDSRowToMSQLRow[FieldDef.FieldNo-1]);
  553. Lengths := mysql_fetch_lengths(c.FRes);
  554. li := Lengths[c.MapDSRowToMSQLRow[FieldDef.FieldNo-1]];
  555. ReAllocMem(ABlobBuf^.BlobBuffer^.Buffer,li);
  556. Move(pchar(row^)^, ABlobBuf^.BlobBuffer^.Buffer^, li);
  557. ABlobBuf^.BlobBuffer^.Size := li;
  558. end;
  559. function InternalStrToFloat(S: string): Extended;
  560. var
  561. I: Integer;
  562. Tmp: string;
  563. begin
  564. Tmp := '';
  565. for I := 1 to Length(S) do
  566. begin
  567. if not (S[I] in ['0'..'9', '+', '-', 'E', 'e']) then
  568. Tmp := Tmp + DecimalSeparator
  569. else
  570. Tmp := Tmp + S[I];
  571. end;
  572. Result := StrToFloat(Tmp);
  573. end;
  574. function InternalStrToCurrency(S: string): Extended;
  575. var
  576. I: Integer;
  577. Tmp: string;
  578. begin
  579. Tmp := '';
  580. for I := 1 to Length(S) do
  581. begin
  582. if not (S[I] in ['0'..'9', '+', '-', 'E', 'e']) then
  583. Tmp := Tmp + DecimalSeparator
  584. else
  585. Tmp := Tmp + S[I];
  586. end;
  587. Result := StrToCurr(Tmp);
  588. end;
  589. function InternalStrToDate(S: string): TDateTime;
  590. var
  591. EY, EM, ED: Word;
  592. begin
  593. EY := StrToInt(Copy(S,1,4));
  594. EM := StrToInt(Copy(S,6,2));
  595. ED := StrToInt(Copy(S,9,2));
  596. if (EY = 0) or (EM = 0) or (ED = 0) then
  597. Result:=0
  598. else
  599. Result:=EncodeDate(EY, EM, ED);
  600. end;
  601. function InternalStrToDateTime(S: string): TDateTime;
  602. var
  603. EY, EM, ED: Word;
  604. EH, EN, ES: Word;
  605. begin
  606. EY := StrToInt(Copy(S, 1, 4));
  607. EM := StrToInt(Copy(S, 6, 2));
  608. ED := StrToInt(Copy(S, 9, 2));
  609. EH := StrToInt(Copy(S, 12, 2));
  610. EN := StrToInt(Copy(S, 15, 2));
  611. ES := StrToInt(Copy(S, 18, 2));
  612. if (EY = 0) or (EM = 0) or (ED = 0) then
  613. Result := 0
  614. else
  615. Result := EncodeDate(EY, EM, ED);
  616. Result := Result + EncodeTime(EH, EN, ES, 0);
  617. end;
  618. function InternalStrToTime(S: string): TDateTime;
  619. var
  620. EH, EM, ES: Word;
  621. begin
  622. EH := StrToInt(Copy(S, 1, 2));
  623. EM := StrToInt(Copy(S, 4, 2));
  624. ES := StrToInt(Copy(S, 7, 2));
  625. Result := EncodeTime(EH, EM, ES, 0);
  626. end;
  627. function InternalStrToTimeStamp(S: string): TDateTime;
  628. var
  629. EY, EM, ED: Word;
  630. EH, EN, ES: Word;
  631. begin
  632. {$IFNDEF mysql40}
  633. EY := StrToInt(Copy(S, 1, 4));
  634. EM := StrToInt(Copy(S, 6, 2));
  635. ED := StrToInt(Copy(S, 9, 2));
  636. EH := StrToInt(Copy(S, 12, 2));
  637. EN := StrToInt(Copy(S, 15, 2));
  638. ES := StrToInt(Copy(S, 18, 2));
  639. {$ELSE}
  640. EY := StrToInt(Copy(S, 1, 4));
  641. EM := StrToInt(Copy(S, 5, 2));
  642. ED := StrToInt(Copy(S, 7, 2));
  643. EH := StrToInt(Copy(S, 9, 2));
  644. EN := StrToInt(Copy(S, 11, 2));
  645. ES := StrToInt(Copy(S, 13, 2));
  646. {$ENDIF}
  647. if (EY = 0) or (EM = 0) or (ED = 0) then
  648. Result := 0
  649. else
  650. Result := EncodeDate(EY, EM, ED);
  651. Result := Result + EncodeTime(EH, EN, ES, 0);;
  652. end;
  653. function TConnectionName.MySQLWriteData(AType: enum_field_types;ASize: Integer; AFieldType: TFieldType; Source, Dest: PChar; out CreateBlob : boolean): Boolean;
  654. var
  655. VI: Integer;
  656. VL: LargeInt;
  657. VS: Smallint;
  658. VF: Double;
  659. VC: Currency;
  660. VD: TDateTime;
  661. Src : String;
  662. begin
  663. Result := False;
  664. CreateBlob := False;
  665. if Source = Nil then
  666. exit;
  667. Src:=StrPas(Source);
  668. case AType of
  669. FIELD_TYPE_TINY, FIELD_TYPE_SHORT:
  670. begin
  671. if (Src<>'') then
  672. VS := StrToInt(Src)
  673. else
  674. VS := 0;
  675. Move(VS, Dest^, SizeOf(smallint));
  676. end;
  677. FIELD_TYPE_LONG, FIELD_TYPE_INT24:
  678. begin
  679. if (Src<>'') then
  680. VI := StrToInt(Src)
  681. else
  682. VI := 0;
  683. Move(VI, Dest^, SizeOf(Integer));
  684. end;
  685. FIELD_TYPE_LONGLONG:
  686. begin
  687. if (Src<>'') then
  688. VL := StrToInt64(Src)
  689. else
  690. VL := 0;
  691. Move(VL, Dest^, SizeOf(LargeInt));
  692. end;
  693. {$ifdef mysql50}
  694. FIELD_TYPE_NEWDECIMAL,
  695. {$endif}
  696. FIELD_TYPE_DECIMAL, FIELD_TYPE_FLOAT, FIELD_TYPE_DOUBLE:
  697. if AFieldType = ftBCD then
  698. begin
  699. VC := InternalStrToCurrency(Src);
  700. Move(VC, Dest^, SizeOf(Currency));
  701. end
  702. else
  703. begin
  704. if Src <> '' then
  705. VF := InternalStrToFloat(Src)
  706. else
  707. VF := 0;
  708. Move(VF, Dest^, SizeOf(Double));
  709. end;
  710. FIELD_TYPE_TIMESTAMP:
  711. begin
  712. if Src <> '' then
  713. VD := InternalStrToTimeStamp(Src)
  714. else
  715. VD := 0;
  716. Move(VD, Dest^, SizeOf(TDateTime));
  717. end;
  718. FIELD_TYPE_DATETIME:
  719. begin
  720. if Src <> '' then
  721. VD := InternalStrToDateTime(Src)
  722. else
  723. VD := 0;
  724. Move(VD, Dest^, SizeOf(TDateTime));
  725. end;
  726. FIELD_TYPE_DATE:
  727. begin
  728. if Src <> '' then
  729. VD := InternalStrToDate(Src)
  730. else
  731. VD := 0;
  732. Move(VD, Dest^, SizeOf(TDateTime));
  733. end;
  734. FIELD_TYPE_TIME:
  735. begin
  736. if Src <> '' then
  737. VD := InternalStrToTime(Src)
  738. else
  739. VD := 0;
  740. Move(VD, Dest^, SizeOf(TDateTime));
  741. end;
  742. FIELD_TYPE_VAR_STRING, FIELD_TYPE_STRING, FIELD_TYPE_ENUM, FIELD_TYPE_SET:
  743. begin
  744. { Write('Moving string of size ',asize,' : ');
  745. P:=Source;
  746. If (P<>nil) then
  747. While P[0]<>#0 do
  748. begin
  749. Write(p[0]);
  750. inc(p);
  751. end;
  752. Writeln;
  753. }
  754. // String-fields which can contain more then dsMaxStringSize characters
  755. // are mapped to ftBlob fields, while their mysql-datatype is FIELD_TYPE_BLOB
  756. if AFieldType in [ftBlob,ftMemo] then
  757. CreateBlob := True
  758. else if Src<> '' then
  759. Move(Source^, Dest^, ASize)
  760. else
  761. Dest^ := #0;
  762. end;
  763. FIELD_TYPE_BLOB:
  764. CreateBlob := True;
  765. end;
  766. Result := True;
  767. end;
  768. procedure TConnectionName.UpdateIndexDefs(IndexDefs : TIndexDefs;TableName : string);
  769. var qry : TSQLQuery;
  770. begin
  771. if not assigned(Transaction) then
  772. DatabaseError(SErrConnTransactionnSet);
  773. qry := tsqlquery.Create(nil);
  774. qry.transaction := Transaction;
  775. qry.database := Self;
  776. with qry do
  777. begin
  778. ParseSQL := False;
  779. sql.clear;
  780. sql.add('show index from ' + TableName);
  781. open;
  782. end;
  783. while not qry.eof do with IndexDefs.AddIndexDef do
  784. begin
  785. Name := trim(qry.fieldbyname('Key_name').asstring);
  786. Fields := trim(qry.fieldbyname('Column_name').asstring);
  787. If Name = 'PRIMARY' then options := options + [ixPrimary];
  788. If qry.fieldbyname('Non_unique').asinteger = 0 then options := options + [ixUnique];
  789. qry.next;
  790. while (name = trim(qry.fieldbyname('Key_name').asstring)) and (not qry.eof) do
  791. begin
  792. Fields := Fields + ';' + trim(qry.fieldbyname('Column_name').asstring);
  793. qry.next;
  794. end;
  795. end;
  796. qry.close;
  797. qry.free;
  798. end;
  799. function TConnectionName.RowsAffected(cursor: TSQLCursor): TRowsCount;
  800. begin
  801. if assigned(cursor) then
  802. // Compile this without range-checking. RowsAffected can be -1, although
  803. // it's an unsigned integer. (small joke from the mysql-guys)
  804. // Without range-checking this goes ok. If Range is turned on, this results
  805. // in range-check errors.
  806. Result := (cursor as TCursorName).RowsAffected
  807. else
  808. Result := -1;
  809. end;
  810. constructor TConnectionName.Create(AOwner: TComponent);
  811. begin
  812. inherited Create(AOwner);
  813. FConnOptions := FConnOptions + [sqEscapeRepeat] + [sqEscapeSlash];
  814. FMySQL := Nil;
  815. end;
  816. procedure TConnectionName.GetFieldNames(const TableName: string; List: TStrings);
  817. begin
  818. GetDBInfo(stColumns,TableName,'field',List);
  819. end;
  820. procedure TConnectionName.GetTableNames(List: TStrings; SystemTables: Boolean);
  821. begin
  822. GetDBInfo(stTables,'','tables_in_'+DatabaseName,List)
  823. end;
  824. function TConnectionName.GetTransactionHandle(trans: TSQLHandle): pointer;
  825. begin
  826. Result:=Nil;
  827. end;
  828. function TConnectionName.Commit(trans: TSQLHandle): boolean;
  829. begin
  830. // Do nothing.
  831. end;
  832. function TConnectionName.RollBack(trans: TSQLHandle): boolean;
  833. begin
  834. // Do nothing
  835. end;
  836. function TConnectionName.StartdbTransaction(trans: TSQLHandle; AParams : string): boolean;
  837. begin
  838. // Do nothing
  839. end;
  840. procedure TConnectionName.CommitRetaining(trans: TSQLHandle);
  841. begin
  842. // Do nothing
  843. end;
  844. procedure TConnectionName.RollBackRetaining(trans: TSQLHandle);
  845. begin
  846. // Do nothing
  847. end;
  848. function TConnectionName.GetSchemaInfoSQL(SchemaType: TSchemaType;
  849. SchemaObjectName, SchemaPattern: string): string;
  850. begin
  851. case SchemaType of
  852. stTables : result := 'show tables';
  853. stColumns : result := 'show columns from ' + EscapeString(SchemaObjectName);
  854. else
  855. DatabaseError(SMetadataUnavailable)
  856. end; {case}
  857. end;
  858. { TMySQLConnectionDef }
  859. class function TMySQLConnectionDef.TypeName: String;
  860. begin
  861. Result:='MySQL '+MySQLVersion;
  862. end;
  863. class function TMySQLConnectionDef.ConnectionClass: TSQLConnectionClass;
  864. begin
  865. {$IfDef mysql50}
  866. Result:=TMySQL50Connection;
  867. {$ELSE}
  868. {$IfDef mysql41}
  869. Result:=TMySQL41Connection;
  870. {$ELSE}
  871. {$IFDEF mysql4} // temporary backwards compatibility for Lazarus
  872. Result:=TMySQLConnection;
  873. {$ELSE}
  874. Result:=TMySQL40Connection;
  875. {$EndIf}
  876. {$EndIf}
  877. {$EndIf}
  878. end;
  879. class function TMySQLConnectionDef.Description: String;
  880. begin
  881. Result:='Connect to a MySQL '+MySQLVersion+'database directly via the client library';
  882. end;
  883. {$IfDef mysql50}
  884. initialization
  885. RegisterConnection(TMySQL50ConnectionDef);
  886. finalization
  887. UnRegisterConnection(TMySQL50ConnectionDef);
  888. {$ELSE}
  889. {$IfDef mysql41}
  890. initialization
  891. RegisterConnection(TMySQL41ConnectionDef);
  892. finalization
  893. UnRegisterConnection(TMySQL41ConnectionDef);
  894. {$ELSE}
  895. {$IFDEF mysql4} // temporary backwards compatibility for Lazarus
  896. initialization
  897. RegisterConnection(TMySQL40ConnectionDef);
  898. finalization
  899. UnRegisterConnection(TMySQL40ConnectionDef);
  900. {$ELSE}
  901. initialization
  902. RegisterConnection(TMySQL40ConnectionDef);
  903. finalization
  904. UnRegisterConnection(TMySQL40ConnectionDef);
  905. {$EndIf}
  906. {$EndIf}
  907. {$EndIf}
  908. end.