mysqlconn.inc 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 1999-2022 by Michael van Canney and other members of the
  4. Free Pascal development team
  5. Mysql database connection component, shared parts
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. {$IFDEF MYSQL80_UP}
  13. {$DEFINE MYSQL57_UP}
  14. {$ENDIF}
  15. {$IFDEF MYSQL57_UP}
  16. {$DEFINE MYSQL56_UP}
  17. {$ENDIF}
  18. {$IFDEF MYSQL56_UP}
  19. {$DEFINE MYSQL55_UP}
  20. {$ENDIF}
  21. {$IFDEF MYSQL55_UP}
  22. {$DEFINE MYSQL51_UP}
  23. {$ENDIF}
  24. {$IFDEF MYSQL51_UP}
  25. {$DEFINE MYSQL50_UP}
  26. {$ENDIF}
  27. {$mode objfpc}{$H+}
  28. interface
  29. uses
  30. Classes, SysUtils,bufdataset,sqldb,db,ctypes,fmtbcd,
  31. {$IFDEF mysql80}
  32. mysql80dyn;
  33. {$ELSE}
  34. {$IFDEF mysql57}
  35. mysql57dyn;
  36. {$ELSE}
  37. {$IFDEF mysql56}
  38. mysql56dyn;
  39. {$ELSE}
  40. {$IFDEF mysql55}
  41. mysql55dyn;
  42. {$ELSE}
  43. {$IFDEF mysql51}
  44. mysql51dyn;
  45. {$ELSE}
  46. {$IfDef mysql50}
  47. mysql50dyn;
  48. {$ELSE}
  49. {$IfDef mysql41}
  50. mysql41dyn;
  51. {$ELSE}
  52. mysql40dyn;
  53. {$EndIf}
  54. {$EndIf}
  55. {$endif}
  56. {$endif}
  57. {$ENDIF}
  58. {$ENDIF}
  59. {$ENDIF}
  60. Const
  61. MySQLVersion =
  62. {$IFDEF mysql80}
  63. '8.0';
  64. {$ELSE}
  65. {$IFDEF mysql57}
  66. '5.7';
  67. {$ELSE}
  68. {$IFDEF mysql56}
  69. '5.6';
  70. {$ELSE}
  71. {$IFDEF mysql55}
  72. '5.5';
  73. {$ELSE}
  74. {$IFDEF mysql51}
  75. '5.1';
  76. {$else}
  77. {$IfDef mysql50}
  78. '5.0';
  79. {$ELSE}
  80. {$IfDef mysql41}
  81. '4.1';
  82. {$ELSE}
  83. '4.0';
  84. {$EndIf}
  85. {$EndIf}
  86. {$endif}
  87. {$endif}
  88. {$ENDIF}
  89. {$ENDIF}
  90. {$ENDIF}
  91. MariaDBVersion =
  92. {$IFDEF mysql57}
  93. '10.';
  94. {$ELSE}
  95. {$IFDEF mysql56} // MariaDB 10.0 is compatible with MySQL 5.6
  96. '10.';
  97. {$ELSE} // MariaDB 5.1..5.5 presumably report the same version number as MySQL
  98. MySQLVersion;
  99. {$ENDIF}
  100. {$ENDIF}
  101. Type
  102. TTransactionName = Class(TSQLHandle)
  103. protected
  104. end;
  105. { TCursorName }
  106. TCursorName = Class(TSQLCursor)
  107. protected
  108. FRes: PMYSQL_RES; { Record pointer }
  109. // Statement with param placeholders $1 $2 etc.
  110. FPreparedStatement : String;
  111. // Statement with param placeholders replaced with actual values.
  112. FActualStatement : String;
  113. FStatement : String;
  114. Row : MYSQL_ROW;
  115. Lengths : pculong; { Lengths of the columns of the current row }
  116. RowsAffected : QWord;
  117. LastInsertID : QWord;
  118. ParamBinding : TParamBinding;
  119. ParamReplaceString : String;
  120. MapDSRowToMSQLRow : array of integer;
  121. end;
  122. { TConnectionName }
  123. TConnectionName = class (TSQLConnection)
  124. private
  125. FSkipLibraryVersionCheck : Boolean;
  126. FHostInfo: String;
  127. FServerInfo: String;
  128. FMySQL : PMySQL;
  129. {$IFDEF MYSQL50_UP}
  130. FConnectionCharsetInfo: MY_CHARSET_INFO;
  131. {$ENDIF}
  132. function GetClientInfo: string;
  133. function GetServerStatus: String;
  134. procedure ConnectMySQL(var HMySQL: PMySQL);
  135. procedure ExecuteDirectMySQL(const query : string);
  136. function InternalStrToBCD(C: pchar; Len: integer): tBCD;
  137. function InternalStrToCurrency(C: pchar; Len: integer): Currency;
  138. function InternalStrToDate(C: pchar; Len: integer): TDateTime;
  139. function InternalStrToDateTime(C: pchar; Len: integer): TDateTime;
  140. function InternalStrToFloat(C: pchar; Len: integer): Extended;
  141. function InternalStrToInt(C: pchar; Len: integer): integer;
  142. function InternalStrToDWord(C: pchar; Len: integer): DWord;
  143. function InternalStrToInt64(C: pchar; Len: integer): Int64;
  144. function InternalStrToTime(C: pchar; Len: integer): TDateTime;
  145. function StrToMSecs(C: pchar; Len: integer): Word;
  146. {$IFDEF MYSQL40}
  147. function InternalStrToTimeStamp(C: pchar; Len: integer): TDateTime;
  148. {$ENDIF}
  149. protected
  150. Procedure ConnectToServer; virtual;
  151. Procedure SelectDatabase; virtual;
  152. function MySQLDataType(AField: PMYSQL_FIELD; var NewType: TFieldType; var NewSize: Integer): Boolean;
  153. function MySQLWriteData(AField: PMYSQL_FIELD; FieldDef: TFieldDef; Source, Dest: PChar; Len: integer; out CreateBlob : boolean): Boolean;
  154. function EscapeString(const Str : string) : string;
  155. // SQLConnection methods
  156. procedure DoInternalConnect; override;
  157. procedure DoInternalDisconnect; override;
  158. function GetHandle : pointer; override;
  159. function GetConnectionCharSet: string; override;
  160. function GetAsSQLText(Param : TParam) : string; overload; override;
  161. Function AllocateCursorHandle : TSQLCursor; override;
  162. Procedure DeAllocateCursorHandle(var cursor : TSQLCursor); override;
  163. Function AllocateTransactionHandle : TSQLHandle; override;
  164. function StrToStatementType(s : string) : TStatementType; override;
  165. procedure PrepareStatement(cursor: TSQLCursor;ATransaction : TSQLTransaction;buf : string; AParams : TParams); override;
  166. procedure UnPrepareStatement(cursor:TSQLCursor); override;
  167. procedure FreeFldBuffers(cursor : TSQLCursor); override;
  168. procedure Execute(cursor: TSQLCursor;atransaction:tSQLtransaction;AParams : TParams); override;
  169. procedure AddFieldDefs(cursor: TSQLCursor; FieldDefs : TfieldDefs); override;
  170. function Fetch(cursor : TSQLCursor) : boolean; override;
  171. function LoadField(cursor : TSQLCursor;FieldDef : TfieldDef;buffer : pointer; out CreateBlob : boolean) : boolean; override;
  172. procedure LoadBlobIntoBuffer(FieldDef: TFieldDef;ABlobBuf: PBufBlobField; cursor: TSQLCursor;ATransaction : TSQLTransaction); override;
  173. function GetTransactionHandle(trans : TSQLHandle): pointer; override;
  174. function Commit(trans : TSQLHandle) : boolean; override;
  175. function RollBack(trans : TSQLHandle) : boolean; override;
  176. function StartdbTransaction(trans : TSQLHandle; AParams : string) : boolean; override;
  177. procedure CommitRetaining(trans : TSQLHandle); override;
  178. procedure RollBackRetaining(trans : TSQLHandle); override;
  179. function GetSchemaInfoSQL(SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string) : string; override;
  180. procedure UpdateIndexDefs(IndexDefs : TIndexDefs;TableName : string); override;
  181. function RowsAffected(cursor: TSQLCursor): TRowsCount; override;
  182. function RefreshLastInsertID(Query : TCustomSQLQuery; Field : TField): Boolean; override;
  183. Public
  184. constructor Create(AOwner : TComponent); override;
  185. {$IFNDEF MYSQL50_UP}
  186. procedure GetFieldNames(const TableName : string; List : TStrings); override;
  187. procedure GetTableNames(List : TStrings; SystemTables : Boolean = false); override;
  188. {$ENDIF}
  189. function GetConnectionInfo(InfoType:TConnInfoType): string; override;
  190. Function GetInsertID: int64;
  191. procedure CreateDB; override;
  192. procedure DropDB; override;
  193. Property ServerInfo : String Read FServerInfo;
  194. Property HostInfo : String Read FHostInfo;
  195. property ClientInfo: string read GetClientInfo;
  196. property ServerStatus : String read GetServerStatus;
  197. published
  198. Property SkipLibraryVersionCheck : Boolean Read FSkipLibraryVersionCheck Write FSkipLibraryVersionCheck;
  199. property DatabaseName;
  200. property HostName;
  201. property KeepConnection;
  202. property LoginPrompt;
  203. property Params;
  204. property Port stored false;
  205. property OnLogin;
  206. end;
  207. { TMySQLConnectionDef }
  208. TMySQLConnectionDef = Class(TConnectionDef)
  209. Class Function TypeName : String; override;
  210. Class Function ConnectionClass : TSQLConnectionClass; override;
  211. Class Function Description : String; override;
  212. Class Function DefaultLibraryName : String; override;
  213. Class Function LoadFunction : TLibraryLoadFunction; override;
  214. Class Function UnLoadFunction : TLibraryUnLoadFunction; override;
  215. Class Function LoadedLibraryName : string; override;
  216. end;
  217. {$IFDEF mysql80}
  218. TMySQL80Connection = Class(TConnectionName);
  219. TMySQL80ConnectionDef = Class(TMySQLConnectionDef);
  220. TMySQL80Transaction = Class(TTransactionName);
  221. TMySQL80Cursor = Class(TCursorName);
  222. {$ELSE}
  223. {$IFDEF mysql57}
  224. TMySQL57Connection = Class(TConnectionName);
  225. TMySQL57ConnectionDef = Class(TMySQLConnectionDef);
  226. TMySQL57Transaction = Class(TTransactionName);
  227. TMySQL57Cursor = Class(TCursorName);
  228. {$ELSE}
  229. {$IFDEF mysql56}
  230. TMySQL56Connection = Class(TConnectionName);
  231. TMySQL56ConnectionDef = Class(TMySQLConnectionDef);
  232. TMySQL56Transaction = Class(TTransactionName);
  233. TMySQL56Cursor = Class(TCursorName);
  234. {$ELSE}
  235. {$ifdef mysql55}
  236. TMySQL55Connection = Class(TConnectionName);
  237. TMySQL55ConnectionDef = Class(TMySQLConnectionDef);
  238. TMySQL55Transaction = Class(TTransactionName);
  239. TMySQL55Cursor = Class(TCursorName);
  240. {$else}
  241. {$IfDef mysql51}
  242. TMySQL51Connection = Class(TConnectionName);
  243. TMySQL51ConnectionDef = Class(TMySQLConnectionDef);
  244. TMySQL51Transaction = Class(TTransactionName);
  245. TMySQL51Cursor = Class(TCursorName);
  246. {$ELSE}
  247. {$IfDef mysql50}
  248. TMySQL50Connection = Class(TConnectionName);
  249. TMySQL50ConnectionDef = Class(TMySQLConnectionDef);
  250. TMySQL50Transaction = Class(TTransactionName);
  251. TMySQL50Cursor = Class(TCursorName);
  252. {$ELSE}
  253. {$IfDef mysql41}
  254. TMySQL41Connection = Class(TConnectionName);
  255. TMySQL41ConnectionDef = Class(TMySQLConnectionDef);
  256. TMySQL41Transaction = Class(TTransactionName);
  257. TMySQL41Cursor = Class(TCursorName);
  258. {$ELSE}
  259. TMySQL40Connection = Class(TConnectionName);
  260. TMySQL40ConnectionDef = Class(TMySQLConnectionDef);
  261. TMySQL40Transaction = Class(TTransactionName);
  262. TMySQL40Cursor = Class(TCursorName);
  263. {$EndIf}
  264. {$endif}
  265. {$EndIf}
  266. {$ENDIF}
  267. {$ENDIF}
  268. {$ENDIF}
  269. {$ENDIF}
  270. implementation
  271. uses
  272. dbconst,
  273. StrUtils,
  274. DateUtils;
  275. const
  276. Mysql_Option_Names : array[mysql_option] of string = (
  277. 'MYSQL_OPT_CONNECT_TIMEOUT',
  278. 'MYSQL_OPT_COMPRESS',
  279. 'MYSQL_OPT_NAMED_PIPE',
  280. 'MYSQL_INIT_COMMAND',
  281. 'MYSQL_READ_DEFAULT_FILE',
  282. 'MYSQL_READ_DEFAULT_GROUP',
  283. 'MYSQL_SET_CHARSET_DIR',
  284. 'MYSQL_SET_CHARSET_NAME',
  285. 'MYSQL_OPT_LOCAL_INFILE',
  286. 'MYSQL_OPT_PROTOCOL',
  287. 'MYSQL_SHARED_MEMORY_BASE_NAME',
  288. 'MYSQL_OPT_READ_TIMEOUT',
  289. 'MYSQL_OPT_WRITE_TIMEOUT',
  290. 'MYSQL_OPT_USE_RESULT'
  291. {$IFDEF MYSQL80}
  292. ,'MYSQL_REPORT_DATA_TRUNCATION',
  293. 'MYSQL_OPT_RECONNECT',
  294. 'MYSQL_PLUGIN_DIR',
  295. 'MYSQL_DEFAULT_AUTH',
  296. 'MYSQL_OPT_BIND',
  297. 'MYSQL_OPT_SSL_KEY',
  298. 'MYSQL_OPT_SSL_CERT',
  299. 'MYSQL_OPT_SSL_CA',
  300. 'MYSQL_OPT_SSL_CAPATH',
  301. 'MYSQL_OPT_SSL_CIPHER',
  302. 'MYSQL_OPT_SSL_CRL',
  303. 'MYSQL_OPT_SSL_CRLPATH',
  304. 'MYSQL_OPT_CONNECT_ATTR_RESET',
  305. 'MYSQL_OPT_CONNECT_ATTR_ADD',
  306. 'MYSQL_OPT_CONNECT_ATTR_DELETE',
  307. 'MYSQL_SERVER_PUBLIC_KEY',
  308. 'MYSQL_ENABLE_CLEARTEXT_PLUGIN',
  309. 'MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS',
  310. 'MYSQL_OPT_MAX_ALLOWED_PACKET',
  311. 'MYSQL_OPT_NET_BUFFER_LENGTH',
  312. 'MYSQL_OPT_TLS_VERSION',
  313. 'MYSQL_OPT_SSL_MODE',
  314. 'MYSQL_OPT_GET_SERVER_PUBLIC_KEY',
  315. 'MYSQL_OPT_RETRY_COUNT',
  316. 'MYSQL_OPT_OPTIONAL_RESULTSET_METADATA',
  317. 'MYSQL_OPT_SSL_FIPS_MODE',
  318. 'MYSQL_OPT_TLS_CIPHERSUITES',
  319. 'MYSQL_OPT_COMPRESSION_ALGORITHMS',
  320. 'MYSQL_OPT_ZSTD_COMPRESSION_LEVEL',
  321. 'MYSQL_OPT_LOAD_DATA_LOCAL_DIR'
  322. {$ELSE}
  323. ,'MYSQL_OPT_USE_REMOTE_CONNECTION',
  324. 'MYSQL_OPT_USE_EMBEDDED_CONNECTION',
  325. 'MYSQL_OPT_GUESS_CONNECTION',
  326. 'MYSQL_SET_CLIENT_IP',
  327. 'MYSQL_SECURE_AUTH'
  328. {$IFDEF MYSQL50_UP}
  329. ,'MYSQL_REPORT_DATA_TRUNCATION', 'MYSQL_OPT_RECONNECT'
  330. {$IFDEF mysql51_UP}
  331. ,'MYSQL_OPT_SSL_VERIFY_SERVER_CERT'
  332. {$IFDEF mysql55_UP}
  333. ,'MYSQL_PLUGIN_DIR', 'MYSQL_DEFAULT_AUTH'
  334. {$IFDEF MYSQL56_UP}
  335. ,'MYSQL_OPT_BIND'
  336. ,'MYSQL_OPT_SSL_KEY', 'MYSQL_OPT_SSL_CERT', 'MYSQL_OPT_SSL_CA', 'MYSQL_OPT_SSL_CAPATH', 'MYSQL_OPT_SSL_CIPHER', 'MYSQL_OPT_SSL_CRL', 'MYSQL_OPT_SSL_CRLPATH'
  337. ,'MYSQL_OPT_CONNECT_ATTR_RESET', 'MYSQL_OPT_CONNECT_ATTR_ADD', 'MYSQL_OPT_CONNECT_ATTR_DELETE'
  338. ,'MYSQL_SERVER_PUBLIC_KEY'
  339. ,'MYSQL_ENABLE_CLEARTEXT_PLUGIN'
  340. ,'MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS'
  341. {$IFDEF MYSQL57_UP}
  342. ,'MYSQL_OPT_SSL_ENFORCE'
  343. {$ENDIF}
  344. {$ENDIF}
  345. {$ENDIF}
  346. {$ENDIF}
  347. {$ENDIF}
  348. {$ENDIF}
  349. );
  350. Resourcestring
  351. SErrServerConnectFailed = 'Server connect failed.';
  352. SErrSetCharsetFailed = 'Failed to set connection character set: %s';
  353. SErrDatabaseSelectFailed = 'Failed to select database: %s';
  354. //SErrDatabaseCreate = 'Failed to create database: %s';
  355. //SErrDatabaseDrop = 'Failed to drop database: %s';
  356. //SErrNoData = 'No data for record';
  357. SErrExecuting = 'Error executing query: %s';
  358. SErrFetchingdata = 'Error fetching row data: %s';
  359. SErrGettingResult = 'Error getting result set: %s';
  360. SErrNoQueryResult = 'No result from query.';
  361. SErrVersionMismatch = '%s can not work with the installed MySQL client version: Expected (%s), got (%s).';
  362. SErrSettingParameter = 'Error setting parameter "%s"';
  363. Procedure MySQLError(R : PMySQL; Msg: String; Comp : TComponent);
  364. Var
  365. MySQLError, MySQLState : String;
  366. MySQLErrno: integer;
  367. begin
  368. If (R<>Nil) then
  369. begin
  370. MySQLError:=StrPas(mysql_error(R));
  371. MySQLErrno:=mysql_errno(R);
  372. MySQLState:=StrPas(mysql_sqlstate(R));
  373. end
  374. else
  375. begin
  376. MySQLError:='';
  377. MySQLErrno:=0;
  378. MySQLState:='';
  379. end;
  380. raise ESQLDatabaseError.CreateFmt(Msg, [MySQLError], Comp, MySQLErrno, MySQLState);
  381. end;
  382. function MysqlOption(const OptionName: string; out AMysql_Option: mysql_option) : boolean;
  383. var AMysql_Option_i: mysql_option;
  384. begin
  385. result := false;
  386. for AMysql_Option_i:=low(AMysql_Option) to high(AMysql_Option) do
  387. if sametext(Mysql_Option_Names[AMysql_Option_i],OptionName) then
  388. begin
  389. result := true;
  390. AMysql_Option:=AMysql_Option_i;
  391. break;
  392. end;
  393. end;
  394. { TConnectionName }
  395. function TConnectionName.StrToStatementType(s : string) : TStatementType;
  396. begin
  397. s:=Lowercase(s);
  398. if (s='analyze') or (s='check') or (s='checksum') or (s='optimize') or (s='repair') or (s='show') then
  399. exit(stSelect)
  400. else if s='call' then
  401. exit(stExecProcedure)
  402. else
  403. Result := inherited StrToStatementType(s);
  404. end;
  405. function TConnectionName.GetClientInfo: string;
  406. begin
  407. // To make it possible to call this if there's no connection yet
  408. InitialiseMysql;
  409. Try
  410. Result:=strpas(mysql_get_client_info());
  411. Finally
  412. ReleaseMysql;
  413. end;
  414. end;
  415. function TConnectionName.GetServerStatus: String;
  416. begin
  417. CheckConnected;
  418. Result := mysql_stat(FMYSQL);
  419. end;
  420. Function TConnectionName.GetInsertID: int64;
  421. begin
  422. CheckConnected;
  423. Result:=mysql_insert_id(GetHandle);
  424. end;
  425. procedure TConnectionName.ConnectMySQL(var HMySQL: PMySQL);
  426. Var
  427. APort : Cardinal;
  428. i,e: integer;
  429. AMysql_Option: mysql_option;
  430. OptStr: string;
  431. OptInt: cuint;
  432. Opt: pointer;
  433. begin
  434. HMySQL := mysql_init(HMySQL);
  435. APort:=Abs(StrToIntDef(Params.Values['Port'],0));
  436. for i := 0 to Params.Count-1 do
  437. begin
  438. if MysqlOption(Params.Names[i],AMysql_Option) then
  439. begin
  440. OptStr:=Params.ValueFromIndex[i];
  441. val(OptStr,OptInt,e);
  442. if e=0 then
  443. Opt := @OptInt
  444. else
  445. Opt := pchar(OptStr);
  446. if mysql_options(HMySQL,AMysql_Option,Opt) <> 0 then
  447. MySQLError(HMySQL,Format(SErrSettingParameter,[Params.Names[i]]),Self);
  448. end;
  449. end;
  450. if mysql_real_connect(HMySQL,PChar(HostName),PChar(UserName),PChar(Password),Nil,APort,Nil,CLIENT_MULTI_RESULTS) = nil then //CLIENT_MULTI_RESULTS is required by CALL SQL statement(executes stored procedure), that produces result sets
  451. MySQLError(HMySQL,SErrServerConnectFailed,Self);
  452. if (trim(CharSet) <> '') then
  453. // major_version*10000 + minor_version *100 + sub_version
  454. if (50007 <= mysql_get_server_version(HMySQL)) then
  455. begin
  456. // Only available for MySQL 5.0.7 and later...
  457. if mysql_set_character_set(HMySQL, PChar(CharSet)) <> 0 then
  458. MySQLError(HMySQL,SErrSetCharsetFailed,Self);
  459. end
  460. else
  461. if mysql_query(HMySQL,PChar('SET NAMES ''' + EscapeString(CharSet) +'''')) <> 0 then
  462. MySQLError(HMySQL,SErrExecuting,Self);
  463. end;
  464. function TConnectionName.GetAsSQLText(Param: TParam) : string;
  465. begin
  466. if (not assigned(Param)) or Param.IsNull then
  467. Result := 'Null'
  468. else if Param.DataType in [ftString,ftFixedChar,ftMemo] then
  469. Result := '''' + EscapeString(GetAsString(Param)) + ''''
  470. else if Param.DataType in [ftBlob,ftBytes,ftVarBytes] then
  471. Result := '''' + EscapeString(Param.AsString) + ''''
  472. else
  473. Result := inherited GetAsSQLText(Param);
  474. end;
  475. Procedure TConnectionName.ConnectToServer;
  476. begin
  477. ConnectMySQL(FMySQL);
  478. FServerInfo := strpas(mysql_get_server_info(FMYSQL));
  479. FHostInfo := strpas(mysql_get_host_info(FMYSQL));
  480. {$IFDEF MYSQL50_UP}
  481. mysql_get_character_set_info(FMYSQL, @FConnectionCharsetInfo);
  482. {$ENDIF}
  483. end;
  484. Procedure TConnectionName.SelectDatabase;
  485. begin
  486. if mysql_select_db(FMySQL,pchar(DatabaseName))<>0 then
  487. MySQLError(FMySQL,SErrDatabaseSelectFailed,Self);
  488. end;
  489. procedure TConnectionName.CreateDB;
  490. begin
  491. ExecuteDirectMySQL('CREATE DATABASE ' +DatabaseName);
  492. end;
  493. procedure TConnectionName.DropDB;
  494. begin
  495. ExecuteDirectMySQL('DROP DATABASE ' +DatabaseName);
  496. end;
  497. procedure TConnectionName.ExecuteDirectMySQL(const query : string);
  498. var AMySQL : PMySQL;
  499. begin
  500. CheckDisConnected;
  501. InitialiseMysql;
  502. try
  503. AMySQL := nil;
  504. ConnectMySQL(AMySQL);
  505. try
  506. if mysql_query(AMySQL,pchar(query))<>0 then
  507. MySQLError(AMySQL,SErrExecuting,Self);
  508. finally
  509. mysql_close(AMySQL);
  510. end;
  511. finally
  512. ReleaseMysql;
  513. end;
  514. end;
  515. function TConnectionName.EscapeString(const Str: string): string;
  516. var Len : integer;
  517. begin
  518. SetLength(result,length(str)*2+1);
  519. Len := mysql_real_escape_string(FMySQL,pchar(Result),pchar(Str),length(Str));
  520. SetLength(result,Len);
  521. end;
  522. procedure TConnectionName.DoInternalConnect;
  523. var
  524. FullVersion: string;
  525. begin
  526. InitialiseMysql;
  527. if not SkipLibraryVersionCheck then
  528. begin
  529. FullVersion:=strpas(mysql_get_client_info());
  530. // Version string should start with version number:
  531. // Note: in case of MariaDB version mismatch: tough luck, we report MySQL
  532. // version only.
  533. if (pos(MySQLVersion, FullVersion) <> 1) and
  534. (pos(MariaDBVersion, FullVersion) <> 1) then
  535. Raise EInOutError.CreateFmt(SErrVersionMisMatch,[ClassName,MySQLVersion,FullVersion]);
  536. end;
  537. inherited DoInternalConnect;
  538. ConnectToServer;
  539. SelectDatabase;
  540. end;
  541. procedure TConnectionName.DoInternalDisconnect;
  542. begin
  543. inherited DoInternalDisconnect;
  544. mysql_close(FMySQL);
  545. FMySQL:=Nil;
  546. ReleaseMysql;
  547. end;
  548. function TConnectionName.GetHandle: pointer;
  549. begin
  550. Result:=FMySQL;
  551. end;
  552. function TConnectionName.GetConnectionCharSet: string;
  553. begin
  554. Result:=StrPas(mysql_character_set_name(FMySQL));
  555. end;
  556. Function TConnectionName.AllocateCursorHandle: TSQLCursor;
  557. begin
  558. {$IFDEF mysql80}
  559. Result:=TMySQL80Cursor.Create;
  560. {$ELSE}
  561. {$IFDEF mysql57}
  562. Result:=TMySQL57Cursor.Create;
  563. {$ELSE}
  564. {$IFDEF mysql56}
  565. Result:=TMySQL56Cursor.Create;
  566. {$ELSE}
  567. {$IfDef mysql55}
  568. Result:=TMySQL55Cursor.Create;
  569. {$ELSE}
  570. {$IfDef mysql51}
  571. Result:=TMySQL51Cursor.Create;
  572. {$ELSE}
  573. {$IfDef mysql50}
  574. Result:=TMySQL50Cursor.Create;
  575. {$ELSE}
  576. {$IfDef mysql41}
  577. Result:=TMySQL41Cursor.Create;
  578. {$ELSE}
  579. Result:=TMySQL40Cursor.Create;
  580. {$EndIf}
  581. {$EndIf}
  582. {$EndIf}
  583. {$EndIf}
  584. {$ENDIF}
  585. {$ENDIF}
  586. {$ENDIF}
  587. end;
  588. Procedure TConnectionName.DeAllocateCursorHandle(var cursor : TSQLCursor);
  589. begin
  590. FreeAndNil(cursor);
  591. end;
  592. Function TConnectionName.AllocateTransactionHandle: TSQLHandle;
  593. begin
  594. // Result:=TTransactionName.Create;
  595. Result := nil;
  596. end;
  597. procedure TConnectionName.PrepareStatement(cursor: TSQLCursor;
  598. ATransaction: TSQLTransaction; buf: string;AParams : TParams);
  599. begin
  600. // if assigned(AParams) and (AParams.count > 0) then
  601. // DatabaseError('Parameters (not) yet supported for the MySQL SqlDB connection.',self);
  602. With Cursor as TCursorName do
  603. begin
  604. FPreparedStatement:=Buf;
  605. if assigned(AParams) and (AParams.count > 0) then
  606. FPreparedStatement := AParams.ParseSQL(FPreparedStatement,false,sqEscapeSlash in ConnOptions, sqEscapeRepeat in ConnOptions,psSimulated,paramBinding,ParamReplaceString);
  607. FPrepared:=True;
  608. end;
  609. end;
  610. procedure TConnectionName.UnPrepareStatement(cursor: TSQLCursor);
  611. Var
  612. C : TCursorName;
  613. begin
  614. C:=Cursor as TCursorName;
  615. if assigned(C.FRes) then //ExecSQL with dataset returned
  616. begin
  617. mysql_free_result(C.FRes);
  618. C.FRes:=nil;
  619. end;
  620. end;
  621. procedure TConnectionName.FreeFldBuffers(cursor: TSQLCursor);
  622. Var
  623. C : TCursorName;
  624. begin
  625. C:=Cursor as TCursorName;
  626. if assigned(C.FRes) then
  627. begin
  628. mysql_free_result(C.FRes);
  629. C.FRes:=Nil;
  630. end;
  631. C.FInitFieldDef:=True;
  632. SetLength(c.MapDSRowToMSQLRow,0);
  633. inherited;
  634. end;
  635. procedure TConnectionName.Execute(cursor: TSQLCursor;
  636. atransaction: tSQLtransaction;AParams : TParams);
  637. Var
  638. C : TCursorName;
  639. i : integer;
  640. ParamNames,ParamValues : array of string;
  641. Res: PMYSQL_RES;
  642. Status : Integer;
  643. begin
  644. C:=Cursor as TCursorName;
  645. If (C.FRes=Nil) then
  646. begin
  647. if Assigned(AParams) and (AParams.count > 0) then
  648. begin
  649. setlength(ParamNames,AParams.Count);
  650. setlength(ParamValues,AParams.Count);
  651. for i := 0 to AParams.count -1 do
  652. begin
  653. ParamNames[AParams.count-i-1] := C.ParamReplaceString+inttostr(AParams[i].Index+1);
  654. ParamValues[AParams.count-i-1] := GetAsSQLText(AParams[i]);
  655. end;
  656. C.FActualStatement := stringsreplace(C.FPreparedStatement,ParamNames,ParamValues,[rfReplaceAll]);
  657. end
  658. else
  659. C.FActualStatement:=C.FPreparedStatement;
  660. if LogEvent(detParamValue) then
  661. LogParams(AParams);
  662. if LogEvent(detExecute) then
  663. Log(detExecute, C.FPreparedStatement);
  664. if LogEvent(detActualSQL) then
  665. Log(detActualSQL,C.FActualStatement);
  666. if mysql_query(FMySQL,Pchar(C.FActualStatement))<>0 then
  667. begin
  668. if not ForcedClose then
  669. MySQLError(FMYSQL,SErrExecuting,Self)
  670. else //don't return a resulset. We are shutting down, not opening.
  671. begin
  672. C.RowsAffected:=0;
  673. C.FSelectable:= False;
  674. C.FRes:=nil;
  675. end;
  676. end
  677. else
  678. begin
  679. C.RowsAffected := mysql_affected_rows(FMYSQL);
  680. C.LastInsertID := mysql_insert_id(FMYSQL);
  681. C.FSelectable := False;
  682. repeat
  683. Res:=mysql_store_result(FMySQL); //returns a null pointer also if the statement didn't return a result set
  684. if mysql_errno(FMySQL)<>0 then
  685. begin
  686. if not ForcedClose then
  687. MySQLError(FMySQL, SErrGettingResult, Self)
  688. else
  689. begin
  690. C.RowsAffected:=0;
  691. C.FSelectable:= False;
  692. C.FRes:=nil;
  693. break;
  694. end;
  695. end;
  696. if Res<>nil then
  697. begin
  698. mysql_free_result(C.FRes);
  699. C.FRes:=Res;
  700. C.FSelectable:=True;
  701. end;
  702. Status:=mysql_next_result(FMySQL);
  703. if (Status>0) then
  704. begin
  705. if not ForcedClose then
  706. MySQLError(FMySQL, SErrGettingResult, Self)
  707. else
  708. begin
  709. C.RowsAffected:=0;
  710. C.FSelectable:= False;
  711. C.FRes:=nil;
  712. break;
  713. end;
  714. end;
  715. until (Status<>0);
  716. end;
  717. end;
  718. end;
  719. function TConnectionName.MySQLDataType(AField: PMYSQL_FIELD; var NewType: TFieldType; var NewSize: Integer): Boolean;
  720. var
  721. ASize: culong;
  722. ADecimals: cuint;
  723. begin
  724. Result := True;
  725. ASize := AField^.length;
  726. NewSize := 0;
  727. case AField^.ftype of
  728. FIELD_TYPE_LONGLONG:
  729. begin
  730. NewType := ftLargeint;
  731. end;
  732. FIELD_TYPE_TINY, FIELD_TYPE_SHORT, FIELD_TYPE_YEAR:
  733. begin
  734. if AField^.flags and UNSIGNED_FLAG <> 0 then
  735. NewType := ftWord
  736. else
  737. NewType := ftSmallint;
  738. end;
  739. FIELD_TYPE_LONG, FIELD_TYPE_INT24:
  740. begin
  741. if AField^.flags and AUTO_INCREMENT_FLAG <> 0 then
  742. NewType := ftAutoInc
  743. else
  744. NewType := ftInteger;
  745. end;
  746. {$ifdef mysql50_up}
  747. FIELD_TYPE_NEWDECIMAL,
  748. {$endif}
  749. FIELD_TYPE_DECIMAL:
  750. begin
  751. ADecimals:=AField^.decimals;
  752. if (ADecimals < 5) and (ASize-2-ADecimals < 15) then //ASize is display size i.e. with sign and decimal point
  753. NewType := ftBCD
  754. else if (ADecimals = 0) and (ASize < 20) then
  755. NewType := ftLargeInt
  756. else
  757. NewType := ftFmtBCD;
  758. NewSize := ADecimals;
  759. end;
  760. FIELD_TYPE_FLOAT, FIELD_TYPE_DOUBLE:
  761. begin
  762. NewType := ftFloat;
  763. end;
  764. FIELD_TYPE_TIMESTAMP, FIELD_TYPE_DATETIME:
  765. begin
  766. NewType := ftDateTime;
  767. end;
  768. FIELD_TYPE_DATE:
  769. begin
  770. NewType := ftDate;
  771. end;
  772. FIELD_TYPE_TIME:
  773. begin
  774. NewType := ftTime;
  775. end;
  776. FIELD_TYPE_VAR_STRING, FIELD_TYPE_STRING, FIELD_TYPE_ENUM, FIELD_TYPE_SET:
  777. begin
  778. // Since mysql server version 5.0.3 string-fields with a length of more
  779. // then 256 characters are suported
  780. if AField^.ftype = FIELD_TYPE_STRING then
  781. NewType := ftFixedChar
  782. else
  783. NewType := ftString;
  784. {$IFDEF MYSQL50_UP}
  785. if AField^.charsetnr = 63 then begin //BINARY vs. CHAR, VARBINARY vs. VARCHAR
  786. if NewType = ftFixedChar then
  787. NewType := ftBytes
  788. else
  789. NewType := ftVarBytes;
  790. NewSize := ASize;
  791. end
  792. else
  793. NewSize := ASize div FConnectionCharsetInfo.mbmaxlen;
  794. {$ELSE}
  795. NewSize := ASize;
  796. {$ENDIF}
  797. end;
  798. FIELD_TYPE_TINY_BLOB..FIELD_TYPE_BLOB:
  799. begin
  800. {$IFDEF MYSQL50_UP}
  801. if AField^.charsetnr = 63 then //character set is binary
  802. NewType := ftBlob
  803. else
  804. NewType := ftMemo;
  805. {$ELSE}
  806. NewType := ftBlob;
  807. {$ENDIF}
  808. end;
  809. {$IFDEF MYSQL50_UP}
  810. FIELD_TYPE_BIT:
  811. NewType := ftLargeInt;
  812. {$ENDIF}
  813. else
  814. Result := False;
  815. end;
  816. end;
  817. procedure TConnectionName.AddFieldDefs(cursor: TSQLCursor;
  818. FieldDefs: TfieldDefs);
  819. var
  820. C : TCursorName;
  821. I, FC: Integer;
  822. field: PMYSQL_FIELD;
  823. DFT: TFieldType;
  824. DFS: Integer;
  825. begin
  826. // Writeln('MySQL: Adding fielddefs');
  827. C:=(Cursor as TCursorName);
  828. If (C.FRes=Nil) then
  829. begin
  830. // Writeln('res is nil');
  831. MySQLError(FMySQL,SErrNoQueryResult,Self);
  832. end;
  833. // Writeln('MySQL: have result');
  834. FC:=mysql_num_fields(C.FRes);
  835. SetLength(c.MapDSRowToMSQLRow,FC);
  836. For I := 0 to FC-1 do
  837. begin
  838. field := mysql_fetch_field_direct(C.FRES, I);
  839. // Writeln('MySQL: creating fielddef ',I+1);
  840. if MySQLDataType(field, DFT, DFS) then
  841. begin
  842. AddFieldDef(FieldDefs, I+1, field^.name, DFT, DFS, -1,
  843. False,
  844. (field^.flags and (AUTO_INCREMENT_FLAG or NOT_NULL_FLAG {$IFDEF MYSQL50_UP}or NO_DEFAULT_VALUE_FLAG{$ENDIF})) = (NOT_NULL_FLAG {$IFDEF MYSQL50_UP}or NO_DEFAULT_VALUE_FLAG{$ENDIF}),
  845. False);
  846. c.MapDSRowToMSQLRow[I] := I;
  847. end
  848. end;
  849. // Writeln('MySQL: Finished adding fielddefs');
  850. end;
  851. function TConnectionName.Fetch(cursor: TSQLCursor): boolean;
  852. Var
  853. C : TCursorName;
  854. begin
  855. C:=Cursor as TCursorName;
  856. C.Row:=MySQL_Fetch_row(C.FRes);
  857. Result:=(C.Row<>Nil);
  858. if Result then
  859. C.Lengths := mysql_fetch_lengths(C.FRes)
  860. else
  861. C.Lengths := nil;
  862. end;
  863. function TConnectionName.LoadField(cursor : TSQLCursor;
  864. FieldDef : TfieldDef;buffer : pointer; out CreateBlob : boolean) : boolean;
  865. var
  866. field: PMYSQL_FIELD;
  867. C : TCursorName;
  868. i : integer;
  869. begin
  870. // Writeln('LoadFieldsFromBuffer');
  871. C:=Cursor as TCursorName;
  872. if (C.Row=nil) or (C.Lengths=nil) then
  873. begin
  874. // Writeln('LoadFieldsFromBuffer: row=nil');
  875. MySQLError(FMySQL,SErrFetchingData,Self);
  876. end;
  877. i := c.MapDSRowToMSQLRow[FieldDef.FieldNo-1];
  878. field := mysql_fetch_field_direct(C.FRES, i);
  879. Result := MySQLWriteData(field, FieldDef, C.Row[i], Buffer, C.Lengths[i], CreateBlob);
  880. end;
  881. procedure TConnectionName.LoadBlobIntoBuffer(FieldDef: TFieldDef;
  882. ABlobBuf: PBufBlobField; cursor: TSQLCursor; ATransaction: TSQLTransaction);
  883. var
  884. C : TCursorName;
  885. i : integer;
  886. len : longint;
  887. begin
  888. C:=Cursor as TCursorName;
  889. if (C.Row=nil) or (C.Lengths=nil) then
  890. MySQLError(FMySQL,SErrFetchingData,Self);
  891. i := c.MapDSRowToMSQLRow[FieldDef.FieldNo-1];
  892. len := C.Lengths[i];
  893. ReAllocMem(ABlobBuf^.BlobBuffer^.Buffer, len);
  894. Move(C.Row[i]^, ABlobBuf^.BlobBuffer^.Buffer^, len);
  895. ABlobBuf^.BlobBuffer^.Size := len;
  896. end;
  897. function TConnectionName.InternalStrToInt(C: pchar; Len: integer): integer;
  898. Var
  899. S : String;
  900. begin
  901. Result := 0;
  902. if (Len=0) or (C=Nil) then
  903. exit;
  904. SetString(S,C,Len);
  905. Result:=StrToInt(S);
  906. end;
  907. function TConnectionName.InternalStrToDWord(C: pchar; Len: integer): DWord;
  908. Var
  909. S : String;
  910. begin
  911. Result := 0;
  912. if (Len=0) or (C=Nil) then
  913. exit;
  914. SetString(S,C,Len);
  915. Result:=StrToDWord(S);
  916. end;
  917. function TConnectionName.InternalStrToInt64(C: pchar; Len: integer): Int64;
  918. Var
  919. S : String;
  920. begin
  921. Result := 0;
  922. if (Len=0) or (C=Nil) then
  923. exit;
  924. SetString(S,C,Len);
  925. Result:=StrToInt64(S);
  926. end;
  927. function TConnectionName.InternalStrToFloat(C: pchar; Len: integer): Extended;
  928. var
  929. Tmp: string;
  930. begin
  931. SetString(Tmp, C, Len);
  932. if Tmp='' then
  933. Exit(0);
  934. Result := StrToFloat(Tmp, FSQLFormatSettings);
  935. end;
  936. function TConnectionName.InternalStrToCurrency(C: pchar; Len: integer): Currency;
  937. var
  938. Tmp: string;
  939. begin
  940. SetString(Tmp, C, Len);
  941. if Tmp='' then
  942. Exit(0);
  943. Result := StrToCurr(Tmp, FSQLFormatSettings);
  944. end;
  945. function TConnectionName.InternalStrToBCD(C: pchar; Len: integer): tBCD;
  946. var
  947. Tmp: string;
  948. begin
  949. SetString(Tmp, C, Len);
  950. if Tmp='' then
  951. Exit(0);
  952. Result := StrToBCD(Tmp, FSQLFormatSettings);
  953. end;
  954. function TConnectionName.InternalStrToDate(C: pchar; Len: integer): TDateTime;
  955. var
  956. EY, EM, ED: Word;
  957. begin
  958. if Len=0 then
  959. Exit(0);
  960. if Len<10 then
  961. raise EConvertError.Create('Invalid date string');
  962. EY := InternalStrToInt(C,4);
  963. EM := InternalStrToInt(C+5,2);
  964. ED := InternalStrToInt(C+8,2);
  965. if (EY = 0) or (EM = 0) or (ED = 0) then
  966. Result:=0
  967. else
  968. Result:=EncodeDate(EY, EM, ED);
  969. end;
  970. function TConnectionName.StrToMSecs(C: pchar; Len: integer): Word;
  971. {$IFDEF MYSQL56_UP}
  972. var I: Integer;
  973. d, MSecs: double;
  974. {$ENDIF}
  975. begin
  976. {$IFDEF MYSQL56_UP}
  977. // datetime(n), where n is fractional seconds precision (between 0 and 6)
  978. MSecs := 0;
  979. d := 100;
  980. for I := 1 to Len do
  981. begin
  982. case C^ of
  983. '0'..'9': MSecs := MSecs + (ord(C^)-ord('0'))*d;
  984. #0: break;
  985. end;
  986. d := d / 10;
  987. Inc(C);
  988. end;
  989. Result := Round(MSecs);
  990. {$ELSE}
  991. Result := 0;
  992. {$ENDIF}
  993. end;
  994. function TConnectionName.InternalStrToDateTime(C: pchar; Len: integer): TDateTime;
  995. var
  996. EY, EM, ED: Word;
  997. EH, EN, ES, EMS: Word;
  998. begin
  999. if Len=0 then
  1000. Exit(0);
  1001. if Len<19 then
  1002. raise EConvertError.Create('Invalid datetime string');
  1003. EY := InternalStrToInt(C,4);
  1004. EM := InternalStrToInt(C+5,2);
  1005. ED := InternalStrToInt(C+8,2);
  1006. EH := InternalStrToInt(C+11, 2);
  1007. EN := InternalStrToInt(C+14, 2);
  1008. ES := InternalStrToInt(C+17, 2);
  1009. if Len>20 then
  1010. EMS := StrToMSecs(C+20, Len-20)
  1011. else
  1012. EMS := 0;
  1013. if (EY = 0) or (EM = 0) or (ED = 0) then
  1014. Result := 0
  1015. else
  1016. Result := EncodeDate(EY, EM, ED);
  1017. Result := ComposeDateTime(Result, EncodeTimeInterval(EH, EN, ES, EMS));
  1018. end;
  1019. function TConnectionName.InternalStrToTime(C: pchar; Len: integer): TDateTime;
  1020. var
  1021. EH, EM, ES, EMS: Word;
  1022. M: PChar;
  1023. I: Integer;
  1024. begin
  1025. if Len=0 then
  1026. Exit(0);
  1027. if Len<8 then
  1028. raise EConvertError.Create('Invalid time string');
  1029. //hours can be 2 or 3 digits
  1030. M:=C;
  1031. for I := 1 to Len do
  1032. begin
  1033. if M^=':' then
  1034. break;
  1035. Inc(M);
  1036. end;
  1037. if M^<>':' then
  1038. raise EConvertError.Create('Invalid time string');
  1039. EH := InternalStrToInt(C, NativeInt(M-C));
  1040. EM := InternalStrToInt(M+1, 2);
  1041. ES := InternalStrToInt(M+4, 2);
  1042. if Len>NativeInt(M-C)+7 then
  1043. EMS := StrToMSecs(M+7, Len-(NativeInt(M-C)+7))
  1044. else
  1045. EMS := 0;
  1046. Result := EncodeTimeInterval(EH, EM, ES, EMS);
  1047. end;
  1048. {$IFDEF mysql40}
  1049. function TConnectionName.InternalStrToTimeStamp(C: pchar; Len: integer): TDateTime;
  1050. var
  1051. EY, EM, ED: Word;
  1052. EH, EN, ES: Word;
  1053. begin
  1054. if Len=0 then
  1055. Exit(0);
  1056. if Len<14 then
  1057. raise EConvertError.Create('Invalid timestamp string');
  1058. EY := InternalStrToInt(C, 4);
  1059. EM := InternalStrToInt(C+4, 2));
  1060. ED := InternalStrToInt(C+6, 2));
  1061. EH := InternalStrToInt(C+8, 2));
  1062. EN := InternalStrToInt(C+10, 2));
  1063. ES := InternalStrToInt(C+12, 2));
  1064. if (EY = 0) or (EM = 0) or (ED = 0) then
  1065. Result := 0
  1066. else
  1067. Result := EncodeDate(EY, EM, ED);
  1068. Result := ComposeDateTime(Result, EncodeTime(EH, EN, ES, 0));
  1069. end;
  1070. {$ENDIF}
  1071. function TConnectionName.MySQLWriteData(AField: PMYSQL_FIELD; FieldDef: TFieldDef; Source, Dest: PChar; Len: integer; out CreateBlob : boolean): Boolean;
  1072. var
  1073. VI: Integer;
  1074. VL: LargeInt;
  1075. VS: Smallint;
  1076. VW: Word;
  1077. VO: LongWord;
  1078. VF: Double;
  1079. VC: Currency;
  1080. VD: TDateTime;
  1081. VB: TBCD;
  1082. begin
  1083. Result := False;
  1084. CreateBlob := False;
  1085. if Source = Nil then // If the pointer is NULL, the field is NULL
  1086. exit;
  1087. case FieldDef.DataType of
  1088. ftSmallint:
  1089. begin
  1090. VS := InternalStrToInt(Source, Len);
  1091. Move(VS, Dest^, SizeOf(Smallint));
  1092. end;
  1093. ftWord:
  1094. begin
  1095. VW := InternalStrToInt(Source, Len);
  1096. Move(VW, Dest^, SizeOf(Word));
  1097. end;
  1098. ftInteger, ftAutoInc:
  1099. begin
  1100. VI := InternalStrToInt(Source, Len);
  1101. Move(VI, Dest^, SizeOf(Integer));
  1102. end;
  1103. ftLargeInt:
  1104. begin
  1105. {$IFDEF MYSQL50_UP}
  1106. if AField^.ftype = FIELD_TYPE_BIT then
  1107. begin
  1108. VL := 0;
  1109. for VI := 0 to Len-1 do
  1110. VL := VL * 256 + PByte(Source+VI)^;
  1111. end
  1112. else
  1113. {$ENDIF}
  1114. VL := InternalStrToInt64(Source, Len);
  1115. Move(VL, Dest^, SizeOf(LargeInt));
  1116. end;
  1117. ftLongWord:
  1118. begin
  1119. VO := InternalStrToDWord(Source, Len);
  1120. Move(VO, Dest^, SizeOf(LongWord));
  1121. end;
  1122. ftFloat:
  1123. begin
  1124. VF := InternalStrToFloat(Source, Len);
  1125. Move(VF, Dest^, SizeOf(Double));
  1126. end;
  1127. ftBCD:
  1128. begin
  1129. VC := InternalStrToCurrency(Source, Len);
  1130. Move(VC, Dest^, SizeOf(Currency));
  1131. end;
  1132. ftFmtBCD:
  1133. begin
  1134. VB := InternalStrToBCD(Source, Len);
  1135. Move(VB, Dest^, SizeOf(TBCD));
  1136. end;
  1137. ftDate:
  1138. begin
  1139. VD := InternalStrToDate(Source, Len);
  1140. Move(VD, Dest^, SizeOf(TDateTime));
  1141. end;
  1142. ftTime:
  1143. begin
  1144. VD := InternalStrToTime(Source, Len);
  1145. Move(VD, Dest^, SizeOf(TDateTime));
  1146. end;
  1147. ftDateTime:
  1148. begin
  1149. {$IFDEF mysql40}
  1150. if AField^.ftype = FIELD_TYPE_TIMESTAMP then
  1151. VD := InternalStrToTimeStamp(Source, Len)
  1152. else
  1153. {$ENDIF}
  1154. VD := InternalStrToDateTime(Source, Len);
  1155. Move(VD, Dest^, SizeOf(TDateTime));
  1156. end;
  1157. ftString, ftFixedChar:
  1158. // String-fields which can contain more then dsMaxStringSize characters
  1159. // are mapped to ftBlob fields, while their mysql-datatype is FIELD_TYPE_BLOB
  1160. begin
  1161. if Len > FieldDef.Size*FieldDef.CharSize then Len := FieldDef.Size*FieldDef.CharSize;
  1162. Move(Source^, Dest^, Len);
  1163. (Dest+Len)^ := #0;
  1164. end;
  1165. ftVarBytes:
  1166. begin
  1167. if Len > FieldDef.Size then Len := FieldDef.Size;
  1168. PWord(Dest)^ := Len;
  1169. Move(Source^, (Dest+sizeof(Word))^, Len);
  1170. end;
  1171. ftBytes:
  1172. begin
  1173. if Len > FieldDef.Size then Len := FieldDef.Size;
  1174. Move(Source^, Dest^, Len);
  1175. end;
  1176. ftBlob, ftMemo:
  1177. CreateBlob := True;
  1178. end;
  1179. Result := True;
  1180. end;
  1181. procedure TConnectionName.UpdateIndexDefs(IndexDefs : TIndexDefs;TableName : string);
  1182. var qry : TSQLQuery;
  1183. begin
  1184. if not assigned(Transaction) then
  1185. DatabaseError(SErrConnTransactionnSet);
  1186. qry := TSQLQuery.Create(nil);
  1187. qry.Transaction := Transaction;
  1188. qry.Database := Self;
  1189. try
  1190. with qry do
  1191. begin
  1192. ParseSQL := False;
  1193. SQL.Clear;
  1194. SQL.Add('show index from ' + TableName);
  1195. Open;
  1196. end;
  1197. while not qry.Eof do with IndexDefs.AddIndexDef do
  1198. begin
  1199. Name := trim(qry.FieldByName('Key_name').AsString);
  1200. Fields := trim(qry.FieldByName('Column_name').AsString);
  1201. If Name = 'PRIMARY' then Options := Options + [ixPrimary];
  1202. If qry.FieldByName('Non_unique').AsInteger = 0 then Options := Options + [ixUnique];
  1203. qry.Next;
  1204. while (Name = trim(qry.FieldByName('Key_name').AsString)) and (not qry.Eof) do
  1205. begin
  1206. Fields := Fields + ';' + trim(qry.FieldByName('Column_name').AsString);
  1207. qry.Next;
  1208. end;
  1209. end;
  1210. qry.Close;
  1211. finally
  1212. qry.Free;
  1213. end;
  1214. end;
  1215. function TConnectionName.RowsAffected(cursor: TSQLCursor): TRowsCount;
  1216. begin
  1217. if assigned(cursor) then
  1218. // Compile this without range-checking. RowsAffected can be -1, although
  1219. // it's an unsigned integer. (small joke from the mysql-guys)
  1220. // Without range-checking this goes ok. If Range is turned on, this results
  1221. // in range-check errors.
  1222. Result := (cursor as TCursorName).RowsAffected
  1223. else
  1224. Result := -1;
  1225. end;
  1226. function TConnectionName.RefreshLastInsertID(Query: TCustomSQLQuery; Field: TField): Boolean;
  1227. begin
  1228. Field.AsLargeInt:=GetInsertID;
  1229. Result := True;
  1230. end;
  1231. constructor TConnectionName.Create(AOwner: TComponent);
  1232. const SingleBackQoutes: TQuoteChars = ('`','`');
  1233. begin
  1234. inherited Create(AOwner);
  1235. FConnOptions := [sqEscapeRepeat, sqEscapeSlash, sqImplicitTransaction, sqLastInsertID];
  1236. FieldNameQuoteChars:=SingleBackQoutes;
  1237. FMySQL := Nil;
  1238. end;
  1239. {$IFNDEF MYSQL50_UP}
  1240. procedure TConnectionName.GetFieldNames(const TableName: string; List: TStrings);
  1241. begin
  1242. GetDBInfo(stColumns,TableName,'field',List);
  1243. end;
  1244. procedure TConnectionName.GetTableNames(List: TStrings; SystemTables: Boolean);
  1245. begin
  1246. GetDBInfo(stTables,'','tables_in_'+DatabaseName,List)
  1247. end;
  1248. {$ENDIF}
  1249. function TConnectionName.GetConnectionInfo(InfoType: TConnInfoType): string;
  1250. begin
  1251. Result:='';
  1252. try
  1253. InitialiseMysql;
  1254. case InfoType of
  1255. citServerType:
  1256. Result:='MySQL';
  1257. citServerVersion:
  1258. if Connected then
  1259. Result:=format('%6.6d', [mysql_get_server_version(FMySQL)]);
  1260. citServerVersionString:
  1261. if Connected then
  1262. Result:=mysql_get_server_info(FMySQL);
  1263. citClientVersion:
  1264. Result:=format('%6.6d', [mysql_get_client_version()]);
  1265. citClientName:
  1266. Result:=TMySQLConnectionDef.LoadedLibraryName;
  1267. else
  1268. Result:=inherited GetConnectionInfo(InfoType);
  1269. end;
  1270. finally
  1271. ReleaseMysql;
  1272. end;
  1273. end;
  1274. function TConnectionName.GetTransactionHandle(trans: TSQLHandle): pointer;
  1275. begin
  1276. Result:=Nil;
  1277. end;
  1278. function TConnectionName.Commit(trans: TSQLHandle): boolean;
  1279. begin
  1280. //mysql_commit(FMySQL);
  1281. Result := (mysql_query(FMySQL, 'COMMIT') = 0) or ForcedClose;
  1282. if not Result then
  1283. MySQLError(FMySQL, SErrExecuting, Self);
  1284. end;
  1285. function TConnectionName.RollBack(trans: TSQLHandle): boolean;
  1286. begin
  1287. //mysql_rollback(FMySQL);
  1288. Result := (mysql_query(FMySQL, 'ROLLBACK') = 0) or ForcedClose;
  1289. if not Result then
  1290. MySQLError(FMySQL, SErrExecuting, Self);
  1291. end;
  1292. function TConnectionName.StartdbTransaction(trans: TSQLHandle; AParams : string): boolean;
  1293. begin
  1294. Result := mysql_query(FMySQL, 'START TRANSACTION') = 0;
  1295. if not Result then
  1296. MySQLError(FMySQL, SErrExecuting, Self);
  1297. end;
  1298. procedure TConnectionName.CommitRetaining(trans: TSQLHandle);
  1299. begin
  1300. {$IFDEF MYSQL50_UP}
  1301. if mysql_query(FMySQL, 'COMMIT AND CHAIN') <> 0 then
  1302. MySQLError(FMySQL, SErrExecuting, Self);
  1303. {$ELSE}
  1304. if mysql_query(FMySQL, 'COMMIT') <> 0 then
  1305. MySQLError(FMySQL, SErrExecuting, Self);
  1306. if mysql_query(FMySQL, 'START TRANSACTION') <> 0 then
  1307. MySQLError(FMySQL, SErrExecuting, Self);
  1308. {$ENDIF}
  1309. end;
  1310. procedure TConnectionName.RollBackRetaining(trans: TSQLHandle);
  1311. begin
  1312. {$IFDEF MYSQL50_UP}
  1313. if mysql_query(FMySQL, 'ROLLBACK AND CHAIN') <> 0 then
  1314. MySQLError(FMySQL, SErrExecuting, Self);
  1315. {$ELSE}
  1316. if mysql_query(FMySQL, 'ROLLBACK') <> 0 then
  1317. MySQLError(FMySQL, SErrExecuting, Self);
  1318. if mysql_query(FMySQL, 'START TRANSACTION') <> 0 then
  1319. MySQLError(FMySQL, SErrExecuting, Self);
  1320. {$ENDIF}
  1321. end;
  1322. function TConnectionName.GetSchemaInfoSQL(SchemaType: TSchemaType;
  1323. SchemaObjectName, SchemaPattern: string): string;
  1324. begin
  1325. case SchemaType of
  1326. {$IFDEF MYSQL50_UP}
  1327. stTables : result := 'SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=SCHEMA() AND TABLE_TYPE IN (''BASE TABLE'',''VIEW'')';
  1328. stColumns : result := 'SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=SCHEMA() AND TABLE_NAME='+QuotedStr(SchemaObjectName);
  1329. {$ELSE}
  1330. stTables : result := 'show tables';
  1331. stColumns : result := 'show columns from ' + EscapeString(SchemaObjectName);
  1332. {$ENDIF}
  1333. else
  1334. result := inherited;
  1335. end; {case}
  1336. end;
  1337. { TMySQLConnectionDef }
  1338. class function TMySQLConnectionDef.TypeName: String;
  1339. begin
  1340. Result:='MySQL '+MySQLVersion;
  1341. end;
  1342. class function TMySQLConnectionDef.ConnectionClass: TSQLConnectionClass;
  1343. begin
  1344. {$IFDEF mysql80}
  1345. Result:=TMySQL80Connection;
  1346. {$ELSE}
  1347. {$IFDEF mysql57}
  1348. Result:=TMySQL57Connection;
  1349. {$ELSE}
  1350. {$IFDEF mysql56}
  1351. Result:=TMySQL56Connection;
  1352. {$ELSE}
  1353. {$IfDef mysql55}
  1354. Result:=TMySQL55Connection;
  1355. {$ELSE}
  1356. {$IfDef mysql51}
  1357. Result:=TMySQL51Connection;
  1358. {$ELSE}
  1359. {$IfDef mysql50}
  1360. Result:=TMySQL50Connection;
  1361. {$ELSE}
  1362. {$IfDef mysql41}
  1363. Result:=TMySQL41Connection;
  1364. {$ELSE}
  1365. Result:=TMySQL40Connection;
  1366. {$EndIf}
  1367. {$EndIf}
  1368. {$endif}
  1369. {$endif}
  1370. {$ENDIF}
  1371. {$ENDIF}
  1372. {$ENDIF}
  1373. end;
  1374. class function TMySQLConnectionDef.Description: String;
  1375. begin
  1376. Result:='Connect to a MySQL '+MySQLVersion+' database directly via the client library';
  1377. end;
  1378. class function TMySQLConnectionDef.DefaultLibraryName: String;
  1379. begin
  1380. Result:=mysqlvlib;
  1381. end;
  1382. class function TMySQLConnectionDef.LoadFunction: TLibraryLoadFunction;
  1383. begin
  1384. Result:=@InitialiseMySQL;
  1385. end;
  1386. class function TMySQLConnectionDef.UnLoadFunction: TLibraryUnLoadFunction;
  1387. begin
  1388. Result:=@ReleaseMySQL;
  1389. end;
  1390. class function TMySQLConnectionDef.LoadedLibraryName: string;
  1391. begin
  1392. Result:=MysqlLoadedLibrary;
  1393. end;
  1394. {$IFDEF mysql80}
  1395. initialization
  1396. RegisterConnection(TMySQL80ConnectionDef);
  1397. finalization
  1398. UnRegisterConnection(TMySQL80ConnectionDef);
  1399. {$ELSE}
  1400. {$IFDEF mysql57}
  1401. initialization
  1402. RegisterConnection(TMySQL57ConnectionDef);
  1403. finalization
  1404. UnRegisterConnection(TMySQL57ConnectionDef);
  1405. {$ELSE}
  1406. {$IFDEF mysql56}
  1407. initialization
  1408. RegisterConnection(TMySQL56ConnectionDef);
  1409. finalization
  1410. UnRegisterConnection(TMySQL56ConnectionDef);
  1411. {$ELSE}
  1412. {$IfDef mysql55}
  1413. initialization
  1414. RegisterConnection(TMySQL55ConnectionDef);
  1415. finalization
  1416. UnRegisterConnection(TMySQL55ConnectionDef);
  1417. {$else}
  1418. {$IfDef mysql51}
  1419. initialization
  1420. RegisterConnection(TMySQL51ConnectionDef);
  1421. finalization
  1422. UnRegisterConnection(TMySQL51ConnectionDef);
  1423. {$ELSE}
  1424. {$IfDef mysql50}
  1425. initialization
  1426. RegisterConnection(TMySQL50ConnectionDef);
  1427. finalization
  1428. UnRegisterConnection(TMySQL50ConnectionDef);
  1429. {$ELSE}
  1430. {$IfDef mysql41}
  1431. initialization
  1432. RegisterConnection(TMySQL41ConnectionDef);
  1433. finalization
  1434. UnRegisterConnection(TMySQL41ConnectionDef);
  1435. {$ELSE}
  1436. initialization
  1437. RegisterConnection(TMySQL40ConnectionDef);
  1438. finalization
  1439. UnRegisterConnection(TMySQL40ConnectionDef);
  1440. {$EndIf}
  1441. {$EndIf}
  1442. {$ENDIF}
  1443. {$ENDIF}
  1444. {$ENDIF}
  1445. {$ENDIF}
  1446. {$ENDIF}
  1447. end.