mysqlconn.inc 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972
  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, FieldDefs.MakeNameUnique(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. Result := MySQLWriteData(field^.ftype, field^.length, FieldDef.DataType, Row^, Buffer, CreateBlob);
  502. end;
  503. procedure TConnectionName.LoadBlobIntoBuffer(FieldDef: TFieldDef;
  504. ABlobBuf: PBufBlobField; cursor: TSQLCursor; ATransaction: TSQLTransaction);
  505. var
  506. row : MYSQL_ROW;
  507. C : TCursorName;
  508. li : longint;
  509. Lengths : pculong;
  510. begin
  511. C:=Cursor as TCursorName;
  512. if C.Row=nil then
  513. MySQLError(FMySQL,SErrFetchingData,Self);
  514. Row:=C.Row;
  515. inc(Row,c.MapDSRowToMSQLRow[FieldDef.FieldNo-1]);
  516. Lengths := mysql_fetch_lengths(c.FRes);
  517. li := Lengths[c.MapDSRowToMSQLRow[FieldDef.FieldNo-1]];
  518. ReAllocMem(ABlobBuf^.BlobBuffer^.Buffer,li);
  519. Move(pchar(row^)^, ABlobBuf^.BlobBuffer^.Buffer^, li);
  520. ABlobBuf^.BlobBuffer^.Size := li;
  521. end;
  522. function InternalStrToFloat(S: string): Extended;
  523. var
  524. I: Integer;
  525. Tmp: string;
  526. begin
  527. Tmp := '';
  528. for I := 1 to Length(S) do
  529. begin
  530. if not (S[I] in ['0'..'9', '+', '-', 'E', 'e']) then
  531. Tmp := Tmp + DecimalSeparator
  532. else
  533. Tmp := Tmp + S[I];
  534. end;
  535. Result := StrToFloat(Tmp);
  536. end;
  537. function InternalStrToCurrency(S: string): Extended;
  538. var
  539. I: Integer;
  540. Tmp: string;
  541. begin
  542. Tmp := '';
  543. for I := 1 to Length(S) do
  544. begin
  545. if not (S[I] in ['0'..'9', '+', '-', 'E', 'e']) then
  546. Tmp := Tmp + DecimalSeparator
  547. else
  548. Tmp := Tmp + S[I];
  549. end;
  550. Result := StrToCurr(Tmp);
  551. end;
  552. function InternalStrToDate(S: string): TDateTime;
  553. var
  554. EY, EM, ED: Word;
  555. begin
  556. EY := StrToInt(Copy(S,1,4));
  557. EM := StrToInt(Copy(S,6,2));
  558. ED := StrToInt(Copy(S,9,2));
  559. if (EY = 0) or (EM = 0) or (ED = 0) then
  560. Result:=0
  561. else
  562. Result:=EncodeDate(EY, EM, ED);
  563. end;
  564. function InternalStrToDateTime(S: string): TDateTime;
  565. var
  566. EY, EM, ED: Word;
  567. EH, EN, ES: Word;
  568. begin
  569. EY := StrToInt(Copy(S, 1, 4));
  570. EM := StrToInt(Copy(S, 6, 2));
  571. ED := StrToInt(Copy(S, 9, 2));
  572. EH := StrToInt(Copy(S, 12, 2));
  573. EN := StrToInt(Copy(S, 15, 2));
  574. ES := StrToInt(Copy(S, 18, 2));
  575. if (EY = 0) or (EM = 0) or (ED = 0) then
  576. Result := 0
  577. else
  578. Result := EncodeDate(EY, EM, ED);
  579. Result := Result + EncodeTime(EH, EN, ES, 0);
  580. end;
  581. function InternalStrToTime(S: string): TDateTime;
  582. var
  583. EH, EM, ES: Word;
  584. begin
  585. EH := StrToInt(Copy(S, 1, 2));
  586. EM := StrToInt(Copy(S, 4, 2));
  587. ES := StrToInt(Copy(S, 7, 2));
  588. Result := EncodeTime(EH, EM, ES, 0);
  589. end;
  590. function InternalStrToTimeStamp(S: string): TDateTime;
  591. var
  592. EY, EM, ED: Word;
  593. EH, EN, ES: Word;
  594. begin
  595. {$IFNDEF mysql40}
  596. EY := StrToInt(Copy(S, 1, 4));
  597. EM := StrToInt(Copy(S, 6, 2));
  598. ED := StrToInt(Copy(S, 9, 2));
  599. EH := StrToInt(Copy(S, 12, 2));
  600. EN := StrToInt(Copy(S, 15, 2));
  601. ES := StrToInt(Copy(S, 18, 2));
  602. {$ELSE}
  603. EY := StrToInt(Copy(S, 1, 4));
  604. EM := StrToInt(Copy(S, 5, 2));
  605. ED := StrToInt(Copy(S, 7, 2));
  606. EH := StrToInt(Copy(S, 9, 2));
  607. EN := StrToInt(Copy(S, 11, 2));
  608. ES := StrToInt(Copy(S, 13, 2));
  609. {$ENDIF}
  610. if (EY = 0) or (EM = 0) or (ED = 0) then
  611. Result := 0
  612. else
  613. Result := EncodeDate(EY, EM, ED);
  614. Result := Result + EncodeTime(EH, EN, ES, 0);;
  615. end;
  616. function TConnectionName.MySQLWriteData(AType: enum_field_types;ASize: Integer; AFieldType: TFieldType; Source, Dest: PChar; out CreateBlob : boolean): Boolean;
  617. var
  618. VI: Integer;
  619. VL: LargeInt;
  620. VS: Smallint;
  621. VF: Double;
  622. VC: Currency;
  623. VD: TDateTime;
  624. Src : String;
  625. begin
  626. Result := False;
  627. CreateBlob := False;
  628. if Source = Nil then
  629. exit;
  630. Src:=StrPas(Source);
  631. case AType of
  632. FIELD_TYPE_TINY, FIELD_TYPE_SHORT:
  633. begin
  634. if (Src<>'') then
  635. VS := StrToInt(Src)
  636. else
  637. VS := 0;
  638. Move(VS, Dest^, SizeOf(smallint));
  639. end;
  640. FIELD_TYPE_LONG, FIELD_TYPE_INT24:
  641. begin
  642. if (Src<>'') then
  643. VI := StrToInt(Src)
  644. else
  645. VI := 0;
  646. Move(VI, Dest^, SizeOf(Integer));
  647. end;
  648. FIELD_TYPE_LONGLONG:
  649. begin
  650. if (Src<>'') then
  651. VL := StrToInt64(Src)
  652. else
  653. VL := 0;
  654. Move(VL, Dest^, SizeOf(LargeInt));
  655. end;
  656. {$ifdef mysql50}
  657. FIELD_TYPE_NEWDECIMAL,
  658. {$endif}
  659. FIELD_TYPE_DECIMAL, FIELD_TYPE_FLOAT, FIELD_TYPE_DOUBLE:
  660. if AFieldType = ftBCD then
  661. begin
  662. VC := InternalStrToCurrency(Src);
  663. Move(VC, Dest^, SizeOf(Currency));
  664. end
  665. else
  666. begin
  667. if Src <> '' then
  668. VF := InternalStrToFloat(Src)
  669. else
  670. VF := 0;
  671. Move(VF, Dest^, SizeOf(Double));
  672. end;
  673. FIELD_TYPE_TIMESTAMP:
  674. begin
  675. if Src <> '' then
  676. VD := InternalStrToTimeStamp(Src)
  677. else
  678. VD := 0;
  679. Move(VD, Dest^, SizeOf(TDateTime));
  680. end;
  681. FIELD_TYPE_DATETIME:
  682. begin
  683. if Src <> '' then
  684. VD := InternalStrToDateTime(Src)
  685. else
  686. VD := 0;
  687. Move(VD, Dest^, SizeOf(TDateTime));
  688. end;
  689. FIELD_TYPE_DATE:
  690. begin
  691. if Src <> '' then
  692. VD := InternalStrToDate(Src)
  693. else
  694. VD := 0;
  695. Move(VD, Dest^, SizeOf(TDateTime));
  696. end;
  697. FIELD_TYPE_TIME:
  698. begin
  699. if Src <> '' then
  700. VD := InternalStrToTime(Src)
  701. else
  702. VD := 0;
  703. Move(VD, Dest^, SizeOf(TDateTime));
  704. end;
  705. FIELD_TYPE_VAR_STRING, FIELD_TYPE_STRING, FIELD_TYPE_ENUM, FIELD_TYPE_SET:
  706. begin
  707. { Write('Moving string of size ',asize,' : ');
  708. P:=Source;
  709. If (P<>nil) then
  710. While P[0]<>#0 do
  711. begin
  712. Write(p[0]);
  713. inc(p);
  714. end;
  715. Writeln;
  716. }
  717. // String-fields which can contain more then dsMaxStringSize characters
  718. // are mapped to ftBlob fields, while their mysql-datatype is FIELD_TYPE_BLOB
  719. if AFieldType in [ftBlob,ftMemo] then
  720. CreateBlob := True
  721. else if Src<> '' then
  722. Move(Source^, Dest^, ASize)
  723. else
  724. Dest^ := #0;
  725. end;
  726. FIELD_TYPE_BLOB:
  727. CreateBlob := True;
  728. end;
  729. Result := True;
  730. end;
  731. procedure TConnectionName.UpdateIndexDefs(IndexDefs : TIndexDefs;TableName : string);
  732. var qry : TSQLQuery;
  733. begin
  734. if not assigned(Transaction) then
  735. DatabaseError(SErrConnTransactionnSet);
  736. qry := tsqlquery.Create(nil);
  737. qry.transaction := Transaction;
  738. qry.database := Self;
  739. with qry do
  740. begin
  741. ParseSQL := False;
  742. sql.clear;
  743. sql.add('show index from ' + TableName);
  744. open;
  745. end;
  746. while not qry.eof do with IndexDefs.AddIndexDef do
  747. begin
  748. Name := trim(qry.fieldbyname('Key_name').asstring);
  749. Fields := trim(qry.fieldbyname('Column_name').asstring);
  750. If Name = 'PRIMARY' then options := options + [ixPrimary];
  751. If qry.fieldbyname('Non_unique').asinteger = 0 then options := options + [ixUnique];
  752. qry.next;
  753. while (name = trim(qry.fieldbyname('Key_name').asstring)) and (not qry.eof) do
  754. begin
  755. Fields := Fields + ';' + trim(qry.fieldbyname('Column_name').asstring);
  756. qry.next;
  757. end;
  758. end;
  759. qry.close;
  760. qry.free;
  761. end;
  762. function TConnectionName.RowsAffected(cursor: TSQLCursor): TRowsCount;
  763. begin
  764. if assigned(cursor) then
  765. // Compile this without range-checking. RowsAffected can be -1, although
  766. // it's an unsigned integer. (small joke from the mysql-guys)
  767. // Without range-checking this goes ok. If Range is turned on, this results
  768. // in range-check errors.
  769. Result := (cursor as TCursorName).RowsAffected
  770. else
  771. Result := -1;
  772. end;
  773. constructor TConnectionName.Create(AOwner: TComponent);
  774. begin
  775. inherited Create(AOwner);
  776. FConnOptions := FConnOptions + [sqEscapeRepeat] + [sqEscapeSlash];
  777. FMySQL := Nil;
  778. end;
  779. procedure TConnectionName.GetFieldNames(const TableName: string; List: TStrings);
  780. begin
  781. GetDBInfo(stColumns,TableName,'field',List);
  782. end;
  783. procedure TConnectionName.GetTableNames(List: TStrings; SystemTables: Boolean);
  784. begin
  785. GetDBInfo(stTables,'','tables_in_'+DatabaseName,List)
  786. end;
  787. function TConnectionName.GetTransactionHandle(trans: TSQLHandle): pointer;
  788. begin
  789. Result:=Nil;
  790. end;
  791. function TConnectionName.Commit(trans: TSQLHandle): boolean;
  792. begin
  793. // Do nothing.
  794. end;
  795. function TConnectionName.RollBack(trans: TSQLHandle): boolean;
  796. begin
  797. // Do nothing
  798. end;
  799. function TConnectionName.StartdbTransaction(trans: TSQLHandle; AParams : string): boolean;
  800. begin
  801. // Do nothing
  802. end;
  803. procedure TConnectionName.CommitRetaining(trans: TSQLHandle);
  804. begin
  805. // Do nothing
  806. end;
  807. procedure TConnectionName.RollBackRetaining(trans: TSQLHandle);
  808. begin
  809. // Do nothing
  810. end;
  811. function TConnectionName.GetSchemaInfoSQL(SchemaType: TSchemaType;
  812. SchemaObjectName, SchemaPattern: string): string;
  813. begin
  814. case SchemaType of
  815. stTables : result := 'show tables';
  816. stColumns : result := 'show columns from ' + EscapeString(SchemaObjectName);
  817. else
  818. DatabaseError(SMetadataUnavailable)
  819. end; {case}
  820. end;
  821. { TMySQLConnectionDef }
  822. class function TMySQLConnectionDef.TypeName: String;
  823. begin
  824. Result:='MySQL '+MySQLVersion;
  825. end;
  826. class function TMySQLConnectionDef.ConnectionClass: TSQLConnectionClass;
  827. begin
  828. Result:=TConnectionName;
  829. end;
  830. class function TMySQLConnectionDef.Description: String;
  831. begin
  832. Result:='Connect to a MySQL '+MySQLVersion+'database directly via the client library';
  833. end;
  834. initialization
  835. RegisterConnection(TMySQLConnectionDef);
  836. finalization
  837. UnRegisterConnection(TMySQLConnectionDef);
  838. end.