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.

1005 lines
37 KiB

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