mysqlconn.inc 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041
  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. begin
  282. InitialiseMysql;
  283. {$IFDEF mysql50}
  284. if copy(strpas(mysql_get_client_info()),1,3)<>'5.0' then
  285. Raise EInOutError.CreateFmt(SErrNotversion50,[strpas(mysql_get_client_info())]);
  286. {$ELSE}
  287. {$IFDEF mysql41}
  288. if copy(strpas(mysql_get_client_info()),1,3)<>'4.1' then
  289. Raise EInOutError.CreateFmt(SErrNotversion41,[strpas(mysql_get_client_info())]);
  290. {$ELSE}
  291. if copy(strpas(mysql_get_client_info()),1,3)<>'4.0' then
  292. Raise EInOutError.CreateFmt(SErrNotversion40,[strpas(mysql_get_client_info())]);
  293. {$ENDIF}
  294. {$ENDIF}
  295. inherited DoInternalConnect;
  296. ConnectToServer;
  297. SelectDatabase;
  298. end;
  299. procedure TConnectionName.DoInternalDisconnect;
  300. begin
  301. inherited DoInternalDisconnect;
  302. mysql_close(FMySQL);
  303. FMySQL:=Nil;
  304. ReleaseMysql;
  305. end;
  306. function TConnectionName.GetHandle: pointer;
  307. begin
  308. Result:=FMySQL;
  309. end;
  310. function TConnectionName.AllocateCursorHandle: TSQLCursor;
  311. begin
  312. {$IfDef mysql50}
  313. Result:=TMySQL50Cursor.Create;
  314. {$ELSE}
  315. {$IfDef mysql41}
  316. Result:=TMySQL41Cursor.Create;
  317. {$ELSE}
  318. {$IFDEF mysql4} // temporary backwards compatibility for Lazarus
  319. Result:=TMySQLCursor.Create;
  320. {$ELSE}
  321. Result:=TMySQL40Cursor.Create;
  322. {$EndIf}
  323. {$EndIf}
  324. {$EndIf}
  325. end;
  326. Procedure TConnectionName.DeAllocateCursorHandle(var cursor : TSQLCursor);
  327. begin
  328. FreeAndNil(cursor);
  329. end;
  330. function TConnectionName.AllocateTransactionHandle: TSQLHandle;
  331. begin
  332. // Result:=TTransactionName.Create;
  333. Result := nil;
  334. end;
  335. procedure TConnectionName.PrepareStatement(cursor: TSQLCursor;
  336. ATransaction: TSQLTransaction; buf: string;AParams : TParams);
  337. begin
  338. // if assigned(AParams) and (AParams.count > 0) then
  339. // DatabaseError('Parameters (not) yet supported for the MySQL SqlDB connection.',self);
  340. With Cursor as TCursorName do
  341. begin
  342. FStatement:=Buf;
  343. if assigned(AParams) and (AParams.count > 0) then
  344. FStatement := AParams.ParseSQL(FStatement,false,sqEscapeSlash in ConnOptions, sqEscapeRepeat in ConnOptions,psSimulated,paramBinding,ParamReplaceString);
  345. if FStatementType=stSelect then
  346. FNeedData:=True;
  347. end
  348. end;
  349. procedure TConnectionName.UnPrepareStatement(cursor: TSQLCursor);
  350. begin
  351. // do nothing
  352. end;
  353. procedure TConnectionName.FreeFldBuffers(cursor: TSQLCursor);
  354. Var
  355. C : TCursorName;
  356. begin
  357. C:=Cursor as TCursorName;
  358. if c.FStatementType=stSelect then
  359. c.FNeedData:=False;
  360. If (C.FRes<>Nil) then
  361. begin
  362. Mysql_free_result(C.FRes);
  363. C.FRes:=Nil;
  364. end;
  365. SetLength(c.MapDSRowToMSQLRow,0);
  366. inherited;
  367. end;
  368. procedure TConnectionName.Execute(cursor: TSQLCursor;
  369. atransaction: tSQLtransaction;AParams : TParams);
  370. Var
  371. C : TCursorName;
  372. i : integer;
  373. ParamNames,ParamValues : array of string;
  374. begin
  375. C:=Cursor as TCursorName;
  376. If (C.FRes=Nil) then
  377. begin
  378. if Assigned(AParams) and (AParams.count > 0) then
  379. begin
  380. setlength(ParamNames,AParams.Count);
  381. setlength(ParamValues,AParams.Count);
  382. for i := 0 to AParams.count -1 do
  383. begin
  384. ParamNames[AParams.count-i-1] := C.ParamReplaceString+inttostr(AParams[i].Index+1);
  385. ParamValues[AParams.count-i-1] := GetAsSQLText(AParams[i]);
  386. end;
  387. // paramreplacestring kan een probleem geven bij postgres als hij niet meer gewoon $ is?
  388. C.FStatement := stringsreplace(C.FStatement,ParamNames,ParamValues,[rfReplaceAll]);
  389. end;
  390. if mysql_query(FMySQL,Pchar(C.FStatement))<>0 then
  391. MySQLError(FMYSQL,Format(SErrExecuting,[StrPas(mysql_error(FMySQL))]),Self)
  392. else
  393. begin
  394. C.RowsAffected := mysql_affected_rows(FMYSQL);
  395. C.LastInsertID := mysql_insert_id(FMYSQL);
  396. if C.FNeedData then
  397. C.FRes:=mysql_store_result(FMySQL);
  398. end;
  399. end;
  400. end;
  401. function TConnectionName.MySQLDataType(AType: enum_field_types; ASize, ADecimals: Integer;
  402. var NewType: TFieldType; var NewSize: Integer): Boolean;
  403. begin
  404. Result := True;
  405. case AType of
  406. FIELD_TYPE_LONGLONG:
  407. begin
  408. NewType := ftLargeint;
  409. NewSize := 0;
  410. end;
  411. FIELD_TYPE_TINY, FIELD_TYPE_SHORT:
  412. begin
  413. NewType := ftSmallint;
  414. NewSize := 0;
  415. end;
  416. FIELD_TYPE_LONG, FIELD_TYPE_INT24:
  417. begin
  418. NewType := ftInteger;
  419. NewSize := 0;
  420. end;
  421. {$ifdef mysql50}
  422. FIELD_TYPE_NEWDECIMAL,
  423. {$endif}
  424. FIELD_TYPE_DECIMAL: if ADecimals < 5 then
  425. begin
  426. NewType := ftBCD;
  427. NewSize := ADecimals;
  428. end
  429. else
  430. begin
  431. NewType := ftFloat;
  432. NewSize := 0;
  433. end;
  434. FIELD_TYPE_FLOAT, FIELD_TYPE_DOUBLE:
  435. begin
  436. NewType := ftFloat;
  437. NewSize := 0;
  438. end;
  439. FIELD_TYPE_TIMESTAMP, FIELD_TYPE_DATETIME:
  440. begin
  441. NewType := ftDateTime;
  442. NewSize := 0;
  443. end;
  444. FIELD_TYPE_DATE:
  445. begin
  446. NewType := ftDate;
  447. NewSize := 0;
  448. end;
  449. FIELD_TYPE_TIME:
  450. begin
  451. NewType := ftTime;
  452. NewSize := 0;
  453. end;
  454. FIELD_TYPE_VAR_STRING, FIELD_TYPE_STRING, FIELD_TYPE_ENUM, FIELD_TYPE_SET:
  455. begin
  456. // Since mysql server version 5.0.3 string-fields with a length of more
  457. // then 256 characters are suported
  458. if ASize>dsMaxStringSize then
  459. begin
  460. NewType := ftMemo;
  461. NewSize := 0;
  462. end
  463. else
  464. begin
  465. NewType := ftString;
  466. NewSize := ASize;
  467. end;
  468. end;
  469. FIELD_TYPE_BLOB:
  470. begin
  471. NewType := ftBlob;
  472. NewSize := 0;
  473. end
  474. else
  475. Result := False;
  476. end;
  477. end;
  478. procedure TConnectionName.AddFieldDefs(cursor: TSQLCursor;
  479. FieldDefs: TfieldDefs);
  480. var
  481. C : TCursorName;
  482. I, TF, FC: Integer;
  483. field: PMYSQL_FIELD;
  484. DFT: TFieldType;
  485. DFS: Integer;
  486. begin
  487. // Writeln('MySQL: Adding fielddefs');
  488. C:=(Cursor as TCursorName);
  489. If (C.FRes=Nil) then
  490. begin
  491. // Writeln('res is nil');
  492. MySQLError(FMySQL,SErrNoQueryResult,Self);
  493. end;
  494. // Writeln('MySQL: have result');
  495. FC:=mysql_num_fields(C.FRes);
  496. SetLength(c.MapDSRowToMSQLRow,FC);
  497. TF := 1;
  498. For I:= 0 to FC-1 do
  499. begin
  500. field := mysql_fetch_field_direct(C.FRES, I);
  501. // Writeln('MySQL: creating fielddef ',I+1);
  502. if MySQLDataType(field^.ftype, field^.length, field^.decimals, DFT, DFS) then
  503. begin
  504. TFieldDef.Create(FieldDefs, FieldDefs.MakeNameUnique(field^.name), DFT, DFS, False, TF);
  505. c.MapDSRowToMSQLRow[TF-1] := I;
  506. inc(TF);
  507. end
  508. end;
  509. // Writeln('MySQL: Finished adding fielddefs');
  510. end;
  511. function TConnectionName.Fetch(cursor: TSQLCursor): boolean;
  512. Var
  513. C : TCursorName;
  514. begin
  515. C:=Cursor as TCursorName;
  516. C.Row:=MySQL_Fetch_row(C.FRes);
  517. Result:=(C.Row<>Nil);
  518. end;
  519. function TConnectionName.LoadField(cursor : TSQLCursor;
  520. FieldDef : TfieldDef;buffer : pointer; out CreateBlob : boolean) : boolean;
  521. var
  522. field: PMYSQL_FIELD;
  523. row : MYSQL_ROW;
  524. C : TCursorName;
  525. begin
  526. // Writeln('LoadFieldsFromBuffer');
  527. C:=Cursor as TCursorName;
  528. if C.Row=nil then
  529. begin
  530. // Writeln('LoadFieldsFromBuffer: row=nil');
  531. MySQLError(FMySQL,SErrFetchingData,Self);
  532. end;
  533. Row:=C.Row;
  534. inc(Row,c.MapDSRowToMSQLRow[FieldDef.FieldNo-1]);
  535. field := mysql_fetch_field_direct(C.FRES, c.MapDSRowToMSQLRow[FieldDef.FieldNo-1]);
  536. Result := MySQLWriteData(field^.ftype, field^.length, FieldDef.DataType, Row^, Buffer, CreateBlob);
  537. end;
  538. procedure TConnectionName.LoadBlobIntoBuffer(FieldDef: TFieldDef;
  539. ABlobBuf: PBufBlobField; cursor: TSQLCursor; ATransaction: TSQLTransaction);
  540. var
  541. row : MYSQL_ROW;
  542. C : TCursorName;
  543. li : longint;
  544. Lengths : pculong;
  545. begin
  546. C:=Cursor as TCursorName;
  547. if C.Row=nil then
  548. MySQLError(FMySQL,SErrFetchingData,Self);
  549. Row:=C.Row;
  550. inc(Row,c.MapDSRowToMSQLRow[FieldDef.FieldNo-1]);
  551. Lengths := mysql_fetch_lengths(c.FRes);
  552. li := Lengths[c.MapDSRowToMSQLRow[FieldDef.FieldNo-1]];
  553. ReAllocMem(ABlobBuf^.BlobBuffer^.Buffer,li);
  554. Move(pchar(row^)^, ABlobBuf^.BlobBuffer^.Buffer^, li);
  555. ABlobBuf^.BlobBuffer^.Size := li;
  556. end;
  557. function InternalStrToFloat(S: string): Extended;
  558. var
  559. I: Integer;
  560. Tmp: string;
  561. begin
  562. Tmp := '';
  563. for I := 1 to Length(S) do
  564. begin
  565. if not (S[I] in ['0'..'9', '+', '-', 'E', 'e']) then
  566. Tmp := Tmp + DecimalSeparator
  567. else
  568. Tmp := Tmp + S[I];
  569. end;
  570. Result := StrToFloat(Tmp);
  571. end;
  572. function InternalStrToCurrency(S: string): Extended;
  573. var
  574. I: Integer;
  575. Tmp: string;
  576. begin
  577. Tmp := '';
  578. for I := 1 to Length(S) do
  579. begin
  580. if not (S[I] in ['0'..'9', '+', '-', 'E', 'e']) then
  581. Tmp := Tmp + DecimalSeparator
  582. else
  583. Tmp := Tmp + S[I];
  584. end;
  585. Result := StrToCurr(Tmp);
  586. end;
  587. function InternalStrToDate(S: string): TDateTime;
  588. var
  589. EY, EM, ED: Word;
  590. begin
  591. EY := StrToInt(Copy(S,1,4));
  592. EM := StrToInt(Copy(S,6,2));
  593. ED := StrToInt(Copy(S,9,2));
  594. if (EY = 0) or (EM = 0) or (ED = 0) then
  595. Result:=0
  596. else
  597. Result:=EncodeDate(EY, EM, ED);
  598. end;
  599. function InternalStrToDateTime(S: string): TDateTime;
  600. var
  601. EY, EM, ED: Word;
  602. EH, EN, ES: Word;
  603. begin
  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. 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 InternalStrToTime(S: string): TDateTime;
  617. var
  618. EH, EM, ES: Word;
  619. begin
  620. EH := StrToInt(Copy(S, 1, 2));
  621. EM := StrToInt(Copy(S, 4, 2));
  622. ES := StrToInt(Copy(S, 7, 2));
  623. Result := EncodeTime(EH, EM, ES, 0);
  624. end;
  625. function InternalStrToTimeStamp(S: string): TDateTime;
  626. var
  627. EY, EM, ED: Word;
  628. EH, EN, ES: Word;
  629. begin
  630. {$IFNDEF mysql40}
  631. EY := StrToInt(Copy(S, 1, 4));
  632. EM := StrToInt(Copy(S, 6, 2));
  633. ED := StrToInt(Copy(S, 9, 2));
  634. EH := StrToInt(Copy(S, 12, 2));
  635. EN := StrToInt(Copy(S, 15, 2));
  636. ES := StrToInt(Copy(S, 18, 2));
  637. {$ELSE}
  638. EY := StrToInt(Copy(S, 1, 4));
  639. EM := StrToInt(Copy(S, 5, 2));
  640. ED := StrToInt(Copy(S, 7, 2));
  641. EH := StrToInt(Copy(S, 9, 2));
  642. EN := StrToInt(Copy(S, 11, 2));
  643. ES := StrToInt(Copy(S, 13, 2));
  644. {$ENDIF}
  645. if (EY = 0) or (EM = 0) or (ED = 0) then
  646. Result := 0
  647. else
  648. Result := EncodeDate(EY, EM, ED);
  649. Result := Result + EncodeTime(EH, EN, ES, 0);;
  650. end;
  651. function TConnectionName.MySQLWriteData(AType: enum_field_types;ASize: Integer; AFieldType: TFieldType; Source, Dest: PChar; out CreateBlob : boolean): Boolean;
  652. var
  653. VI: Integer;
  654. VL: LargeInt;
  655. VS: Smallint;
  656. VF: Double;
  657. VC: Currency;
  658. VD: TDateTime;
  659. Src : String;
  660. begin
  661. Result := False;
  662. CreateBlob := False;
  663. if Source = Nil then
  664. exit;
  665. Src:=StrPas(Source);
  666. case AType of
  667. FIELD_TYPE_TINY, FIELD_TYPE_SHORT:
  668. begin
  669. if (Src<>'') then
  670. VS := StrToInt(Src)
  671. else
  672. VS := 0;
  673. Move(VS, Dest^, SizeOf(smallint));
  674. end;
  675. FIELD_TYPE_LONG, FIELD_TYPE_INT24:
  676. begin
  677. if (Src<>'') then
  678. VI := StrToInt(Src)
  679. else
  680. VI := 0;
  681. Move(VI, Dest^, SizeOf(Integer));
  682. end;
  683. FIELD_TYPE_LONGLONG:
  684. begin
  685. if (Src<>'') then
  686. VL := StrToInt64(Src)
  687. else
  688. VL := 0;
  689. Move(VL, Dest^, SizeOf(LargeInt));
  690. end;
  691. {$ifdef mysql50}
  692. FIELD_TYPE_NEWDECIMAL,
  693. {$endif}
  694. FIELD_TYPE_DECIMAL, FIELD_TYPE_FLOAT, FIELD_TYPE_DOUBLE:
  695. if AFieldType = ftBCD then
  696. begin
  697. VC := InternalStrToCurrency(Src);
  698. Move(VC, Dest^, SizeOf(Currency));
  699. end
  700. else
  701. begin
  702. if Src <> '' then
  703. VF := InternalStrToFloat(Src)
  704. else
  705. VF := 0;
  706. Move(VF, Dest^, SizeOf(Double));
  707. end;
  708. FIELD_TYPE_TIMESTAMP:
  709. begin
  710. if Src <> '' then
  711. VD := InternalStrToTimeStamp(Src)
  712. else
  713. VD := 0;
  714. Move(VD, Dest^, SizeOf(TDateTime));
  715. end;
  716. FIELD_TYPE_DATETIME:
  717. begin
  718. if Src <> '' then
  719. VD := InternalStrToDateTime(Src)
  720. else
  721. VD := 0;
  722. Move(VD, Dest^, SizeOf(TDateTime));
  723. end;
  724. FIELD_TYPE_DATE:
  725. begin
  726. if Src <> '' then
  727. VD := InternalStrToDate(Src)
  728. else
  729. VD := 0;
  730. Move(VD, Dest^, SizeOf(TDateTime));
  731. end;
  732. FIELD_TYPE_TIME:
  733. begin
  734. if Src <> '' then
  735. VD := InternalStrToTime(Src)
  736. else
  737. VD := 0;
  738. Move(VD, Dest^, SizeOf(TDateTime));
  739. end;
  740. FIELD_TYPE_VAR_STRING, FIELD_TYPE_STRING, FIELD_TYPE_ENUM, FIELD_TYPE_SET:
  741. begin
  742. { Write('Moving string of size ',asize,' : ');
  743. P:=Source;
  744. If (P<>nil) then
  745. While P[0]<>#0 do
  746. begin
  747. Write(p[0]);
  748. inc(p);
  749. end;
  750. Writeln;
  751. }
  752. // String-fields which can contain more then dsMaxStringSize characters
  753. // are mapped to ftBlob fields, while their mysql-datatype is FIELD_TYPE_BLOB
  754. if AFieldType in [ftBlob,ftMemo] then
  755. CreateBlob := True
  756. else if Src<> '' then
  757. Move(Source^, Dest^, ASize)
  758. else
  759. Dest^ := #0;
  760. end;
  761. FIELD_TYPE_BLOB:
  762. CreateBlob := True;
  763. end;
  764. Result := True;
  765. end;
  766. procedure TConnectionName.UpdateIndexDefs(IndexDefs : TIndexDefs;TableName : string);
  767. var qry : TSQLQuery;
  768. begin
  769. if not assigned(Transaction) then
  770. DatabaseError(SErrConnTransactionnSet);
  771. qry := tsqlquery.Create(nil);
  772. qry.transaction := Transaction;
  773. qry.database := Self;
  774. with qry do
  775. begin
  776. ParseSQL := False;
  777. sql.clear;
  778. sql.add('show index from ' + TableName);
  779. open;
  780. end;
  781. while not qry.eof do with IndexDefs.AddIndexDef do
  782. begin
  783. Name := trim(qry.fieldbyname('Key_name').asstring);
  784. Fields := trim(qry.fieldbyname('Column_name').asstring);
  785. If Name = 'PRIMARY' then options := options + [ixPrimary];
  786. If qry.fieldbyname('Non_unique').asinteger = 0 then options := options + [ixUnique];
  787. qry.next;
  788. while (name = trim(qry.fieldbyname('Key_name').asstring)) and (not qry.eof) do
  789. begin
  790. Fields := Fields + ';' + trim(qry.fieldbyname('Column_name').asstring);
  791. qry.next;
  792. end;
  793. end;
  794. qry.close;
  795. qry.free;
  796. end;
  797. function TConnectionName.RowsAffected(cursor: TSQLCursor): TRowsCount;
  798. begin
  799. if assigned(cursor) then
  800. // Compile this without range-checking. RowsAffected can be -1, although
  801. // it's an unsigned integer. (small joke from the mysql-guys)
  802. // Without range-checking this goes ok. If Range is turned on, this results
  803. // in range-check errors.
  804. Result := (cursor as TCursorName).RowsAffected
  805. else
  806. Result := -1;
  807. end;
  808. constructor TConnectionName.Create(AOwner: TComponent);
  809. begin
  810. inherited Create(AOwner);
  811. FConnOptions := FConnOptions + [sqEscapeRepeat] + [sqEscapeSlash];
  812. FMySQL := Nil;
  813. end;
  814. procedure TConnectionName.GetFieldNames(const TableName: string; List: TStrings);
  815. begin
  816. GetDBInfo(stColumns,TableName,'field',List);
  817. end;
  818. procedure TConnectionName.GetTableNames(List: TStrings; SystemTables: Boolean);
  819. begin
  820. GetDBInfo(stTables,'','tables_in_'+DatabaseName,List)
  821. end;
  822. function TConnectionName.GetTransactionHandle(trans: TSQLHandle): pointer;
  823. begin
  824. Result:=Nil;
  825. end;
  826. function TConnectionName.Commit(trans: TSQLHandle): boolean;
  827. begin
  828. // Do nothing.
  829. end;
  830. function TConnectionName.RollBack(trans: TSQLHandle): boolean;
  831. begin
  832. // Do nothing
  833. end;
  834. function TConnectionName.StartdbTransaction(trans: TSQLHandle; AParams : string): boolean;
  835. begin
  836. // Do nothing
  837. end;
  838. procedure TConnectionName.CommitRetaining(trans: TSQLHandle);
  839. begin
  840. // Do nothing
  841. end;
  842. procedure TConnectionName.RollBackRetaining(trans: TSQLHandle);
  843. begin
  844. // Do nothing
  845. end;
  846. function TConnectionName.GetSchemaInfoSQL(SchemaType: TSchemaType;
  847. SchemaObjectName, SchemaPattern: string): string;
  848. begin
  849. case SchemaType of
  850. stTables : result := 'show tables';
  851. stColumns : result := 'show columns from ' + EscapeString(SchemaObjectName);
  852. else
  853. DatabaseError(SMetadataUnavailable)
  854. end; {case}
  855. end;
  856. { TMySQLConnectionDef }
  857. class function TMySQLConnectionDef.TypeName: String;
  858. begin
  859. Result:='MySQL '+MySQLVersion;
  860. end;
  861. class function TMySQLConnectionDef.ConnectionClass: TSQLConnectionClass;
  862. begin
  863. {$IfDef mysql50}
  864. Result:=TMySQL50Connection;
  865. {$ELSE}
  866. {$IfDef mysql41}
  867. Result:=TMySQL41Connection;
  868. {$ELSE}
  869. {$IFDEF mysql4} // temporary backwards compatibility for Lazarus
  870. Result:=TMySQLConnection;
  871. {$ELSE}
  872. Result:=TMySQL40Connection;
  873. {$EndIf}
  874. {$EndIf}
  875. {$EndIf}
  876. end;
  877. class function TMySQLConnectionDef.Description: String;
  878. begin
  879. Result:='Connect to a MySQL '+MySQLVersion+'database directly via the client library';
  880. end;
  881. {$IfDef mysql50}
  882. initialization
  883. RegisterConnection(TMySQL50ConnectionDef);
  884. finalization
  885. UnRegisterConnection(TMySQL50ConnectionDef);
  886. {$ELSE}
  887. {$IfDef mysql41}
  888. initialization
  889. RegisterConnection(TMySQL41ConnectionDef);
  890. finalization
  891. UnRegisterConnection(TMySQL41ConnectionDef);
  892. {$ELSE}
  893. {$IFDEF mysql4} // temporary backwards compatibility for Lazarus
  894. initialization
  895. RegisterConnection(TMySQL40ConnectionDef);
  896. finalization
  897. UnRegisterConnection(TMySQL40ConnectionDef);
  898. {$ELSE}
  899. initialization
  900. RegisterConnection(TMySQL40ConnectionDef);
  901. finalization
  902. UnRegisterConnection(TMySQL40ConnectionDef);
  903. {$EndIf}
  904. {$EndIf}
  905. {$EndIf}
  906. end.