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.

413 lines
11 KiB

4 years ago
  1. // Copyright ?2004, 2017 Oracle and/or its affiliates. All rights reserved.
  2. //
  3. // MySQL Connector/NET is licensed under the terms of the GPLv2
  4. // <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most
  5. // MySQL Connectors. There are special exceptions to the terms and
  6. // conditions of the GPLv2 as it is applied to this software, see the
  7. // FLOSS License Exception
  8. // <http://www.mysql.com/about/legal/licensing/foss-exception.html>.
  9. //
  10. // This program is free software; you can redistribute it and/or modify
  11. // it under the terms of the GNU General Public License as published
  12. // by the Free Software Foundation; version 2 of the License.
  13. //
  14. // This program is distributed in the hope that it will be useful, but
  15. // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  16. // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  17. // for more details.
  18. //
  19. // You should have received a copy of the GNU General Public License along
  20. // with this program; if not, write to the Free Software Foundation, Inc.,
  21. // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  22. #if MYSQL_6_9
  23. using System.Text;
  24. using Externals.MySql.Data.Common;
  25. using Externals.MySql.Data.Types;
  26. using System.Globalization;
  27. using System.Text.RegularExpressions;
  28. using System;
  29. using System.Collections;
  30. using System.Collections.Generic;
  31. namespace Externals.MySql.Data.MySqlClient
  32. {
  33. internal enum ColumnFlags : int
  34. {
  35. NOT_NULL = 1,
  36. PRIMARY_KEY = 2,
  37. UNIQUE_KEY = 4,
  38. MULTIPLE_KEY = 8,
  39. BLOB = 16,
  40. UNSIGNED = 32,
  41. ZERO_FILL = 64,
  42. BINARY = 128,
  43. ENUM = 256,
  44. AUTO_INCREMENT = 512,
  45. TIMESTAMP = 1024,
  46. SET = 2048,
  47. NUMBER = 32768
  48. };
  49. /// <summary>
  50. /// Summary description for Field.
  51. /// </summary>
  52. internal class MySqlField
  53. {
  54. #region Fields
  55. // public fields
  56. public string CatalogName;
  57. public int ColumnLength;
  58. public string ColumnName;
  59. public string OriginalColumnName;
  60. public string TableName;
  61. public string RealTableName;
  62. public string DatabaseName;
  63. public Encoding Encoding;
  64. public int maxLength;
  65. // protected fields
  66. protected ColumnFlags colFlags;
  67. protected int charSetIndex;
  68. protected byte precision;
  69. protected byte scale;
  70. protected MySqlDbType mySqlDbType;
  71. protected DBVersion connVersion;
  72. protected Driver driver;
  73. protected bool binaryOk;
  74. protected List<Type> typeConversions = new List<Type>();
  75. #endregion
  76. public MySqlField(Driver driver)
  77. {
  78. this.driver = driver;
  79. connVersion = driver.Version;
  80. maxLength = 1;
  81. binaryOk = true;
  82. }
  83. #region Properties
  84. public int CharacterSetIndex
  85. {
  86. get { return charSetIndex; }
  87. set { charSetIndex = value; SetFieldEncoding(); }
  88. }
  89. public MySqlDbType Type
  90. {
  91. get { return mySqlDbType; }
  92. }
  93. public byte Precision
  94. {
  95. get { return precision; }
  96. set { precision = value; }
  97. }
  98. public byte Scale
  99. {
  100. get { return scale; }
  101. set { scale = value; }
  102. }
  103. public int MaxLength
  104. {
  105. get { return maxLength; }
  106. set { maxLength = value; }
  107. }
  108. public ColumnFlags Flags
  109. {
  110. get { return colFlags; }
  111. }
  112. public bool IsAutoIncrement
  113. {
  114. get { return (colFlags & ColumnFlags.AUTO_INCREMENT) > 0; }
  115. }
  116. public bool IsNumeric
  117. {
  118. get { return (colFlags & ColumnFlags.NUMBER) > 0; }
  119. }
  120. public bool AllowsNull
  121. {
  122. get { return (colFlags & ColumnFlags.NOT_NULL) == 0; }
  123. }
  124. public bool IsUnique
  125. {
  126. get { return (colFlags & ColumnFlags.UNIQUE_KEY) > 0; }
  127. }
  128. public bool IsPrimaryKey
  129. {
  130. get { return (colFlags & ColumnFlags.PRIMARY_KEY) > 0; }
  131. }
  132. public bool IsBlob
  133. {
  134. get
  135. {
  136. return (mySqlDbType >= MySqlDbType.TinyBlob &&
  137. mySqlDbType <= MySqlDbType.Blob) ||
  138. (mySqlDbType >= MySqlDbType.TinyText &&
  139. mySqlDbType <= MySqlDbType.Text) ||
  140. (colFlags & ColumnFlags.BLOB) > 0;
  141. }
  142. }
  143. public bool IsBinary
  144. {
  145. get
  146. {
  147. return binaryOk && (CharacterSetIndex == 63);
  148. }
  149. }
  150. public bool IsUnsigned
  151. {
  152. get { return (colFlags & ColumnFlags.UNSIGNED) > 0; }
  153. }
  154. public bool IsTextField
  155. {
  156. get
  157. {
  158. return Type == MySqlDbType.VarString || Type == MySqlDbType.VarChar ||
  159. Type == MySqlDbType.String || (Type == MySqlDbType.Guid && !driver.Settings.OldGuids);
  160. }
  161. }
  162. public int CharacterLength
  163. {
  164. get { return ColumnLength / MaxLength; }
  165. }
  166. public List<Type> TypeConversions
  167. {
  168. get { return typeConversions; }
  169. }
  170. #endregion
  171. public void SetTypeAndFlags(MySqlDbType type, ColumnFlags flags)
  172. {
  173. colFlags = flags;
  174. mySqlDbType = type;
  175. if (String.IsNullOrEmpty(TableName) && String.IsNullOrEmpty(RealTableName) &&
  176. IsBinary && driver.Settings.FunctionsReturnString)
  177. {
  178. CharacterSetIndex = driver.ConnectionCharSetIndex;
  179. }
  180. // if our type is an unsigned number, then we need
  181. // to bump it up into our unsigned types
  182. // we're trusting that the server is not going to set the UNSIGNED
  183. // flag unless we are a number
  184. if (IsUnsigned)
  185. {
  186. switch (type)
  187. {
  188. case MySqlDbType.Byte:
  189. mySqlDbType = MySqlDbType.UByte;
  190. return;
  191. case MySqlDbType.Int16:
  192. mySqlDbType = MySqlDbType.UInt16;
  193. return;
  194. case MySqlDbType.Int24:
  195. mySqlDbType = MySqlDbType.UInt24;
  196. return;
  197. case MySqlDbType.Int32:
  198. mySqlDbType = MySqlDbType.UInt32;
  199. return;
  200. case MySqlDbType.Int64:
  201. mySqlDbType = MySqlDbType.UInt64;
  202. return;
  203. }
  204. }
  205. if (IsBlob)
  206. {
  207. // handle blob to UTF8 conversion if requested. This is only activated
  208. // on binary blobs
  209. if (IsBinary && driver.Settings.TreatBlobsAsUTF8)
  210. {
  211. bool convertBlob = false;
  212. Regex includeRegex = driver.Settings.GetBlobAsUTF8IncludeRegex();
  213. Regex excludeRegex = driver.Settings.GetBlobAsUTF8ExcludeRegex();
  214. if (includeRegex != null && includeRegex.IsMatch(ColumnName))
  215. convertBlob = true;
  216. else if (includeRegex == null && excludeRegex != null &&
  217. !excludeRegex.IsMatch(ColumnName))
  218. convertBlob = true;
  219. if (convertBlob)
  220. {
  221. binaryOk = false;
  222. Encoding = System.Text.Encoding.GetEncoding("UTF-8");
  223. charSetIndex = -1; // lets driver know we are in charge of encoding
  224. maxLength = 4;
  225. }
  226. }
  227. if (!IsBinary)
  228. {
  229. if (type == MySqlDbType.TinyBlob)
  230. mySqlDbType = MySqlDbType.TinyText;
  231. else if (type == MySqlDbType.MediumBlob)
  232. mySqlDbType = MySqlDbType.MediumText;
  233. else if (type == MySqlDbType.Blob)
  234. mySqlDbType = MySqlDbType.Text;
  235. else if (type == MySqlDbType.LongBlob)
  236. mySqlDbType = MySqlDbType.LongText;
  237. }
  238. }
  239. // now determine if we really should be binary
  240. if (driver.Settings.RespectBinaryFlags)
  241. CheckForExceptions();
  242. if (Type == MySqlDbType.String && CharacterLength == 36 && !driver.Settings.OldGuids)
  243. mySqlDbType = MySqlDbType.Guid;
  244. if (!IsBinary) return;
  245. if (driver.Settings.RespectBinaryFlags)
  246. {
  247. if (type == MySqlDbType.String)
  248. mySqlDbType = MySqlDbType.Binary;
  249. else if (type == MySqlDbType.VarChar ||
  250. type == MySqlDbType.VarString)
  251. mySqlDbType = MySqlDbType.VarBinary;
  252. }
  253. if (CharacterSetIndex == 63)
  254. CharacterSetIndex = driver.ConnectionCharSetIndex;
  255. if (Type == MySqlDbType.Binary && ColumnLength == 16 && driver.Settings.OldGuids)
  256. mySqlDbType = MySqlDbType.Guid;
  257. }
  258. public void AddTypeConversion(Type t)
  259. {
  260. if (TypeConversions.Contains(t)) return;
  261. TypeConversions.Add(t);
  262. }
  263. private void CheckForExceptions()
  264. {
  265. string colName = String.Empty;
  266. if (OriginalColumnName != null)
  267. colName = StringUtility.ToUpperInvariant(OriginalColumnName);
  268. if (colName.StartsWith("CHAR(", StringComparison.Ordinal))
  269. binaryOk = false;
  270. }
  271. public IMySqlValue GetValueObject()
  272. {
  273. IMySqlValue v = GetIMySqlValue(Type);
  274. if (v is MySqlByte && ColumnLength == 1 && driver.Settings.TreatTinyAsBoolean)
  275. {
  276. MySqlByte b = (MySqlByte)v;
  277. b.TreatAsBoolean = true;
  278. v = b;
  279. }
  280. else if (v is MySqlGuid)
  281. {
  282. MySqlGuid g = (MySqlGuid)v;
  283. g.OldGuids = driver.Settings.OldGuids;
  284. v = g;
  285. }
  286. return v;
  287. }
  288. public static IMySqlValue GetIMySqlValue(MySqlDbType type)
  289. {
  290. switch (type)
  291. {
  292. case MySqlDbType.Byte:
  293. return new MySqlByte();
  294. case MySqlDbType.UByte:
  295. return new MySqlUByte();
  296. case MySqlDbType.Int16:
  297. return new MySqlInt16();
  298. case MySqlDbType.UInt16:
  299. return new MySqlUInt16();
  300. case MySqlDbType.Int24:
  301. case MySqlDbType.Int32:
  302. case MySqlDbType.Year:
  303. return new MySqlInt32(type, true);
  304. case MySqlDbType.UInt24:
  305. case MySqlDbType.UInt32:
  306. return new MySqlUInt32(type, true);
  307. case MySqlDbType.Bit:
  308. return new MySqlBit();
  309. case MySqlDbType.Int64:
  310. return new MySqlInt64();
  311. case MySqlDbType.UInt64:
  312. return new MySqlUInt64();
  313. case MySqlDbType.Time:
  314. return new MySqlTimeSpan();
  315. case MySqlDbType.Date:
  316. case MySqlDbType.DateTime:
  317. case MySqlDbType.Newdate:
  318. case MySqlDbType.Timestamp:
  319. return new MySqlDateTime(type, true);
  320. case MySqlDbType.Decimal:
  321. case MySqlDbType.NewDecimal:
  322. return new MySqlDecimal();
  323. case MySqlDbType.Float:
  324. return new MySqlSingle();
  325. case MySqlDbType.Double:
  326. return new MySqlDouble();
  327. case MySqlDbType.Set:
  328. case MySqlDbType.Enum:
  329. case MySqlDbType.String:
  330. case MySqlDbType.VarString:
  331. case MySqlDbType.VarChar:
  332. case MySqlDbType.Text:
  333. case MySqlDbType.TinyText:
  334. case MySqlDbType.MediumText:
  335. case MySqlDbType.LongText:
  336. case MySqlDbType.JSON:
  337. case (MySqlDbType)Field_Type.NULL:
  338. return new MySqlString(type, true);
  339. case MySqlDbType.Geometry:
  340. return new MySqlGeometry(type, true);
  341. case MySqlDbType.Blob:
  342. case MySqlDbType.MediumBlob:
  343. case MySqlDbType.LongBlob:
  344. case MySqlDbType.TinyBlob:
  345. case MySqlDbType.Binary:
  346. case MySqlDbType.VarBinary:
  347. return new MySqlBinary(type, true);
  348. case MySqlDbType.Guid:
  349. return new MySqlGuid();
  350. default:
  351. throw new MySqlException("Unknown data type");
  352. }
  353. }
  354. private void SetFieldEncoding()
  355. {
  356. Dictionary<int, string> charSets = driver.CharacterSets;
  357. DBVersion version = driver.Version;
  358. if (charSets == null || charSets.Count == 0 || CharacterSetIndex == -1) return;
  359. if (charSets[CharacterSetIndex] == null) return;
  360. CharacterSet cs = CharSetMap.GetCharacterSet(version, (string)charSets[CharacterSetIndex]);
  361. MaxLength = cs.byteCount;
  362. Encoding = CharSetMap.GetEncoding(version, (string)charSets[CharacterSetIndex]);
  363. }
  364. }
  365. }
  366. #endif