You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

894 lines
33 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. #if MYSQL_6_9 || MYSQL_6_10
  2. /* 2021.11.07 */
  3. using Externals.MySql.Data.MySqlClient;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Data;
  7. using System.Net;
  8. using System.Text;
  9. using System.Transactions;
  10. using static Apewer.Source.OrmHelper;
  11. namespace Apewer.Source
  12. {
  13. /// <summary></summary>
  14. public class MySql : IDbClient
  15. {
  16. #region 基础
  17. private Timeout _timeout = null;
  18. private string _connectionstring = null;
  19. /// <summary>获取或设置日志记录。</summary>
  20. public Logger Logger { get; set; }
  21. /// <summary>超时设定。</summary>
  22. public Timeout Timeout { get => _timeout; }
  23. /// <summary>创建实例。</summary>
  24. public MySql(string connnectionString, Timeout timeout = default)
  25. {
  26. _connectionstring = connnectionString;
  27. _timeout = timeout ?? Timeout.Default;
  28. }
  29. /// <summary>获取当前的 MySqlConnection 对象。</summary>
  30. public IDbConnection Connection { get => _connection; }
  31. /// <summary>构建连接字符串以创建实例。</summary>
  32. public MySql(string address, string store, string user, string pass, Timeout timeout = null)
  33. {
  34. _timeout = timeout ?? Timeout.Default;
  35. var a = address ?? "";
  36. var s = store ?? "";
  37. var u = user ?? "";
  38. var p = pass ?? "";
  39. var cs = $"server={a}; database={s}; uid={u}; pwd={p}; ";
  40. _connectionstring = cs;
  41. _storename = new Class<string>(s);
  42. }
  43. #endregion
  44. #region 日志。
  45. private void LogError(string action, Exception ex, string addtion)
  46. {
  47. var logger = Logger;
  48. if (logger != null) logger.Error(this, "MySQL", action, ex.GetType().FullName, ex.Message, addtion);
  49. }
  50. #endregion
  51. #region Connection
  52. private MySqlConnection _connection = null;
  53. /// <summary></summary>
  54. public bool Online { get => _connection == null ? false : (_connection.State == ConnectionState.Open); }
  55. /// <summary>连接字符串。</summary>
  56. public string ConnectionString { get => _connectionstring; }
  57. /// <summary></summary>
  58. public string Connect()
  59. {
  60. if (_connection == null)
  61. {
  62. _connection = new MySqlConnection();
  63. _connection.ConnectionString = _connectionstring;
  64. }
  65. else
  66. {
  67. if (_connection.State == ConnectionState.Open) return null;
  68. }
  69. // try
  70. {
  71. _connection.Open();
  72. switch (_connection.State)
  73. {
  74. case ConnectionState.Open: return null;
  75. default: return $"连接失败,当前处于 {_connection.State} 状态。";
  76. }
  77. }
  78. // catch (Exception ex)
  79. // {
  80. // LogError("Connection", ex, _connection.ConnectionString);
  81. // Close();
  82. // return false;
  83. // }
  84. }
  85. /// <summary></summary>
  86. public void Close()
  87. {
  88. if (_connection != null)
  89. {
  90. if (_transaction != null)
  91. {
  92. if (_autocommit) Commit();
  93. else Rollback();
  94. }
  95. _connection.Close();
  96. _connection.Dispose();
  97. _connection = null;
  98. }
  99. }
  100. /// <summary></summary>
  101. public void Dispose() { Close(); }
  102. #endregion
  103. #region Transaction
  104. private IDbTransaction _transaction = null;
  105. private bool _autocommit = false;
  106. /// <summary>启动事务。</summary>
  107. public string Begin(bool commit = true) => Begin(commit, null);
  108. /// <summary>启动事务。</summary>
  109. public string Begin(bool commit, Class<System.Data.IsolationLevel> isolation)
  110. {
  111. if (Connect() != null) return "未连接。";
  112. if (_transaction != null) return "存在已启动的事务,无法再次启动。";
  113. try
  114. {
  115. _transaction = isolation ? _connection.BeginTransaction(isolation.Value) : _connection.BeginTransaction();
  116. _autocommit = commit;
  117. return null;
  118. }
  119. catch (Exception ex)
  120. {
  121. Logger.Error(nameof(MySql), "Commit", ex.Message());
  122. return ex.Message();
  123. }
  124. }
  125. /// <summary>提交事务。</summary>
  126. public string Commit()
  127. {
  128. if (_transaction == null) return "事务不存在。";
  129. try
  130. {
  131. _transaction.Commit();
  132. RuntimeUtility.Dispose(_transaction);
  133. _transaction = null;
  134. return null;
  135. }
  136. catch (Exception ex)
  137. {
  138. RuntimeUtility.Dispose(_transaction);
  139. _transaction = null;
  140. Logger.Error(nameof(MySql), "Commit", ex.Message());
  141. return ex.Message();
  142. }
  143. }
  144. /// <summary>从挂起状态回滚事务。</summary>
  145. public string Rollback()
  146. {
  147. if (_transaction == null) return "事务不存在。";
  148. try
  149. {
  150. _transaction.Rollback();
  151. RuntimeUtility.Dispose(_transaction);
  152. _transaction = null;
  153. return null;
  154. }
  155. catch (Exception ex)
  156. {
  157. RuntimeUtility.Dispose(_transaction);
  158. _transaction = null;
  159. Logger.Error(nameof(MySql), "Rollback", ex.Message());
  160. return ex.Message();
  161. }
  162. }
  163. #endregion
  164. #region SQL
  165. /// <summary></summary>
  166. public IQuery Query(string sql, IEnumerable<IDataParameter> parameters)
  167. {
  168. if (sql.IsBlank()) return Example.InvalidQueryStatement;
  169. var connected = Connect();
  170. if (connected.NotEmpty()) return Example.InvalidQueryConnection;
  171. try
  172. {
  173. using (var command = new MySqlCommand())
  174. {
  175. command.Connection = _connection;
  176. command.CommandTimeout = _timeout.Query;
  177. command.CommandText = sql;
  178. if (parameters != null)
  179. {
  180. foreach (var p in parameters)
  181. {
  182. if (p != null) command.Parameters.Add(p);
  183. }
  184. }
  185. using (var ds = new DataSet())
  186. {
  187. using (var da = new MySqlDataAdapter(sql, _connection))
  188. {
  189. var name = "table_" + Guid.NewGuid().ToString("n");
  190. da.Fill(ds, name);
  191. var table = ds.Tables[name];
  192. return new Query(table);
  193. }
  194. }
  195. }
  196. }
  197. catch (Exception exception)
  198. {
  199. Logger.Error(nameof(MySql), "Query", exception, sql);
  200. return new Query(exception);
  201. }
  202. }
  203. /// <summary></summary>
  204. public IExecute Execute(string sql, IEnumerable<IDataParameter> parameters)
  205. {
  206. if (sql.IsBlank()) return Example.InvalidExecuteStatement;
  207. var connected = Connect();
  208. if (connected.NotEmpty()) return new Execute(false, connected);
  209. var inTransaction = _transaction != null;
  210. if (!inTransaction) Begin();
  211. try
  212. {
  213. using (var command = new MySqlCommand())
  214. {
  215. command.Connection = _connection;
  216. command.Transaction = (MySqlTransaction)_transaction;
  217. command.CommandTimeout = _timeout.Execute;
  218. command.CommandText = sql;
  219. if (parameters != null)
  220. {
  221. foreach (var parameter in parameters)
  222. {
  223. if (parameter == null) continue;
  224. command.Parameters.Add(parameter);
  225. }
  226. }
  227. var rows = command.ExecuteNonQuery();
  228. if (!inTransaction) Commit(); // todo 此处应该检查事务提交产生的错误。
  229. return new Execute(true, rows);
  230. }
  231. }
  232. catch (Exception exception)
  233. {
  234. Logger.Error(nameof(MySql), "Execute", exception, sql);
  235. if (!inTransaction) Rollback();
  236. return new Execute(exception);
  237. }
  238. }
  239. /// <summary></summary>
  240. public IQuery Query(string sql) => Query(sql, null);
  241. /// <summary></summary>
  242. public IExecute Execute(string sql, IEnumerable<Parameter> parameters)
  243. {
  244. var dps = null as List<IDataParameter>;
  245. if (parameters != null)
  246. {
  247. var count = parameters.Count();
  248. dps = new List<IDataParameter>(count);
  249. foreach (var p in parameters)
  250. {
  251. var dp = Parameter(p);
  252. dps.Add(dp);
  253. }
  254. }
  255. return Execute(sql, dps);
  256. }
  257. /// <summary></summary>
  258. public IExecute Execute(string sql) => Execute(sql, null as IEnumerable<IDataParameter>);
  259. #endregion
  260. #region ORM
  261. private Class<string> _storename = null;
  262. private string StoreName()
  263. {
  264. if (_storename) return _storename.Value;
  265. _storename = new Class<string>(Internals.TextHelper.ParseConnectionString(_connectionstring).GetValue("database"));
  266. return _storename.Value ?? "";
  267. }
  268. private string[] FirstColumn(string sql)
  269. {
  270. using (var query = Query(sql) as Query) return query.ReadColumn();
  271. }
  272. /// <summary></summary>
  273. public string[] TableNames()
  274. {
  275. var sql = TextUtility.Merge("select table_name from information_schema.tables where table_schema='", StoreName(), "' and table_type='base table';");
  276. return FirstColumn(sql);
  277. }
  278. /// <summary></summary>
  279. public string[] ViewNames()
  280. {
  281. var sql = TextUtility.Merge("select table_name from information_schema.tables where table_schema='", StoreName(), "' and table_type='view';");
  282. return FirstColumn(sql);
  283. }
  284. /// <summary></summary>
  285. public string[] ColumnNames(string table)
  286. {
  287. var sql = TextUtility.Merge("select column_name from information_schema.columns where table_schema='", StoreName(), "' and table_name='", TextUtility.AntiInject(table), "';");
  288. return FirstColumn(sql);
  289. }
  290. /// <summary>获取用于创建表的语句。</summary>
  291. private string GetCreateStetement(TableStructure structure)
  292. {
  293. // 检查现存表。
  294. var exists = false;
  295. var tables = TableNames();
  296. if (tables.Length > 0)
  297. {
  298. var lower = structure.Name.ToLower();
  299. foreach (var table in tables)
  300. {
  301. if (TextUtility.IsBlank(table)) continue;
  302. if (table.ToLower() == lower)
  303. {
  304. exists = true;
  305. break;
  306. }
  307. }
  308. }
  309. if (exists)
  310. {
  311. var columns = ColumnNames(structure.Name);
  312. if (columns.Length > 0)
  313. {
  314. var lower = new List<string>(columns.Length);
  315. var added = 0;
  316. foreach (var column in columns)
  317. {
  318. if (TextUtility.IsBlank(column)) continue;
  319. lower.Add(column.ToLower());
  320. added++;
  321. }
  322. lower.Capacity = added;
  323. columns = lower.ToArray();
  324. }
  325. var sqlsb = new StringBuilder();
  326. foreach (var column in structure.Columns)
  327. {
  328. // 检查 Independent 特性。
  329. if (structure.Independent && column.Independent) continue;
  330. // 去重。
  331. var lower = column.Field.ToLower();
  332. if (columns.Contains(lower)) continue;
  333. var type = Declaration(column);
  334. if (type.IsEmpty()) return TextUtility.Merge("类型 ", column.Type.ToString(), " 不受支持。");
  335. // alter table `_record` add column `_index` bigint;
  336. sqlsb.Append("alter table `", structure.Name, "` add column ", type, "; ");
  337. }
  338. var sql = sqlsb.ToString();
  339. return sql;
  340. }
  341. else
  342. {
  343. // create table _record (`_index` bigint, `_key` varchar(255), `_text` longtext) engine=innodb default charset=utf8mb4
  344. var columns = new List<string>(structure.Columns.Length);
  345. var columnsAdded = 0;
  346. var primarykey = null as string;
  347. foreach (var column in structure.Columns)
  348. {
  349. // 检查 Independent 特性。
  350. if (structure.Independent && column.Independent) continue;
  351. // 字段。
  352. var type = Declaration(column);
  353. if (type.IsEmpty()) return TextUtility.Merge("类型 ", column.Type.ToString(), " 不受支持。");
  354. columns.Add(type);
  355. columnsAdded++;
  356. // 主键。
  357. if (column.Property.Name == "Key") primarykey = column.Field;
  358. }
  359. columns.Capacity = columnsAdded;
  360. var table = structure.Name;
  361. var joined = string.Join(", ", columns);
  362. // 设置主键。
  363. string sql;
  364. if (!structure.Independent && !string.IsNullOrEmpty(primarykey))
  365. {
  366. sql = TextUtility.Merge("create table `", table, "`(", joined, ", primary key (", primarykey, ") ) engine=innodb default charset=utf8mb4; ");
  367. }
  368. else
  369. {
  370. sql = TextUtility.Merge("create table `", table, "`(", joined, ") engine=innodb default charset=utf8mb4; ");
  371. }
  372. return sql;
  373. }
  374. }
  375. /// <summary></summary>
  376. private string Initialize(Type model, out string sql)
  377. {
  378. if (model == null)
  379. {
  380. sql = null;
  381. return "指定的类型无效。";
  382. }
  383. var structure = TableStructure.Parse(model);
  384. if (structure == null)
  385. {
  386. sql = null;
  387. return "无法解析记录模型。";
  388. }
  389. // 连接数据库。
  390. var connect = Connect();
  391. if (connect.NotEmpty())
  392. {
  393. sql = null;
  394. return $"连接数据库失败。({connect})";
  395. }
  396. sql = GetCreateStetement(structure);
  397. if (sql.NotEmpty())
  398. {
  399. var execute = Execute(sql);
  400. if (!execute.Success) return execute.Message;
  401. }
  402. return null;
  403. }
  404. /// <summary></summary>
  405. public string Initialize(Type model) => Initialize(model, out string sql);
  406. /// <summary></summary>
  407. public string Initialize<T>() where T : class, new() => Initialize(typeof(T));
  408. /// <summary></summary>
  409. public string Initialize(Record model) => (model == null) ? "参数无效。" : Initialize(model.GetType());
  410. /// <summary>插入记录。返回错误信息。</summary>
  411. public string Insert(object record, string table = null)
  412. {
  413. if (record == null) return "参数无效。";
  414. OrmHelper.FixProperties(record);
  415. var structure = TableStructure.Parse(record.GetType());
  416. if (structure == null) return "无法解析记录模型。";
  417. if (string.IsNullOrEmpty(table)) table = structure.Name;
  418. if (string.IsNullOrEmpty(table)) return "表名称无效。";
  419. var ps = structure.CreateParameters(record, Parameter);
  420. var psc = ps.Length;
  421. if (psc < 1) return "数据模型不包含字段。";
  422. var names = new List<string>(psc);
  423. var values = new List<string>(psc);
  424. foreach (var p in ps)
  425. {
  426. var pn = p.ParameterName;
  427. names.Add("`" + p + "`");
  428. values.Add("@" + p);
  429. }
  430. var ns = string.Join(", ", names);
  431. var vs = string.Join(", ", values);
  432. var sql = $"insert into `{table}` ({ns}) values ({vs}); ";
  433. var execute = Execute(sql, ps);
  434. if (execute.Success) return TextUtility.Empty;
  435. return execute.Message;
  436. }
  437. /// <summary>更新记录,实体中的 Key 属性不被更新。返回错误信息。</summary>
  438. /// <remarks>无法更新带有 Independent 特性的模型(缺少 Key 属性)。</remarks>
  439. public string Update(IRecord record, string table = null)
  440. {
  441. if (record == null) return "参数无效。";
  442. FixProperties(record);
  443. SetUpdated(record);
  444. var structure = TableStructure.Parse(record.GetType());
  445. if (structure == null) return "无法解析记录模型。";
  446. if (structure.Independent) return "无法更新带有 Independent 特性的模型。";
  447. if (string.IsNullOrEmpty(table)) table = structure.Name;
  448. if (string.IsNullOrEmpty(table)) return "表名称无效。";
  449. var ps = structure.CreateParameters(record, Parameter, "_key");
  450. var psc = ps.Length;
  451. if (psc < 1) return "数据模型不包含字段。";
  452. var items = new List<string>(psc);
  453. foreach (var p in ps)
  454. {
  455. var pn = p.ParameterName;
  456. items.Add(TextUtility.Merge("`", pn, "` = @", pn));
  457. }
  458. var key = record.Key.SafeKey();
  459. var sql = $"update `{table}` set {string.Join(", ", items)} where `_key`='{key}'; ";
  460. var execute = Execute(sql, ps);
  461. if (execute.Success) return TextUtility.Empty;
  462. return execute.Message;
  463. }
  464. /// <summary></summary>
  465. public Result<object[]> Query(Type model, string sql) => OrmHelper.Query(this, model, sql);
  466. /// <summary></summary>
  467. public Result<T[]> Query<T>(string sql) where T : class, new()
  468. {
  469. var query = Query(sql);
  470. if (!query.Success) return new Result<T[]>(query.Message);
  471. var records = query.Fill<T>();
  472. query.Dispose();
  473. var result = new Result<T[]>(records);
  474. return result;
  475. }
  476. /// <summary>获取所有记录。Flag 为 0 时将忽略 Flag 条件。</summary>
  477. public Result<object[]> Query(Type model, long flag = 0) => OrmHelper.Query(this, model, (tn) =>
  478. {
  479. if (flag == 0) return $"select * from `{tn}`; ";
  480. return $"select * from `{tn}` where `_flag`={flag}; ";
  481. });
  482. /// <summary>获取所有记录。Flag 为 0 时将忽略 Flag 条件。</summary>
  483. public Result<T[]> Query<T>(long flag = 0) where T : class, IRecord, new() => OrmHelper.Query<T>(this, (tn) =>
  484. {
  485. if (flag == 0) return $"select * from `{tn}`; ";
  486. return $"select * from `{tn}` where `_flag`={flag}; ";
  487. });
  488. /// <summary>获取记录。</summary>
  489. /// <param name="model">填充的记录模型。</param>
  490. /// <param name="skip">要跳过的记录数,可用最小值为 0。</param>
  491. /// <param name="count">要获取的记录数,可用最小值为 1。</param>
  492. public Result<object[]> Query(Type model, int skip, int count)
  493. {
  494. if (skip < 0) return new Result<object[]>("参数 skip 超出了范围。");
  495. if (count < 1) return new Result<object[]>("参数 count 超出了范围。");
  496. return OrmHelper.Query(this, model, (tn) => $"select * from `{tn}` limit {skip}, {count}; ");
  497. }
  498. /// <summary>获取记录。</summary>
  499. /// <param name="skip">要跳过的记录数,可用最小值为 0。</param>
  500. /// <param name="count">要获取的记录数,可用最小值为 1。</param>
  501. public Result<T[]> Query<T>(int skip, int count) where T : class, IRecord, new()
  502. {
  503. if (skip < 0) return new Result<T[]>("参数 skip 超出了范围。");
  504. if (count < 1) return new Result<T[]>("参数 count 超出了范围。");
  505. return OrmHelper.Query<T>(this, (tn) => $"select * from `{tn}` limit {skip}, {count}; ");
  506. }
  507. /// <summary>获取记录。</summary>
  508. public Result<object> Get(Type model, string key, long flag = 0) => OrmHelper.Get(this, model, key, (tn, sk) =>
  509. {
  510. if (flag == 0) return $"select * from `{tn}` where `_key`='{sk}' limit 1;";
  511. return $"select * from `{tn}` where `_key`='{sk}' and `_flag`={flag} limit 1;";
  512. });
  513. /// <summary>获取记录。</summary>
  514. public Result<T> Get<T>(string key, long flag = 0) where T : class, IRecord, new() => OrmHelper.Get<T>(this, key, (tn, sk) =>
  515. {
  516. if (flag == 0) return $"select * from `{tn}` where `_key`='{sk}' limit 1;";
  517. return $"select * from `{tn}` where `_key`='{sk}' and `_flag`={flag} limit 1;";
  518. });
  519. /// <summary>>获取指定类型的主键,按 Flag 属性筛选。</summary>
  520. public Result<string[]> Keys(Type model, long flag = 0) => OrmHelper.Keys(this, model, (tn) =>
  521. {
  522. if (flag == 0) return $"select `_key` from `{tn}`;";
  523. return $"select `_key` from `{tn}` where `_flag`={flag};";
  524. });
  525. /// <summary>>获取指定类型的主键,按 Flag 属性筛选。</summary>
  526. public Result<string[]> Keys<T>(long flag = 0) where T : class, IRecord, new() => Keys(typeof(T), flag);
  527. /// <summary>对表添加列,返回错误信息。</summary>
  528. /// <typeparam name="T">记录类型。</typeparam>
  529. /// <param name="column">列名称。</param>
  530. /// <param name="type">字段类型。</param>
  531. /// <param name="length">字段长度,仅对 VarChar 和 NVarChar 类型有效。</param>
  532. /// <returns></returns>
  533. public string AddColumn<T>(string column, ColumnType type, int length = 0) where T : class, IRecord
  534. {
  535. var columnName = SafeColumn(column);
  536. if (columnName.IsEmpty()) return "列名无效。";
  537. var ta = TableAttribute.Parse(typeof(T));
  538. if (ta == null) return "无法解析记录模型。";
  539. var tableName = ta.Name;
  540. var columeType = "";
  541. switch (type)
  542. {
  543. case ColumnType.Integer:
  544. columeType = "bigint";
  545. break;
  546. case ColumnType.Float:
  547. columeType = "double";
  548. break;
  549. case ColumnType.Bytes:
  550. columeType = "longblob";
  551. break;
  552. case ColumnType.DateTime:
  553. columeType = "datetime";
  554. break;
  555. case ColumnType.VarChar:
  556. case ColumnType.NVarChar:
  557. columeType = $"varchar({length})";
  558. break;
  559. case ColumnType.VarChar191:
  560. case ColumnType.NVarChar191:
  561. columeType = "varchar(191)";
  562. break;
  563. case ColumnType.VarCharMax:
  564. case ColumnType.NVarCharMax:
  565. columeType = "varchar(max)";
  566. break;
  567. case ColumnType.Text:
  568. columeType = "longtext";
  569. break;
  570. }
  571. if (columeType.IsEmpty()) return "类型不支持。";
  572. var sql = $"alter table `{tableName}` add {columnName} {columeType}; ";
  573. var execute = Execute(sql) as Execute;
  574. var error = execute.Message;
  575. return error;
  576. }
  577. #endregion
  578. #region static
  579. /// <summary>对文本转义,符合 SQL 安全性。可根据字段类型限制 UTF-8 字节数,默认为 0 时不限制字节数。</summary>
  580. public static string Escape(string text, int bytes = 0)
  581. {
  582. if (text.IsEmpty()) return "";
  583. var t = text ?? "";
  584. t = t.Replace("\\", "\\\\");
  585. t = t.Replace("'", "\\'");
  586. t = t.Replace("\n", "\\n");
  587. t = t.Replace("\r", "\\r");
  588. t = t.Replace("\b", "\\b");
  589. t = t.Replace("\t", "\\t");
  590. t = t.Replace("\f", "\\f");
  591. if (bytes > 5)
  592. {
  593. if (t.Bytes(Encoding.UTF8).Length > bytes)
  594. {
  595. while (true)
  596. {
  597. t = t.Substring(0, t.Length - 1);
  598. if (t.Bytes(Encoding.UTF8).Length <= (bytes - 4)) break;
  599. }
  600. t = t + " ...";
  601. }
  602. }
  603. return t;
  604. }
  605. /// <summary></summary>
  606. public static string SafeTable(string table)
  607. {
  608. const string chars = "0123456789_-ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
  609. var safety = TextUtility.Restrict(table, chars).Lower();
  610. var pc = 0;
  611. for (var i = 0; i < safety.Length; i++)
  612. {
  613. if (safety[i] == '-') pc += 1;
  614. else break;
  615. }
  616. if (pc == safety.Length) return "";
  617. if (pc > 0) safety = safety.Substring(pc);
  618. return safety;
  619. }
  620. /// <summary></summary>
  621. public static string SafeColumn(string column) => SafeTable(column);
  622. /// <summary></summary>
  623. /// <exception cref="ArgumentNullException"></exception>
  624. /// <exception cref="InvalidOperationException"></exception>
  625. internal static MySqlParameter Parameter(Parameter parameter)
  626. {
  627. if (parameter == null) throw new InvalidOperationException("参数无效。");
  628. return Parameter(parameter.Name, parameter.Type, parameter.Size, parameter.Value);
  629. }
  630. /// <summary></summary>
  631. internal static MySqlParameter Parameter(string name, ColumnType type, Int32 size, object value)
  632. {
  633. if (TextUtility.IsBlank(name)) return null;
  634. var dbtype = MySqlDbType.Int64;
  635. switch (type)
  636. {
  637. case ColumnType.Bytes:
  638. dbtype = MySqlDbType.LongBlob;
  639. break;
  640. case ColumnType.Integer:
  641. dbtype = MySqlDbType.Int64;
  642. break;
  643. case ColumnType.Float:
  644. dbtype = MySqlDbType.Double;
  645. break;
  646. case ColumnType.DateTime:
  647. dbtype = MySqlDbType.DateTime;
  648. break;
  649. case ColumnType.VarChar:
  650. case ColumnType.VarChar191:
  651. case ColumnType.VarCharMax:
  652. case ColumnType.NVarChar:
  653. case ColumnType.NVarChar191:
  654. case ColumnType.NVarCharMax:
  655. dbtype = MySqlDbType.VarChar;
  656. break;
  657. case ColumnType.Text:
  658. case ColumnType.NText:
  659. dbtype = MySqlDbType.LongText;
  660. break;
  661. default:
  662. throw new InvalidOperationException(TextUtility.Merge("类型 ", type.ToString(), " 不受支持。"));
  663. }
  664. switch (type)
  665. {
  666. case ColumnType.VarChar:
  667. case ColumnType.NVarChar:
  668. size = NumberUtility.Restrict(size, 0, 65535);
  669. break;
  670. case ColumnType.VarChar191:
  671. case ColumnType.NVarChar191:
  672. size = NumberUtility.Restrict(size, 0, 191);
  673. break;
  674. default:
  675. size = 0;
  676. break;
  677. }
  678. if (value is string && value != null && size > 0)
  679. {
  680. value = TextUtility.Left((string)value, size);
  681. }
  682. var parameter = new MySqlParameter();
  683. parameter.ParameterName = name;
  684. parameter.MySqlDbType = dbtype;
  685. parameter.Value = value;
  686. if (size > 0) parameter.Size = size;
  687. return parameter;
  688. }
  689. /// <summary></summary>
  690. internal static MySqlParameter Parameter(string name, MySqlDbType type, object value, Int32 size = 0)
  691. {
  692. var parameter = new MySqlParameter();
  693. parameter.ParameterName = name;
  694. parameter.MySqlDbType = type;
  695. parameter.Value = value;
  696. if (size > 0) parameter.Size = size;
  697. return parameter;
  698. }
  699. private static string Declaration(ColumnAttribute column)
  700. {
  701. var type = TextUtility.Empty;
  702. var length = Math.Max(0, (int)column.Length);
  703. switch (column.Type)
  704. {
  705. case ColumnType.Integer:
  706. type = "bigint";
  707. break;
  708. case ColumnType.Float:
  709. type = "double";
  710. break;
  711. case ColumnType.Bytes:
  712. type = "longblob";
  713. break;
  714. case ColumnType.DateTime:
  715. type = "datetime";
  716. break;
  717. case ColumnType.VarChar:
  718. type = TextUtility.Merge("varchar(", Math.Max(65535, length).ToString(), ")");
  719. break;
  720. case ColumnType.VarChar191:
  721. type = TextUtility.Merge("varchar(191)");
  722. break;
  723. case ColumnType.VarCharMax:
  724. type = TextUtility.Merge("varchar(max)");
  725. break;
  726. case ColumnType.Text:
  727. type = TextUtility.Merge("longtext");
  728. break;
  729. case ColumnType.NVarChar:
  730. type = TextUtility.Merge("varchar(", Math.Min(65535, length).ToString(), ")");
  731. break;
  732. case ColumnType.NVarChar191:
  733. type = TextUtility.Merge("varchar(191)");
  734. break;
  735. case ColumnType.NVarCharMax:
  736. type = TextUtility.Merge("varchar(65535)");
  737. break;
  738. case ColumnType.NText:
  739. type = TextUtility.Merge("longtext");
  740. break;
  741. default:
  742. return TextUtility.Empty;
  743. }
  744. return TextUtility.Merge("`", (string)column.Field, "` ", type);
  745. }
  746. /// <summary>获取每个数据库中,每个表的容量,单位为字节。</summary>
  747. public static Dictionary<string, Dictionary<string, long>> GetTablesCapacity(MySql source)
  748. {
  749. var result = new Dictionary<string, Dictionary<string, long>>();
  750. if (source != null && source.Connect().IsEmpty())
  751. {
  752. var sql = "select `table_schema`, `table_name`, `engine`, `data_length`, `index_length` from `information_schema`.tables order by `table_schema`, `table_name`";
  753. using (var query = (Query)source.Query(sql))
  754. {
  755. for (var r = 0; r < query.Rows; r++)
  756. {
  757. var store = query.Text(r, "table_schema");
  758. var table = query.Text(r, "table_name");
  759. var engine = query.Text(r, "engine");
  760. var capacity = NumberUtility.Int64(query.Text(r, "data_length")) + NumberUtility.Int64(query.Text(r, "index_length"));
  761. if (store == "mysql") continue;
  762. if (store == "information_schema") continue;
  763. if (store == "performance_schema") continue;
  764. if (engine != "MyISAM" && engine != "InnoDB") continue;
  765. if (!result.ContainsKey(store)) result.Add(store, new Dictionary<string, long>());
  766. if (!result[store].ContainsKey(table)) result[store].Add(table, 0L);
  767. result[store][table] = capacity;
  768. }
  769. }
  770. }
  771. return result;
  772. }
  773. #endregion
  774. }
  775. }
  776. #endif