示例#1
0
        protected virtual void DefaultValue(
            [CanBeNull] object defaultValue,
            [CanBeNull] string defaultValueSql,
            [NotNull] MigrationCommandListBuilder builder)
        {
            Check.NotNull(builder, nameof(builder));

            if (defaultValueSql != null)
            {
                builder
                .Append(" DEFAULT (")
                .Append(defaultValueSql)
                .Append(")");
            }
            else if (defaultValue != null)
            {
                builder
                .Append(" DEFAULT ")
                .Append(SqlGenerationHelper.GenerateLiteral(defaultValue));
            }
        }
        protected virtual void Rename(
            [NotNull] string name,
            [NotNull] string newName,
            [CanBeNull] string type,
            [NotNull] RelationalCommandListBuilder builder)
        {
            Check.NotEmpty(name, nameof(name));
            Check.NotEmpty(newName, nameof(newName));
            Check.NotNull(builder, nameof(builder));

            builder
            .Append("EXEC sp_rename ")
            .Append(SqlGenerationHelper.GenerateLiteral(name))
            .Append(", ")
            .Append(SqlGenerationHelper.GenerateLiteral(newName));

            if (type != null)
            {
                builder
                .Append(", ")
                .Append(SqlGenerationHelper.GenerateLiteral(type));
            }
        }
        protected virtual void Generate(
            [NotNull] CreateSequenceOperation operation,
            [CanBeNull] IModel model,
            [NotNull] RelationalCommandListBuilder builder)
        {
            Check.NotNull(operation, nameof(operation));
            Check.NotNull(builder, nameof(builder));

            builder
            .Append("CREATE SEQUENCE ")
            .Append(SqlGenerationHelper.DelimitIdentifier(operation.Name, operation.Schema));

            if (operation.ClrType != typeof(long))
            {
                builder
                .Append(" AS ")
                .Append(TypeMapper.GetMapping(operation.ClrType).DefaultTypeName);
            }

            builder
            .Append(" START WITH ")
            .Append(SqlGenerationHelper.GenerateLiteral(operation.StartValue));
            SequenceOptions(operation, model, builder);
        }
        protected override void Generate(AlterColumnOperation operation, IModel model, MigrationCommandListBuilder builder)
        {
            Check.NotNull(operation, nameof(operation));
            Check.NotNull(builder, nameof(builder));

            var type = operation.ColumnType;

            if (operation.ColumnType == null)
            {
                var property = FindProperty(model, operation.Schema, operation.Table, operation.Name);
                type = property != null
                    ? TypeMapper.GetMapping(property).StoreType
                    : TypeMapper.GetMapping(operation.ClrType).StoreType;
            }

            var serial   = operation.FindAnnotation(MySqlAnnotationNames.Prefix + MySqlAnnotationNames.Serial);
            var isSerial = serial != null && (bool)serial.Value;

            var identifier = SqlGenerationHelper.DelimitIdentifier(operation.Table, operation.Schema);
            var alterBase  = $"ALTER TABLE {identifier} MODIFY COLUMN {SqlGenerationHelper.DelimitIdentifier(operation.Name)}";

            // TYPE
            builder.Append(alterBase)
            .Append(" ")
            .Append(type)
            .Append(operation.IsNullable ? " NULL" : " NOT NULL")
            .AppendLine(SqlGenerationHelper.StatementTerminator);

            switch (type)
            {
            case "tinyblob":
            case "blob":
            case "mediumblob":
            case "longblob":

            case "tinytext":
            case "text":
            case "mediumtext":
            case "longtext":

            case "geometry":
            case "point":
            case "linestring":
            case "polygon":
            case "multipoint":
            case "multilinestring":
            case "multipolygon":
            case "geometrycollection":

            case "json":
                if (operation.DefaultValue != null || !string.IsNullOrWhiteSpace(operation.DefaultValueSql) || isSerial)
                {
                    throw new NotSupportedException($"{type} column can't have a default value");
                }
                break;

            default:
                alterBase = $"ALTER TABLE {identifier} ALTER COLUMN {SqlGenerationHelper.DelimitIdentifier(operation.Name)}";

                builder.Append(alterBase);

                if (operation.DefaultValue != null)
                {
                    builder.Append(" SET DEFAULT ")
                    .Append(SqlGenerationHelper.GenerateLiteral((dynamic)operation.DefaultValue))
                    .AppendLine(SqlGenerationHelper.BatchTerminator);
                }
                else if (!string.IsNullOrWhiteSpace(operation.DefaultValueSql))
                {
                    builder.Append(" SET DEFAULT ")
                    .Append(operation.DefaultValueSql)
                    .AppendLine(SqlGenerationHelper.BatchTerminator);
                }
                else if (isSerial)
                {
                    builder.Append(" SET DEFAULT ");

                    switch (type)
                    {
                    case "smallint":
                    case "int":
                    case "bigint":
                    case "real":
                    case "double precision":
                    case "numeric":
                        //TODO: need function CREATE SEQUENCE IF NOT EXISTS and set to it...
                        //Until this is resolved changing IsIdentity from false to true
                        //on types int2, int4 and int8 won't switch to type serial2, serial4 and serial8
                        throw new NotImplementedException("Not supporting creating sequence for integer types");

                    case "char(38)":
                    case "uuid":
                    case "uniqueidentifier":
                        break;

                    default:
                        throw new NotImplementedException($"Not supporting creating IsIdentity for {type}");
                    }
                }
                else
                {
                    builder.Append(" DROP DEFAULT;");
                }
                break;
            }

            EndStatement(builder);
        }
        protected override void ColumnDefinition([CanBeNull] string schema, [NotNull] string table, [NotNull] string name, [NotNull] Type clrType, [CanBeNull] string type, [CanBeNull] bool?unicode, [CanBeNull] int?maxLength, bool rowVersion, bool nullable, [CanBeNull] object defaultValue, [CanBeNull] string defaultValueSql, [CanBeNull] string computedColumnSql, [NotNull] IAnnotatable annotatable, [CanBeNull] IModel model, [NotNull] MigrationCommandListBuilder builder)
        {
            Check.NotEmpty(name, nameof(name));
            Check.NotNull(annotatable, nameof(annotatable));
            Check.NotNull(clrType, nameof(clrType));
            Check.NotNull(builder, nameof(builder));

            if (type == null)
            {
                var property = FindProperty(model, schema, table, name);
                type = TypeMapper.FindMapping(property).StoreType;
            }

            var matchType = type;
            var matchLen  = "";
            var match     = TypeRe.Match(type);

            if (match.Success)
            {
                matchType = match.Groups[1].Value.ToLower();
                if (!string.IsNullOrWhiteSpace(match.Groups[2].Value))
                {
                    matchLen = match.Groups[2].Value;
                }
            }

            var autoIncrement                    = false;
            var generatedOnAddAnnotation         = annotatable[MySqlAnnotationNames.Prefix + MySqlAnnotationNames.ValueGeneratedOnAdd];
            var generatedOnAdd                   = generatedOnAddAnnotation != null && (bool)generatedOnAddAnnotation;
            var generatedOnAddOrUpdateAnnotation = annotatable[MySqlAnnotationNames.Prefix + MySqlAnnotationNames.ValueGeneratedOnAddOrUpdate];
            var generatedOnAddOrUpdate           = generatedOnAddOrUpdateAnnotation != null && (bool)generatedOnAddOrUpdateAnnotation;

            if (generatedOnAdd && string.IsNullOrWhiteSpace(defaultValueSql) && defaultValue == null)
            {
                switch (matchType)
                {
                case "tinyint":
                case "smallint":
                case "mediumint":
                case "int":
                case "bigint":
                    autoIncrement = true;
                    break;

                case "datetime":
                    if (_mySqlTypeMapper != null && !_mySqlTypeMapper.ConnectionSettings.SupportsDateTime6)
                    {
                        throw new InvalidOperationException(
                                  $"Error in {table}.{name}: DATETIME does not support values generated " +
                                  "on Add or Update in MySql <= 5.5, try explicitly setting the column type to TIMESTAMP");
                    }
                    goto case "timestamp";

                case "timestamp":
                    defaultValueSql = $"CURRENT_TIMESTAMP({matchLen})";
                    break;
                }
            }

            string onUpdateSql = null;

            if (generatedOnAddOrUpdate)
            {
                switch (matchType)
                {
                case "datetime":
                    if (_mySqlTypeMapper != null && !_mySqlTypeMapper.ConnectionSettings.SupportsDateTime6)
                    {
                        throw new InvalidOperationException($"Error in {table}.{name}: DATETIME does not support values generated " +
                                                            "on Add or Update in MySql <= 5.5, try explicitly setting the column type to TIMESTAMP");
                    }
                    goto case "timestamp";

                case "timestamp":
                    if (string.IsNullOrWhiteSpace(defaultValueSql) && defaultValue == null)
                    {
                        defaultValueSql = $"CURRENT_TIMESTAMP({matchLen})";
                    }
                    onUpdateSql = $"CURRENT_TIMESTAMP({matchLen})";
                    break;
                }
            }

            builder
            .Append(SqlGenerationHelper.DelimitIdentifier(name))
            .Append(" ")
            .Append(type ?? GetColumnType(schema, table, name, clrType, unicode, maxLength, rowVersion, model));

            if (!nullable)
            {
                builder.Append(" NOT NULL");
            }

            if (autoIncrement)
            {
                builder.Append(" AUTO_INCREMENT");
            }
            else
            {
                if (defaultValueSql != null)
                {
                    builder
                    .Append(" DEFAULT ")
                    .Append(defaultValueSql);
                }
                else if (defaultValue != null)
                {
                    builder
                    .Append(" DEFAULT ")
                    .Append(SqlGenerationHelper.GenerateLiteral(defaultValue));
                }
                if (onUpdateSql != null)
                {
                    builder
                    .Append(" ON UPDATE ")
                    .Append(onUpdateSql);
                }
            }
        }
        protected override void ColumnDefinition([CanBeNull] string schema, [NotNull] string table, [NotNull] string name, [NotNull] Type clrType, [CanBeNull] string type, [CanBeNull] bool?unicode, [CanBeNull] int?maxLength, bool rowVersion, bool nullable, [CanBeNull] object defaultValue, [CanBeNull] string defaultValueSql, [CanBeNull] string computedColumnSql, [NotNull] IAnnotatable annotatable, [CanBeNull] IModel model, [NotNull] MigrationCommandListBuilder builder)
        {
            Check.NotEmpty(name, nameof(name));
            Check.NotNull(annotatable, nameof(annotatable));
            Check.NotNull(clrType, nameof(clrType));
            Check.NotNull(builder, nameof(builder));

            if (type == null)
            {
                var property = FindProperty(model, schema, table, name);
                type = TypeMapper.FindMapping(property).StoreType;
            }

            var generatedOnAddAnnotation         = annotatable[MySqlAnnotationNames.Prefix + MySqlAnnotationNames.ValueGeneratedOnAdd];
            var generatedOnAdd                   = generatedOnAddAnnotation != null && (bool)generatedOnAddAnnotation;
            var generatedOnAddOrUpdateAnnotation = annotatable[MySqlAnnotationNames.Prefix + MySqlAnnotationNames.ValueGeneratedOnAddOrUpdate];
            var generatedOnAddOrUpdate           = generatedOnAddOrUpdateAnnotation != null && (bool)generatedOnAddOrUpdateAnnotation;

            if (generatedOnAdd && string.IsNullOrWhiteSpace(defaultValueSql) && defaultValue == null)
            {
                switch (type)
                {
                case "int":
                case "int4":
                    type = "int AUTO_INCREMENT";
                    break;

                case "bigint":
                case "int8":
                    type = "bigint AUTO_INCREMENT";
                    break;

                case "smallint":
                case "int2":
                    type = "short AUTO_INCREMENT";
                    break;

                case "datetime":
                case "timestamp":
                    defaultValueSql = "CURRENT_TIMESTAMP";
                    break;
                }
            }

            string onUpdateSql = null;

            if (generatedOnAddOrUpdate)
            {
                switch (type)
                {
                case "datetime":
                case "timestamp":
                    if (string.IsNullOrWhiteSpace(defaultValueSql) && defaultValue == null)
                    {
                        defaultValueSql = "CURRENT_TIMESTAMP";
                    }
                    onUpdateSql = "CURRENT_TIMESTAMP";
                    break;
                }
            }

            builder
            .Append(SqlGenerationHelper.DelimitIdentifier(name))
            .Append(" ")
            .Append(type ?? GetColumnType(schema, table, name, clrType, unicode, maxLength, rowVersion, model));

            if (!nullable)
            {
                builder.Append(" NOT NULL");
            }

            if (defaultValueSql != null)
            {
                builder
                .Append(" DEFAULT ")
                .Append(defaultValueSql);
            }
            else if (defaultValue != null)
            {
                builder
                .Append(" DEFAULT ")
                .Append(SqlGenerationHelper.GenerateLiteral(defaultValue));
            }
            if (onUpdateSql != null)
            {
                builder
                .Append(" ON UPDATE ")
                .Append(onUpdateSql);
            }
        }
        protected override void Generate(AlterColumnOperation operation, IModel model, MigrationCommandListBuilder builder)
        {
            Check.NotNull(operation, nameof(operation));
            Check.NotNull(builder, nameof(builder));

            // TODO: There is probably duplication here with other methods. See ColumnDefinition.

            //TODO: this should provide feature parity with the EF6 provider, check if there's anything missing for EF7

            var type = operation.ColumnType;

            if (operation.ColumnType == null)
            {
                var property = FindProperty(model, operation.Schema, operation.Table, operation.Name);
                type = property != null
                    ? TypeMapper.GetMapping(property).StoreType
                    : TypeMapper.GetMapping(operation.ClrType).StoreType;
            }

            var serial   = operation.FindAnnotation(MySqlAnnotationNames.Prefix + MySqlAnnotationNames.Serial);
            var isSerial = serial != null && (bool)serial.Value;

            var identifier = SqlGenerationHelper.DelimitIdentifier(operation.Table, operation.Schema);
            var alterBase  = $"ALTER TABLE {identifier} MODIFY COLUMN {SqlGenerationHelper.DelimitIdentifier(operation.Name)}";

            // TYPE
            builder.Append(alterBase)
            .Append(" ")
            .Append(type)
            .Append(operation.IsNullable ? " NULL" : " NOT NULL")
            .AppendLine(SqlGenerationHelper.StatementTerminator);

            alterBase = $"ALTER TABLE {identifier} ALTER COLUMN {SqlGenerationHelper.DelimitIdentifier(operation.Name)}";

            builder.Append(alterBase);

            if (operation.DefaultValue != null)
            {
                builder.Append(" SET DEFAULT ")
                .Append(SqlGenerationHelper.GenerateLiteral((dynamic)operation.DefaultValue))
                .AppendLine(SqlGenerationHelper.BatchTerminator);
            }
            else if (!string.IsNullOrWhiteSpace(operation.DefaultValueSql))
            {
                builder.Append(" SET DEFAULT ")
                .Append(operation.DefaultValueSql)
                .AppendLine(SqlGenerationHelper.BatchTerminator);
            }
            else if (isSerial)
            {
                builder.Append(" SET DEFAULT ");

                switch (type)
                {
                case "smallint":
                case "int":
                case "bigint":
                case "real":
                case "double precision":
                case "numeric":
                    //TODO: need function CREATE SEQUENCE IF NOT EXISTS and set to it...
                    //Until this is resolved changing IsIdentity from false to true
                    //on types int2, int4 and int8 won't switch to type serial2, serial4 and serial8
                    throw new NotImplementedException("Not supporting creating sequence for integer types");

                case "char(38)":
                case "uuid":
                case "uniqueidentifier":
                    builder.Append("UUID()");
                    break;

                default:
                    throw new NotImplementedException($"Not supporting creating IsIdentity for {type}");
                }
            }
            else
            {
                builder.Append(" DROP DEFAULT ");
            }
        }