mysqlconn.inc 25 KB

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