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.

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