mysqlconn.inc 30 KB

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