#if MYSQL_6_10 // Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved. // // MySQL Connector/NET is licensed under the terms of the GPLv2 // , like most // MySQL Connectors. There are special exceptions to the terms and // conditions of the GPLv2 as it is applied to this software, see the // FLOSS License Exception // . // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published // by the Free Software Foundation; version 2 of the License. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA using System; using System.ComponentModel; using System.Reflection; using System.Text; using System.Collections; using System.Data; using Externals.MySql.Data.Types; using Externals.MySql.Data.Common; using System.Data.Common; using System.Globalization; namespace Externals.MySql.Data.MySqlClient { /// /// Represents a parameter to a , This class cannot be inherited. /// [TypeConverter(typeof(MySqlParameterConverter))] internal sealed partial class MySqlParameter : DbParameter, IDbDataParameter { private const int UNSIGNED_MASK = 0x8000; private object _paramValue; private string _paramName; private MySqlDbType _mySqlDbType; private bool _inferType = true; private const int GEOMETRY_LENGTH = 25; private DbType _dbType; #region Constructors /// /// Initializes a new instance of the class with the parameter name, the , the size, and the source column name. /// /// The name of the parameter to map. /// One of the values. /// The length of the parameter. /// The name of the source column. public MySqlParameter(string parameterName, MySqlDbType dbType, int size, string sourceColumn) : this(parameterName, dbType) { Size = size; Direction = ParameterDirection.Input; SourceColumn = sourceColumn; SourceVersion = DataRowVersion.Default; } public MySqlParameter() { SourceVersion = DataRowVersion.Default; Direction = ParameterDirection.Input; } /// /// Initializes a new instance of the class with the parameter name and a value of the new MySqlParameter. /// /// The name of the parameter to map. /// An that is the value of the . public MySqlParameter(string parameterName, object value) : this() { ParameterName = parameterName; Value = value; } /// /// Initializes a new instance of the class with the parameter name and the data type. /// /// The name of the parameter to map. /// One of the values. public MySqlParameter(string parameterName, MySqlDbType dbType) : this(parameterName, null) { MySqlDbType = dbType; } /// /// Initializes a new instance of the class with the parameter name, the , and the size. /// /// The name of the parameter to map. /// One of the values. /// The length of the parameter. public MySqlParameter(string parameterName, MySqlDbType dbType, int size) : this(parameterName, dbType) { Size = size; } #if NETSTANDARD1_3 /// /// Initializes a new instance of the class with the parameter name, the type of the parameter, the size of the parameter, a , the precision of the parameter, the scale of the parameter, the source column, a to use, and the value of the parameter. /// /// The name of the parameter to map. /// One of the values. /// The length of the parameter. /// One of the values. /// true if the value of the field can be null, otherwise false. /// The total number of digits to the left and right of the decimal point to which is resolved. /// The total number of decimal places to which is resolved. /// The name of the source column. /// An that is the value of the . /// public MySqlParameter(string parameterName, MySqlDbType dbType, int size, ParameterDirection direction, bool isNullable, byte precision, byte scale, string sourceColumn, object value) : this(parameterName, dbType, size, sourceColumn) #else /// /// Initializes a new instance of the class with the parameter name, the type of the parameter, the size of the parameter, a , the precision of the parameter, the scale of the parameter, the source column, a to use, and the value of the parameter. /// /// The name of the parameter to map. /// One of the values. /// The length of the parameter. /// One of the values. /// true if the value of the field can be null, otherwise false. /// The total number of digits to the left and right of the decimal point to which is resolved. /// The total number of decimal places to which is resolved. /// The name of the source column. /// One of the values. /// An that is the value of the . /// public MySqlParameter(string parameterName, MySqlDbType dbType, int size, ParameterDirection direction, bool isNullable, byte precision, byte scale, string sourceColumn, DataRowVersion sourceVersion, object value) : this(parameterName, dbType, size, sourceColumn) #endif { Direction = direction; IsNullable = isNullable; Precision = precision; Scale = scale; Value = value; #if !NETSTANDARD1_3 SourceVersion = sourceVersion; #endif } #if NETSTANDARD1_3 internal MySqlParameter(string name, MySqlDbType type, ParameterDirection dir, string col, object val) : this(name, type) #else internal MySqlParameter(string name, MySqlDbType type, ParameterDirection dir, string col, DataRowVersion sourceVersion, object val) : this(name, type) #endif { Direction = dir; SourceColumn = col; Value = val; #if !NETSTANDARD1_3 SourceVersion = sourceVersion; #endif } #endregion #region Properties [Category("Misc")] public override String ParameterName { get { return _paramName; } set { SetParameterName(value); } } internal MySqlParameterCollection Collection { get; set; } internal Encoding Encoding { get; set; } internal bool TypeHasBeenSet => _inferType == false; internal string BaseName { get { if (ParameterName.StartsWith("@", StringComparison.Ordinal) || ParameterName.StartsWith("?", StringComparison.Ordinal)) return ParameterName.Substring(1); return ParameterName; } } /// /// Gets or sets a value indicating whether the parameter is input-only, output-only, bidirectional, or a stored procedure return value parameter. /// As of MySql version 4.1 and earlier, input-only is the only valid choice. /// [Category("Data")] public override ParameterDirection Direction { get; set; } /// /// Gets or sets a value indicating whether the parameter accepts null values. /// [Browsable(false)] public override Boolean IsNullable { get; set; } /// /// Gets or sets the of the parameter. /// [Category("Data")] #if !NETSTANDARD1_3 [DbProviderSpecificTypeProperty(true)] #endif public MySqlDbType MySqlDbType { get { return _mySqlDbType; } set { SetMySqlDbType(value); _inferType = false; } } /// /// Gets or sets the maximum number of digits used to represent the property. /// [Category("Data")] #if NET40 public byte Precision { get; set; } #else public override byte Precision { get; set; } #endif /// /// Gets or sets the number of decimal places to which is resolved. /// [Category("Data")] #if NET40 public byte Scale { get; set; } #else public override byte Scale { get; set; } #endif /// /// Gets or sets the maximum size, in bytes, of the data within the column. /// [Category("Data")] public override int Size { get; set; } /// /// Gets or sets the value of the parameter. /// [TypeConverter(typeof(StringConverter))] [Category("Data")] public override object Value { get { return _paramValue; } set { _paramValue = value; byte[] valueAsByte = value as byte[]; string valueAsString = value as string; if (valueAsByte != null) Size = valueAsByte.Length; else if (valueAsString != null) Size = valueAsString.Length; if (_inferType) SetTypeFromValue(); } } internal IMySqlValue ValueObject { get; private set; } /// /// Returns the possible values for this parameter if this parameter is of type /// SET or ENUM. Returns null otherwise. /// public IList PossibleValues { get; internal set; } /// /// Gets or sets the name of the source column that is mapped to the and used for loading or returning the . /// [Category("Data")] public override String SourceColumn { get; set; } /// /// Sets or gets a value which indicates whether the source column is nullable. /// This allows to correctly generate Update statements /// for nullable columns. /// public override bool SourceColumnNullMapping { get; set; } /// /// Gets or sets the of the parameter. /// public override DbType DbType { get { return _dbType; } set { SetDbType(value); _inferType = false; } } #endregion private void SetParameterName(string name) { Collection?.ParameterNameChanged(this, _paramName, name); _paramName = name; } /// /// Overridden. Gets a string containing the . /// /// public override string ToString() => _paramName; internal int GetPSType() { switch (_mySqlDbType) { case MySqlDbType.Bit: return (int)MySqlDbType.Int64 | UNSIGNED_MASK; case MySqlDbType.UByte: return (int)MySqlDbType.Byte | UNSIGNED_MASK; case MySqlDbType.UInt64: return (int)MySqlDbType.Int64 | UNSIGNED_MASK; case MySqlDbType.UInt32: return (int)MySqlDbType.Int32 | UNSIGNED_MASK; case MySqlDbType.UInt24: return (int)MySqlDbType.Int32 | UNSIGNED_MASK; case MySqlDbType.UInt16: return (int)MySqlDbType.Int16 | UNSIGNED_MASK; case MySqlDbType.Guid: return (int)MySqlDbType.Guid - 600; default: int value = (int)_mySqlDbType; value = value > 255 ? value - 500 : value; return value; } } internal void Serialize(MySqlPacket packet, bool binary, MySqlConnectionStringBuilder settings) { if (!binary && (_paramValue == null || _paramValue == DBNull.Value)) packet.WriteStringNoNull("NULL"); else { if (ValueObject.MySqlDbType == MySqlDbType.Guid) { MySqlGuid g = (MySqlGuid)ValueObject; g.OldGuids = settings.OldGuids; ValueObject = g; } if (ValueObject.MySqlDbType == MySqlDbType.Geometry) { MySqlGeometry v = (MySqlGeometry)ValueObject; if (v.IsNull && Value != null) { MySqlGeometry.TryParse(Value.ToString(), out v); } ValueObject = v; } ValueObject.WriteValue(packet, binary, _paramValue, Size); } } private void SetMySqlDbType(MySqlDbType mysqlDbtype) { _mySqlDbType = mysqlDbtype; ValueObject = MySqlField.GetIMySqlValue(_mySqlDbType); SetDbTypeFromMySqlDbType(); } private void SetTypeFromValue() { if (_paramValue == null || _paramValue == DBNull.Value) return; if (_paramValue is Guid) MySqlDbType = MySqlDbType.Guid; else if (_paramValue is TimeSpan) MySqlDbType = MySqlDbType.Time; else if (_paramValue is bool) MySqlDbType = MySqlDbType.Byte; else { Type t = _paramValue.GetType(); switch (t.Name) { case "SByte": MySqlDbType = MySqlDbType.Byte; break; case "Byte": MySqlDbType = MySqlDbType.UByte; break; case "Int16": MySqlDbType = MySqlDbType.Int16; break; case "UInt16": MySqlDbType = MySqlDbType.UInt16; break; case "Int32": MySqlDbType = MySqlDbType.Int32; break; case "UInt32": MySqlDbType = MySqlDbType.UInt32; break; case "Int64": MySqlDbType = MySqlDbType.Int64; break; case "UInt64": MySqlDbType = MySqlDbType.UInt64; break; case "DateTime": MySqlDbType = MySqlDbType.DateTime; break; case "String": MySqlDbType = MySqlDbType.VarChar; break; case "Single": MySqlDbType = MySqlDbType.Float; break; case "Double": MySqlDbType = MySqlDbType.Double; break; case "Decimal": MySqlDbType = MySqlDbType.Decimal; break; case "Object": default: if (t.GetTypeInfo().BaseType == typeof(Enum)) MySqlDbType = MySqlDbType.Int32; else MySqlDbType = MySqlDbType.Blob; break; } } } // this method is pretty dumb but we want it to be fast. it doesn't return size based // on value and type but just on the value. internal long EstimatedSize() { if (Value == null || Value == DBNull.Value) return 4; // size of NULL if (Value is byte[]) return ((byte[])Value).Length; if (Value is string) return ((string)Value).Length * 4; // account for UTF-8 (yeah I know) if (Value is decimal || Value is float) return 64; return 32; } /// /// Resets the DbType property to its original settings. /// public override void ResetDbType() { _inferType = true; } void SetDbTypeFromMySqlDbType() { switch (_mySqlDbType) { case MySqlDbType.NewDecimal: case MySqlDbType.Decimal: _dbType = DbType.Decimal; break; case MySqlDbType.Byte: _dbType = DbType.SByte; break; case MySqlDbType.UByte: _dbType = DbType.Byte; break; case MySqlDbType.Int16: _dbType = DbType.Int16; break; case MySqlDbType.UInt16: _dbType = DbType.UInt16; break; case MySqlDbType.Int24: case MySqlDbType.Int32: _dbType = DbType.Int32; break; case MySqlDbType.UInt24: case MySqlDbType.UInt32: _dbType = DbType.UInt32; break; case MySqlDbType.Int64: _dbType = DbType.Int64; break; case MySqlDbType.UInt64: _dbType = DbType.UInt64; break; case MySqlDbType.Bit: _dbType = DbType.UInt64; break; case MySqlDbType.Float: _dbType = DbType.Single; break; case MySqlDbType.Double: _dbType = DbType.Double; break; case MySqlDbType.Timestamp: case MySqlDbType.DateTime: _dbType = DbType.DateTime; break; case MySqlDbType.Date: case MySqlDbType.Newdate: case MySqlDbType.Year: _dbType = DbType.Date; break; case MySqlDbType.Time: _dbType = DbType.Time; break; case MySqlDbType.Enum: case MySqlDbType.Set: case MySqlDbType.VarChar: _dbType = DbType.String; break; case MySqlDbType.TinyBlob: case MySqlDbType.MediumBlob: case MySqlDbType.LongBlob: case MySqlDbType.Blob: _dbType = DbType.Object; break; case MySqlDbType.String: _dbType = DbType.StringFixedLength; break; case MySqlDbType.Guid: _dbType = DbType.Guid; break; } } private void SetDbType(DbType dbType) { _dbType = dbType; switch (_dbType) { case DbType.Guid: _mySqlDbType = MySqlDbType.Guid; break; case DbType.AnsiString: case DbType.String: _mySqlDbType = MySqlDbType.VarChar; break; case DbType.AnsiStringFixedLength: case DbType.StringFixedLength: _mySqlDbType = MySqlDbType.String; break; case DbType.Boolean: case DbType.Byte: _mySqlDbType = MySqlDbType.UByte; break; case DbType.SByte: _mySqlDbType = MySqlDbType.Byte; break; case DbType.Date: _mySqlDbType = MySqlDbType.Date; break; case DbType.DateTime: _mySqlDbType = MySqlDbType.DateTime; break; case DbType.Time: _mySqlDbType = MySqlDbType.Time; break; case DbType.Single: _mySqlDbType = MySqlDbType.Float; break; case DbType.Double: _mySqlDbType = MySqlDbType.Double; break; case DbType.Int16: _mySqlDbType = MySqlDbType.Int16; break; case DbType.UInt16: _mySqlDbType = MySqlDbType.UInt16; break; case DbType.Int32: _mySqlDbType = MySqlDbType.Int32; break; case DbType.UInt32: _mySqlDbType = MySqlDbType.UInt32; break; case DbType.Int64: _mySqlDbType = MySqlDbType.Int64; break; case DbType.UInt64: _mySqlDbType = MySqlDbType.UInt64; break; case DbType.Decimal: case DbType.Currency: _mySqlDbType = MySqlDbType.Decimal; break; case DbType.Object: case DbType.VarNumeric: case DbType.Binary: default: _mySqlDbType = MySqlDbType.Blob; break; } if (_dbType == DbType.Object) { var value = this._paramValue as byte[]; if (value != null && value.Length == GEOMETRY_LENGTH) _mySqlDbType = MySqlDbType.Geometry; } ValueObject = MySqlField.GetIMySqlValue(_mySqlDbType); } } internal class MySqlParameterConverter : TypeConverter { public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { #if !NETSTANDARD1_3 if (destinationType == typeof(System.ComponentModel.Design.Serialization.InstanceDescriptor)) { return true; } #endif // Always call the base to see if it can perform the conversion. return base.CanConvertTo(context, destinationType); } public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { #if !NETSTANDARD1_3 if (destinationType == typeof(System.ComponentModel.Design.Serialization.InstanceDescriptor)) { ConstructorInfo ci = typeof(MySqlParameter).GetConstructor( new[] { typeof (string), typeof (MySqlDbType), typeof (int), typeof (ParameterDirection), typeof (bool), typeof (byte), typeof (byte), typeof (string), typeof (object) }); MySqlParameter p = (MySqlParameter)value; return new System.ComponentModel.Design.Serialization.InstanceDescriptor(ci, new object[] { p.ParameterName, p.DbType, p.Size, p.Direction, p.IsNullable, p.Precision, p.Scale, p.SourceColumn, p.Value }); } #endif // Always call base, even if you can't convert. return base.ConvertTo(context, culture, value, destinationType); } } } #endif