Remove MySQL and Sqlite deps (#5910)

This commit is contained in:
Nicolas Dorier 2024-04-15 19:08:25 +09:00 committed by GitHub
parent 9699b3837b
commit f1a04a3bd0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
86 changed files with 818 additions and 1729 deletions

View file

@ -33,9 +33,7 @@
<ItemGroup>
<PackageReference Include="HtmlSanitizer" Version="8.0.838" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.1" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.0-beta.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BTCPayServer.Client\BTCPayServer.Client.csproj" />

View file

@ -13,12 +13,12 @@ namespace BTCPayServer.Abstractions.Contracts
public abstract class BaseDbContextFactory<T> where T : DbContext
{
private readonly IOptions<DatabaseOptions> _options;
private readonly string _schemaPrefix;
private readonly string _migrationTableName;
public BaseDbContextFactory(IOptions<DatabaseOptions> options, string schemaPrefix)
public BaseDbContextFactory(IOptions<DatabaseOptions> options, string migrationTableName)
{
_options = options;
_schemaPrefix = schemaPrefix;
_migrationTableName = migrationTableName;
}
public abstract T CreateContext();
@ -68,43 +68,16 @@ namespace BTCPayServer.Abstractions.Contracts
public void ConfigureBuilder(DbContextOptionsBuilder builder)
{
switch (_options.Value.DatabaseType)
builder
.UseNpgsql(_options.Value.ConnectionString, o =>
{
case DatabaseType.Sqlite:
builder.UseSqlite(_options.Value.ConnectionString, o =>
{
if (!string.IsNullOrEmpty(_schemaPrefix))
{
o.MigrationsHistoryTable(_schemaPrefix);
}
});
break;
case DatabaseType.Postgres:
builder
.UseNpgsql(_options.Value.ConnectionString, o =>
{
o.EnableRetryOnFailure(10);
o.SetPostgresVersion(12, 0);
var mainSearchPath = GetSearchPath(_options.Value.ConnectionString);
var schemaPrefix = string.IsNullOrEmpty(_schemaPrefix) ? "__EFMigrationsHistory" : _schemaPrefix;
o.MigrationsHistoryTable(schemaPrefix, mainSearchPath);
})
.ReplaceService<IMigrationsSqlGenerator, CustomNpgsqlMigrationsSqlGenerator>();
break;
case DatabaseType.MySQL:
builder.UseMySql(_options.Value.ConnectionString, ServerVersion.AutoDetect(_options.Value.ConnectionString), o =>
{
o.EnableRetryOnFailure(10);
if (!string.IsNullOrEmpty(_schemaPrefix))
{
o.MigrationsHistoryTable(_schemaPrefix);
}
});
break;
default:
throw new ArgumentOutOfRangeException();
}
o.EnableRetryOnFailure(10);
o.SetPostgresVersion(12, 0);
var mainSearchPath = GetSearchPath(_options.Value.ConnectionString);
var schemaPrefix = string.IsNullOrEmpty(_migrationTableName) ? "__EFMigrationsHistory" : _migrationTableName;
o.MigrationsHistoryTable(schemaPrefix, mainSearchPath);
})
.ReplaceService<IMigrationsSqlGenerator, CustomNpgsqlMigrationsSqlGenerator>();
}
private string GetSearchPath(string connectionString)

View file

@ -2,7 +2,6 @@ namespace BTCPayServer.Abstractions.Models
{
public class DatabaseOptions
{
public DatabaseType DatabaseType { get; set; }
public string ConnectionString { get; set; }
}
}

View file

@ -1,9 +0,0 @@
namespace BTCPayServer.Abstractions.Models
{
public enum DatabaseType
{
Sqlite,
Postgres,
MySQL,
}
}

View file

@ -12,30 +12,18 @@ namespace BTCPayServer.Data
{
public ApplicationDbContext CreateDbContext(string[] args)
{
var builder = new DbContextOptionsBuilder<ApplicationDbContext>();
builder.UseSqlite("Data Source=temp.db");
return new ApplicationDbContext(builder.Options, true);
// Same as launchsettings.json, it's connecting to the docker's postgres.
builder.UseNpgsql("User ID=postgres;Include Error Detail=true;Host=127.0.0.1;Port=39372;Database=btcpayserver");
return new ApplicationDbContext(builder.Options);
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
private readonly bool _designTime;
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options, bool designTime = false)
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
_designTime = designTime;
}
#nullable enable
public async Task<string?> GetMigrationState()
{
return (await Settings.FromSqlRaw("SELECT \"Id\", \"Value\" FROM \"Settings\" WHERE \"Id\"='MigrationData'").AsNoTracking().FirstOrDefaultAsync())?.Value;
}
#nullable restore
public DbSet<AddressInvoiceData> AddressInvoices { get; set; }
public DbSet<APIKeyData> ApiKeys { get; set; }
public DbSet<AppData> Apps { get; set; }
@ -76,13 +64,6 @@ namespace BTCPayServer.Data
public DbSet<PayoutProcessorData> PayoutProcessors { get; set; }
public DbSet<FormData> Forms { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var isConfigured = optionsBuilder.Options.Extensions.OfType<RelationalOptionsExtension>().Any();
if (!isConfigured)
optionsBuilder.UseSqlite("Data Source=temp.db");
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
@ -129,28 +110,6 @@ namespace BTCPayServer.Data
WebhookData.OnModelCreating(builder, Database);
FormData.OnModelCreating(builder, Database);
StoreRole.OnModelCreating(builder, Database);
if (Database.IsSqlite() && !_designTime)
{
// SQLite does not have proper support for DateTimeOffset via Entity Framework Core, see the limitations
// here: https://docs.microsoft.com/en-us/ef/core/providers/sqlite/limitations#query-limitations
// To work around this, when the Sqlite database provider is used, all model properties of type DateTimeOffset
// use the DateTimeOffsetToBinaryConverter
// Based on: https://github.com/aspnet/EntityFrameworkCore/issues/10784#issuecomment-415769754
// This only supports millisecond precision, but should be sufficient for most use cases.
foreach (var entityType in builder.Model.GetEntityTypes())
{
var properties = entityType.ClrType.GetProperties().Where(p => p.PropertyType == typeof(DateTimeOffset));
foreach (var property in properties)
{
builder
.Entity(entityType.Name)
.Property(property.Name)
.HasConversion(new Microsoft.EntityFrameworkCore.Storage.ValueConversion.DateTimeOffsetToBinaryConverter());
}
}
}
}
}

View file

@ -41,12 +41,9 @@ namespace BTCPayServer.Data
builder.Entity<APIKeyData>()
.HasIndex(o => o.StoreId);
if (databaseFacade.IsNpgsql())
{
builder.Entity<APIKeyData>()
.Property(o => o.Blob2)
.HasColumnType("JSONB");
}
builder.Entity<APIKeyData>()
.Property(o => o.Blob2)
.HasColumnType("JSONB");
}
}

View file

@ -25,12 +25,9 @@ namespace BTCPayServer.Data
builder.Entity<AppData>()
.HasOne(a => a.StoreData);
if (databaseFacade.IsNpgsql())
{
builder.Entity<AppData>()
.Property(o => o.Settings)
.HasColumnType("JSONB");
}
builder.Entity<AppData>()
.Property(o => o.Settings)
.HasColumnType("JSONB");
}
// utility methods

View file

@ -35,12 +35,10 @@ namespace BTCPayServer.Data
builder.Entity<ApplicationUser>()
.HasMany<IdentityUserRole<string>>(user => user.UserRoles)
.WithOne().HasForeignKey(role => role.UserId);
if (databaseFacade.IsNpgsql())
{
builder.Entity<ApplicationUser>()
.Property(o => o.Blob2)
.HasColumnType("JSONB");
}
builder.Entity<ApplicationUser>()
.Property(o => o.Blob2)
.HasColumnType("JSONB");
}
}

View file

@ -30,12 +30,10 @@ namespace BTCPayServer.Data
.HasOne(o => o.ApplicationUser)
.WithMany(i => i.Fido2Credentials)
.HasForeignKey(i => i.ApplicationUserId).OnDelete(DeleteBehavior.Cascade);
if (databaseFacade.IsNpgsql())
{
builder.Entity<Fido2Credential>()
.Property(o => o.Blob2)
.HasColumnType("JSONB");
}
builder.Entity<Fido2Credential>()
.Property(o => o.Blob2)
.HasColumnType("JSONB");
}
public ApplicationUser ApplicationUser { get; set; }

View file

@ -21,11 +21,8 @@ public class FormData
.WithMany(o => o.Forms).OnDelete(DeleteBehavior.Cascade);
builder.Entity<FormData>().HasIndex(o => o.StoreId);
if (databaseFacade.IsNpgsql())
{
builder.Entity<FormData>()
.Property(o => o.Config)
.HasColumnType("JSONB");
}
builder.Entity<FormData>()
.Property(o => o.Config)
.HasColumnType("JSONB");
}
}

View file

@ -44,15 +44,12 @@ namespace BTCPayServer.Data
builder.Entity<InvoiceData>().HasIndex(o => o.StoreDataId);
builder.Entity<InvoiceData>().HasIndex(o => o.OrderId);
builder.Entity<InvoiceData>().HasIndex(o => o.Created);
if (databaseFacade.IsNpgsql())
{
builder.Entity<InvoiceData>()
.Property(o => o.Blob2)
.HasColumnType("JSONB");
builder.Entity<InvoiceData>()
.Property(o => o.Amount)
.HasColumnType("NUMERIC");
}
builder.Entity<InvoiceData>()
.Property(o => o.Blob2)
.HasColumnType("JSONB");
builder.Entity<InvoiceData>()
.Property(o => o.Amount)
.HasColumnType("NUMERIC");
}
}
}

View file

@ -27,12 +27,9 @@ public class LightningAddressData : IHasBlob<LightningAddressDataBlob>
.IsRequired()
.OnDelete(DeleteBehavior.Cascade);
builder.Entity<LightningAddressData>().HasKey(o => o.Username);
if (databaseFacade.IsNpgsql())
{
builder.Entity<LightningAddressData>()
.Property(o => o.Blob2)
.HasColumnType("JSONB");
}
builder.Entity<LightningAddressData>()
.Property(o => o.Blob2)
.HasColumnType("JSONB");
}
}

View file

@ -30,12 +30,9 @@ namespace BTCPayServer.Data
.HasOne(o => o.ApplicationUser)
.WithMany(n => n.Notifications)
.HasForeignKey(k => k.ApplicationUserId).OnDelete(DeleteBehavior.Cascade);
if (databaseFacade.IsNpgsql())
{
builder.Entity<NotificationData>()
.Property(o => o.Blob2)
.HasColumnType("JSONB");
}
builder.Entity<NotificationData>()
.Property(o => o.Blob2)
.HasColumnType("JSONB");
}
}
}

View file

@ -41,15 +41,12 @@ namespace BTCPayServer.Data
builder.Entity<PaymentData>()
.Property(o => o.Status)
.HasConversion<string>();
if (databaseFacade.IsNpgsql())
{
builder.Entity<PaymentData>()
.Property(o => o.Blob2)
.HasColumnType("JSONB");
builder.Entity<PaymentData>()
.Property(o => o.Amount)
.HasColumnType("NUMERIC");
}
builder.Entity<PaymentData>()
.Property(o => o.Blob2)
.HasColumnType("JSONB");
builder.Entity<PaymentData>()
.Property(o => o.Amount)
.HasColumnType("NUMERIC");
}
}
}

View file

@ -32,12 +32,9 @@ namespace BTCPayServer.Data
builder.Entity<PaymentRequestData>()
.HasIndex(o => o.Status);
if (databaseFacade.IsNpgsql())
{
builder.Entity<PaymentRequestData>()
.Property(o => o.Blob2)
.HasColumnType("JSONB");
}
builder.Entity<PaymentRequestData>()
.Property(o => o.Blob2)
.HasColumnType("JSONB");
}
}
}

View file

@ -47,32 +47,12 @@ namespace BTCPayServer.Data
builder.Entity<PayoutData>()
.HasIndex(x => new { DestinationId = x.Destination, x.State });
if (databaseFacade.IsNpgsql())
{
builder.Entity<PayoutData>()
.Property(o => o.Blob)
.HasColumnType("JSONB");
builder.Entity<PayoutData>()
.Property(o => o.Proof)
.HasColumnType("JSONB");
}
else if (databaseFacade.IsMySql())
{
builder.Entity<PayoutData>()
.Property(o => o.Blob)
.HasConversion(new ValueConverter<string, byte[]>
(
convertToProviderExpression: (str) => Encoding.UTF8.GetBytes(str),
convertFromProviderExpression: (bytes) => Encoding.UTF8.GetString(bytes)
));
builder.Entity<PayoutData>()
.Property(o => o.Proof)
.HasConversion(new ValueConverter<string, byte[]>
(
convertToProviderExpression: (str) => Encoding.UTF8.GetBytes(str),
convertFromProviderExpression: (bytes) => Encoding.UTF8.GetString(bytes)
));
}
builder.Entity<PayoutData>()
.Property(o => o.Blob)
.HasColumnType("JSONB");
builder.Entity<PayoutData>()
.Property(o => o.Proof)
.HasColumnType("JSONB");
}
// utility methods

View file

@ -29,12 +29,9 @@ public class PayoutProcessorData : IHasBlobUntyped
.HasOne(o => o.Store)
.WithMany(data => data.PayoutProcessors).OnDelete(DeleteBehavior.Cascade);
if (databaseFacade.IsNpgsql())
{
builder.Entity<PayoutProcessorData>()
.Property(o => o.Blob2)
.HasColumnType("JSONB");
}
builder.Entity<PayoutProcessorData>()
.Property(o => o.Blob2)
.HasColumnType("JSONB");
}
public override string ToString()

View file

@ -38,22 +38,9 @@ namespace BTCPayServer.Data
.HasOne(o => o.StoreData)
.WithMany(o => o.PullPayments).OnDelete(DeleteBehavior.Cascade);
if (databaseFacade.IsNpgsql())
{
builder.Entity<PullPaymentData>()
.Property(o => o.Blob)
.HasColumnType("JSONB");
}
else if (databaseFacade.IsMySql())
{
builder.Entity<PullPaymentData>()
.Property(o => o.Blob)
.HasConversion(new ValueConverter<string, byte[]>
(
convertToProviderExpression: (str) => Encoding.UTF8.GetBytes(str),
convertFromProviderExpression: (bytes) => Encoding.UTF8.GetString(bytes)
));
}
builder.Entity<PullPaymentData>()
.Property(o => o.Blob)
.HasColumnType("JSONB");
}
public (DateTimeOffset Start, DateTimeOffset? End)? GetPeriod(DateTimeOffset now)

View file

@ -11,12 +11,9 @@ namespace BTCPayServer.Data
public static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
{
if (databaseFacade.IsNpgsql())
{
builder.Entity<SettingData>()
.Property(o => o.Value)
.HasColumnType("JSONB");
}
builder.Entity<SettingData>()
.Property(o => o.Value)
.HasColumnType("JSONB");
}
}
}

View file

@ -52,26 +52,13 @@ namespace BTCPayServer.Data
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
{
if (databaseFacade.IsNpgsql())
{
builder.Entity<StoreData>()
.Property(o => o.StoreBlob)
.HasColumnType("JSONB");
builder.Entity<StoreData>()
.Property(o => o.StoreBlob)
.HasColumnType("JSONB");
builder.Entity<StoreData>()
.Property(o => o.DerivationStrategies)
.HasColumnType("JSONB");
}
else if (databaseFacade.IsMySql())
{
builder.Entity<StoreData>()
.Property(o => o.StoreBlob)
.HasConversion(new ValueConverter<string, byte[]>
(
convertToProviderExpression: (str) => Encoding.UTF8.GetBytes(str),
convertFromProviderExpression: (bytes) => Encoding.UTF8.GetString(bytes)
));
}
builder.Entity<StoreData>()
.Property(o => o.DerivationStrategies)
.HasColumnType("JSONB");
}
}
}

View file

@ -31,20 +31,5 @@ public class StoreRole
entity.HasIndex(entity => new {entity.StoreDataId, entity.Role}).IsUnique();
});
if (!databaseFacade.IsNpgsql())
{
builder.Entity<StoreRole>()
.Property(o => o.Permissions)
.HasConversion(
v => JsonConvert.SerializeObject(v),
v => JsonConvert.DeserializeObject<List<string>>(v)?? new List<string>(),
new ValueComparer<List<string>>(
(c1, c2) => c1 ==c2 || c1 != null && c2 != null && c1.SequenceEqual(c2),
c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),
c => c.ToList()));
}
}
}

View file

@ -18,11 +18,9 @@ public class StoreSettingData
builder.Entity<StoreSettingData>()
.HasOne(o => o.Store)
.WithMany(o => o.Settings).OnDelete(DeleteBehavior.Cascade);
if (databaseFacade.IsNpgsql())
{
builder.Entity<StoreSettingData>()
.Property(o => o.Value)
.HasColumnType("JSONB");
}
builder.Entity<StoreSettingData>()
.Property(o => o.Value)
.HasColumnType("JSONB");
}
}

View file

@ -83,13 +83,9 @@ namespace BTCPayServer.Data
o.Id
});
if (databaseFacade.IsNpgsql())
{
builder.Entity<WalletObjectData>()
.Property(o => o.Data)
.HasColumnType("JSONB");
}
builder.Entity<WalletObjectData>()
.Property(o => o.Data)
.HasColumnType("JSONB");
}
public bool Equals(WalletObjectData x, WalletObjectData y)

View file

@ -50,12 +50,9 @@ namespace BTCPayServer.Data
.HasForeignKey(o => new { o.WalletId, o.BType, o.BId })
.OnDelete(DeleteBehavior.Cascade);
if (databaseFacade.IsNpgsql())
{
builder.Entity<WalletObjectLinkData>()
.Property(o => o.Data)
.HasColumnType("JSONB");
}
builder.Entity<WalletObjectLinkData>()
.Property(o => o.Data)
.HasColumnType("JSONB");
}
}
}

View file

@ -18,12 +18,9 @@ namespace BTCPayServer.Data
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
{
if (databaseFacade.IsNpgsql())
{
builder.Entity<WebhookData>()
.Property(o => o.Blob2)
.HasColumnType("JSONB");
}
builder.Entity<WebhookData>()
.Property(o => o.Blob2)
.HasColumnType("JSONB");
}
}
}

View file

@ -27,12 +27,9 @@ namespace BTCPayServer.Data
.WithMany(a => a.Deliveries).OnDelete(DeleteBehavior.Cascade);
builder.Entity<WebhookDeliveryData>().HasIndex(o => o.WebhookId);
builder.Entity<WebhookDeliveryData>().HasIndex(o => o.Timestamp);
if (databaseFacade.IsNpgsql())
{
builder.Entity<WebhookDeliveryData>()
.Property(o => o.Blob)
.HasColumnType("JSONB");
}
builder.Entity<WebhookDeliveryData>()
.Property(o => o.Blob)
.HasColumnType("JSONB");
}
}
}

View file

@ -11,12 +11,11 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.CreateTable(
name: "AspNetRoles",
columns: table => new
{
Id = table.Column<string>(nullable: false, maxLength: maxLength),
Id = table.Column<string>(nullable: false, maxLength: null),
ConcurrencyStamp = table.Column<string>(nullable: true),
Name = table.Column<string>(maxLength: 256, nullable: true),
NormalizedName = table.Column<string>(maxLength: 256, nullable: true)
@ -30,7 +29,7 @@ namespace BTCPayServer.Migrations
name: "AspNetUsers",
columns: table => new
{
Id = table.Column<string>(nullable: false, maxLength: maxLength),
Id = table.Column<string>(nullable: false, maxLength: null),
AccessFailedCount = table.Column<int>(nullable: false),
ConcurrencyStamp = table.Column<string>(nullable: true),
Email = table.Column<string>(maxLength: 256, nullable: true),
@ -55,7 +54,7 @@ namespace BTCPayServer.Migrations
name: "Stores",
columns: table => new
{
Id = table.Column<string>(nullable: false, maxLength: maxLength),
Id = table.Column<string>(nullable: false, maxLength: null),
DerivationStrategy = table.Column<string>(nullable: true),
SpeedPolicy = table.Column<int>(nullable: false),
StoreCertificate = table.Column<byte[]>(nullable: true),
@ -75,7 +74,7 @@ namespace BTCPayServer.Migrations
.Annotation("Sqlite:Autoincrement", true),
ClaimType = table.Column<string>(nullable: true),
ClaimValue = table.Column<string>(nullable: true),
RoleId = table.Column<string>(nullable: false, maxLength: maxLength)
RoleId = table.Column<string>(nullable: false, maxLength: null)
},
constraints: table =>
{
@ -96,7 +95,7 @@ namespace BTCPayServer.Migrations
.Annotation("Sqlite:Autoincrement", true),
ClaimType = table.Column<string>(nullable: true),
ClaimValue = table.Column<string>(nullable: true),
UserId = table.Column<string>(nullable: false, maxLength: maxLength)
UserId = table.Column<string>(nullable: false, maxLength: null)
},
constraints: table =>
{
@ -116,7 +115,7 @@ namespace BTCPayServer.Migrations
LoginProvider = table.Column<string>(nullable: false, maxLength: 255),
ProviderKey = table.Column<string>(nullable: false, maxLength: 255),
ProviderDisplayName = table.Column<string>(nullable: true),
UserId = table.Column<string>(nullable: false, maxLength: maxLength)
UserId = table.Column<string>(nullable: false, maxLength: null)
},
constraints: table =>
{
@ -133,8 +132,8 @@ namespace BTCPayServer.Migrations
name: "AspNetUserRoles",
columns: table => new
{
UserId = table.Column<string>(nullable: false, maxLength: maxLength),
RoleId = table.Column<string>(nullable: false, maxLength: maxLength)
UserId = table.Column<string>(nullable: false, maxLength: null),
RoleId = table.Column<string>(nullable: false, maxLength: null)
},
constraints: table =>
{
@ -157,7 +156,7 @@ namespace BTCPayServer.Migrations
name: "AspNetUserTokens",
columns: table => new
{
UserId = table.Column<string>(nullable: false, maxLength: maxLength),
UserId = table.Column<string>(nullable: false, maxLength: null),
LoginProvider = table.Column<string>(nullable: false, maxLength: 64),
Name = table.Column<string>(nullable: false, maxLength: 64),
Value = table.Column<string>(nullable: true)
@ -177,7 +176,7 @@ namespace BTCPayServer.Migrations
name: "Invoices",
columns: table => new
{
Id = table.Column<string>(nullable: false, maxLength: maxLength),
Id = table.Column<string>(nullable: false, maxLength: null),
Blob = table.Column<byte[]>(nullable: true),
Created = table.Column<DateTimeOffset>(nullable: false),
CustomerEmail = table.Column<string>(nullable: true),
@ -185,7 +184,7 @@ namespace BTCPayServer.Migrations
ItemCode = table.Column<string>(nullable: true),
OrderId = table.Column<string>(nullable: true),
Status = table.Column<string>(nullable: true),
StoreDataId = table.Column<string>(nullable: true, maxLength: maxLength)
StoreDataId = table.Column<string>(nullable: true, maxLength: null)
},
constraints: table =>
{
@ -202,8 +201,8 @@ namespace BTCPayServer.Migrations
name: "UserStore",
columns: table => new
{
ApplicationUserId = table.Column<string>(nullable: false, maxLength: maxLength),
StoreDataId = table.Column<string>(nullable: false, maxLength: maxLength),
ApplicationUserId = table.Column<string>(nullable: false, maxLength: null),
StoreDataId = table.Column<string>(nullable: false, maxLength: null),
Role = table.Column<string>(nullable: true)
},
constraints: table =>
@ -227,9 +226,9 @@ namespace BTCPayServer.Migrations
name: "Payments",
columns: table => new
{
Id = table.Column<string>(nullable: false, maxLength: maxLength),
Id = table.Column<string>(nullable: false, maxLength: null),
Blob = table.Column<byte[]>(nullable: true),
InvoiceDataId = table.Column<string>(nullable: true, maxLength: maxLength)
InvoiceDataId = table.Column<string>(nullable: true, maxLength: null)
},
constraints: table =>
{
@ -246,9 +245,9 @@ namespace BTCPayServer.Migrations
name: "RefundAddresses",
columns: table => new
{
Id = table.Column<string>(nullable: false, maxLength: maxLength),
Id = table.Column<string>(nullable: false, maxLength: null),
Blob = table.Column<byte[]>(nullable: true),
InvoiceDataId = table.Column<string>(nullable: true, maxLength: maxLength)
InvoiceDataId = table.Column<string>(nullable: true, maxLength: null)
},
constraints: table =>
{

View file

@ -10,12 +10,11 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.CreateTable(
migrationBuilder.CreateTable(
name: "Settings",
columns: table => new
{
Id = table.Column<string>(nullable: false, maxLength: maxLength),
Id = table.Column<string>(nullable: false, maxLength: null),
Value = table.Column<string>(nullable: true)
},
constraints: table =>

View file

@ -10,8 +10,7 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.AddColumn<bool>(
migrationBuilder.AddColumn<bool>(
name: "RequiresEmailConfirmation",
table: "AspNetUsers",
nullable: false,

View file

@ -10,13 +10,12 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.CreateTable(
migrationBuilder.CreateTable(
name: "AddressInvoices",
columns: table => new
{
Address = table.Column<string>(nullable: false, maxLength: this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)512 : null),
InvoiceDataId = table.Column<string>(nullable: true, maxLength: maxLength)
Address = table.Column<string>(nullable: false, maxLength: null),
InvoiceDataId = table.Column<string>(nullable: true, maxLength: null)
},
constraints: table =>
{

View file

@ -11,18 +11,17 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.CreateTable(
migrationBuilder.CreateTable(
name: "PairedSINData",
columns: table => new
{
Id = table.Column<string>(nullable: false, maxLength: maxLength),
Id = table.Column<string>(nullable: false, maxLength: null),
Facade = table.Column<string>(nullable: true),
Label = table.Column<string>(nullable: true),
Name = table.Column<string>(nullable: true),
PairingTime = table.Column<DateTimeOffset>(nullable: false),
SIN = table.Column<string>(nullable: true, maxLength: maxLength),
StoreDataId = table.Column<string>(nullable: true, maxLength: maxLength)
SIN = table.Column<string>(nullable: true, maxLength: null),
StoreDataId = table.Column<string>(nullable: true, maxLength: null)
},
constraints: table =>
{
@ -33,14 +32,14 @@ namespace BTCPayServer.Migrations
name: "PairingCodes",
columns: table => new
{
Id = table.Column<string>(nullable: false, maxLength: maxLength),
Id = table.Column<string>(nullable: false, maxLength: null),
DateCreated = table.Column<DateTime>(nullable: false),
Expiration = table.Column<DateTimeOffset>(nullable: false),
Facade = table.Column<string>(nullable: true),
Label = table.Column<string>(nullable: true),
Name = table.Column<string>(nullable: true),
SIN = table.Column<string>(nullable: true),
StoreDataId = table.Column<string>(nullable: true, maxLength: maxLength),
StoreDataId = table.Column<string>(nullable: true, maxLength: null),
TokenValue = table.Column<string>(nullable: true)
},
constraints: table =>

View file

@ -10,22 +10,18 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
if (this.SupportDropColumn(migrationBuilder.ActiveProvider))
{
migrationBuilder.DropColumn(
name: "Name",
table: "PairingCodes");
migrationBuilder.DropColumn(
name: "Name",
table: "PairingCodes");
migrationBuilder.DropColumn(
name: "Name",
table: "PairedSINData");
}
migrationBuilder.DropColumn(
name: "Name",
table: "PairedSINData");
migrationBuilder.CreateTable(
name: "PendingInvoices",
columns: table => new
{
Id = table.Column<string>(nullable: false, maxLength: maxLength)
Id = table.Column<string>(nullable: false, maxLength: null)
},
constraints: table =>
{

View file

@ -10,8 +10,7 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.AddColumn<byte[]>(
migrationBuilder.AddColumn<byte[]>(
name: "StoreBlob",
table: "Stores",
nullable: true);

View file

@ -11,7 +11,6 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.AddColumn<DateTimeOffset>(
name: "CreatedTime",
table: "AddressInvoices",
@ -21,8 +20,8 @@ namespace BTCPayServer.Migrations
name: "HistoricalAddressInvoices",
columns: table => new
{
InvoiceDataId = table.Column<string>(nullable: false, maxLength: maxLength),
Address = table.Column<string>(nullable: false, maxLength: this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)512 : null),
InvoiceDataId = table.Column<string>(nullable: false, maxLength: null),
Address = table.Column<string>(nullable: false, maxLength: null),
Assigned = table.Column<DateTimeOffset>(nullable: false),
UnAssigned = table.Column<DateTimeOffset>(nullable: true)
},

View file

@ -10,8 +10,7 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.AddColumn<bool>(
migrationBuilder.AddColumn<bool>(
name: "Accounted",
table: "Payments",
nullable: false,

View file

@ -10,8 +10,7 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.AddColumn<string>(
migrationBuilder.AddColumn<string>(
name: "CryptoCode",
table: "HistoricalAddressInvoices",
nullable: true);

View file

@ -10,8 +10,7 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.AddColumn<string>(
migrationBuilder.AddColumn<string>(
name: "DerivationStrategies",
table: "Stores",
nullable: true);

View file

@ -10,8 +10,7 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.AddColumn<string>(
migrationBuilder.AddColumn<string>(
name: "DefaultCrypto",
table: "Stores",
nullable: true);

View file

@ -11,13 +11,12 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.CreateTable(
migrationBuilder.CreateTable(
name: "InvoiceEvents",
columns: table => new
{
InvoiceDataId = table.Column<string>(nullable: false, maxLength: maxLength),
UniqueId = table.Column<string>(nullable: false, maxLength: maxLength),
InvoiceDataId = table.Column<string>(nullable: false, maxLength: null),
UniqueId = table.Column<string>(nullable: false, maxLength: null),
Message = table.Column<string>(nullable: true),
Timestamp = table.Column<DateTimeOffset>(nullable: false)
},

View file

@ -11,17 +11,16 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.CreateTable(
migrationBuilder.CreateTable(
name: "Apps",
columns: table => new
{
Id = table.Column<string>(nullable: false, maxLength: maxLength),
Id = table.Column<string>(nullable: false, maxLength: null),
AppType = table.Column<string>(nullable: true),
Created = table.Column<DateTimeOffset>(nullable: false),
Name = table.Column<string>(nullable: true),
Settings = table.Column<string>(nullable: true),
StoreDataId = table.Column<string>(nullable: true, maxLength: maxLength)
StoreDataId = table.Column<string>(nullable: true, maxLength: null)
},
constraints: table =>
{

View file

@ -10,8 +10,7 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.CreateTable(
migrationBuilder.CreateTable(
name: "ApiKeys",
columns: table => new
{

View file

@ -10,93 +10,89 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
if (this.SupportDropForeignKey(migrationBuilder.ActiveProvider))
{
migrationBuilder.DropForeignKey(
migrationBuilder.DropForeignKey(
name: "FK_AddressInvoices_Invoices_InvoiceDataId",
table: "AddressInvoices");
migrationBuilder.DropForeignKey(
name: "FK_Apps_Stores_StoreDataId",
table: "Apps");
migrationBuilder.DropForeignKey(
name: "FK_Invoices_Stores_StoreDataId",
table: "Invoices");
migrationBuilder.DropForeignKey(
name: "FK_Payments_Invoices_InvoiceDataId",
table: "Payments");
migrationBuilder.DropForeignKey(
name: "FK_RefundAddresses_Invoices_InvoiceDataId",
table: "RefundAddresses");
migrationBuilder.AddForeignKey(
name: "FK_AddressInvoices_Invoices_InvoiceDataId",
table: "AddressInvoices");
table: "AddressInvoices",
column: "InvoiceDataId",
principalTable: "Invoices",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.DropForeignKey(
name: "FK_Apps_Stores_StoreDataId",
table: "Apps");
migrationBuilder.AddForeignKey(
name: "FK_ApiKeys_Stores_StoreId",
table: "ApiKeys",
column: "StoreId",
principalTable: "Stores",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.DropForeignKey(
name: "FK_Invoices_Stores_StoreDataId",
table: "Invoices");
migrationBuilder.AddForeignKey(
name: "FK_Apps_Stores_StoreDataId",
table: "Apps",
column: "StoreDataId",
principalTable: "Stores",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.DropForeignKey(
name: "FK_Payments_Invoices_InvoiceDataId",
table: "Payments");
migrationBuilder.AddForeignKey(
name: "FK_Invoices_Stores_StoreDataId",
table: "Invoices",
column: "StoreDataId",
principalTable: "Stores",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.DropForeignKey(
name: "FK_RefundAddresses_Invoices_InvoiceDataId",
table: "RefundAddresses");
migrationBuilder.AddForeignKey(
name: "FK_PairedSINData_Stores_StoreDataId",
table: "PairedSINData",
column: "StoreDataId",
principalTable: "Stores",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_AddressInvoices_Invoices_InvoiceDataId",
table: "AddressInvoices",
column: "InvoiceDataId",
principalTable: "Invoices",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_Payments_Invoices_InvoiceDataId",
table: "Payments",
column: "InvoiceDataId",
principalTable: "Invoices",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_ApiKeys_Stores_StoreId",
table: "ApiKeys",
column: "StoreId",
principalTable: "Stores",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_PendingInvoices_Invoices_Id",
table: "PendingInvoices",
column: "Id",
principalTable: "Invoices",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_Apps_Stores_StoreDataId",
table: "Apps",
column: "StoreDataId",
principalTable: "Stores",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_Invoices_Stores_StoreDataId",
table: "Invoices",
column: "StoreDataId",
principalTable: "Stores",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_PairedSINData_Stores_StoreDataId",
table: "PairedSINData",
column: "StoreDataId",
principalTable: "Stores",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_Payments_Invoices_InvoiceDataId",
table: "Payments",
column: "InvoiceDataId",
principalTable: "Invoices",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_PendingInvoices_Invoices_Id",
table: "PendingInvoices",
column: "Id",
principalTable: "Invoices",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_RefundAddresses_Invoices_InvoiceDataId",
table: "RefundAddresses",
column: "InvoiceDataId",
principalTable: "Invoices",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
migrationBuilder.AddForeignKey(
name: "FK_RefundAddresses_Invoices_InvoiceDataId",
table: "RefundAddresses",
column: "InvoiceDataId",
principalTable: "Invoices",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
protected override void Down(MigrationBuilder migrationBuilder)

View file

@ -10,13 +10,12 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.CreateTable(
migrationBuilder.CreateTable(
name: "PaymentRequests",
columns: table => new
{
Id = table.Column<string>(nullable: false, maxLength: maxLength),
StoreDataId = table.Column<string>(nullable: true, maxLength: maxLength),
Id = table.Column<string>(nullable: false, maxLength: null),
StoreDataId = table.Column<string>(nullable: true, maxLength: null),
Status = table.Column<int>(nullable: false),
Blob = table.Column<byte[]>(nullable: true)
},

View file

@ -10,8 +10,7 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.AddColumn<bool>(
migrationBuilder.AddColumn<bool>(
name: "TagAllInvoices",
table: "Apps",
nullable: false,

View file

@ -11,8 +11,7 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.CreateTable(
migrationBuilder.CreateTable(
name: "OpenIddictApplications",
columns: table => new
{
@ -21,13 +20,13 @@ namespace BTCPayServer.Migrations
ConcurrencyToken = table.Column<string>(maxLength: 50, nullable: true),
ConsentType = table.Column<string>(nullable: true),
DisplayName = table.Column<string>(nullable: true),
Id = table.Column<string>(nullable: false, maxLength: maxLength),
Id = table.Column<string>(nullable: false, maxLength: null),
Permissions = table.Column<string>(nullable: true),
PostLogoutRedirectUris = table.Column<string>(nullable: true),
Properties = table.Column<string>(nullable: true),
RedirectUris = table.Column<string>(nullable: true),
Type = table.Column<string>(maxLength: 25, nullable: false),
ApplicationUserId = table.Column<string>(nullable: true, maxLength: maxLength)
ApplicationUserId = table.Column<string>(nullable: true, maxLength: null)
},
constraints: table =>
{
@ -47,7 +46,7 @@ namespace BTCPayServer.Migrations
ConcurrencyToken = table.Column<string>(maxLength: 50, nullable: true),
Description = table.Column<string>(nullable: true),
DisplayName = table.Column<string>(nullable: true),
Id = table.Column<string>(nullable: false, maxLength: maxLength),
Id = table.Column<string>(nullable: false, maxLength: null),
Name = table.Column<string>(maxLength: 200, nullable: false),
Properties = table.Column<string>(nullable: true),
Resources = table.Column<string>(nullable: true)
@ -61,9 +60,9 @@ namespace BTCPayServer.Migrations
name: "OpenIddictAuthorizations",
columns: table => new
{
ApplicationId = table.Column<string>(nullable: true, maxLength: maxLength),
ApplicationId = table.Column<string>(nullable: true, maxLength: null),
ConcurrencyToken = table.Column<string>(maxLength: 50, nullable: true),
Id = table.Column<string>(nullable: false, maxLength: maxLength),
Id = table.Column<string>(nullable: false, maxLength: null),
Properties = table.Column<string>(nullable: true),
Scopes = table.Column<string>(nullable: true),
Status = table.Column<string>(maxLength: 25, nullable: false),
@ -85,12 +84,12 @@ namespace BTCPayServer.Migrations
name: "OpenIddictTokens",
columns: table => new
{
ApplicationId = table.Column<string>(nullable: true, maxLength: maxLength),
AuthorizationId = table.Column<string>(nullable: true, maxLength: maxLength),
ApplicationId = table.Column<string>(nullable: true, maxLength: null),
AuthorizationId = table.Column<string>(nullable: true, maxLength: null),
ConcurrencyToken = table.Column<string>(maxLength: 50, nullable: true),
CreationDate = table.Column<DateTimeOffset>(nullable: true),
ExpirationDate = table.Column<DateTimeOffset>(nullable: true),
Id = table.Column<string>(nullable: false, maxLength: maxLength),
Id = table.Column<string>(nullable: false, maxLength: null),
Payload = table.Column<string>(nullable: true),
Properties = table.Column<string>(nullable: true),
ReferenceId = table.Column<string>(maxLength: 100, nullable: true),

View file

@ -11,16 +11,15 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.CreateTable(
migrationBuilder.CreateTable(
name: "Files",
columns: table => new
{
Id = table.Column<string>(nullable: false, maxLength: maxLength),
Id = table.Column<string>(nullable: false, maxLength: null),
FileName = table.Column<string>(nullable: true),
StorageFileName = table.Column<string>(nullable: true),
Timestamp = table.Column<DateTime>(nullable: false),
ApplicationUserId = table.Column<string>(nullable: true, maxLength: maxLength)
ApplicationUserId = table.Column<string>(nullable: true, maxLength: null)
},
constraints: table =>
{

View file

@ -10,25 +10,21 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
if (this.SupportDropColumn(migrationBuilder.ActiveProvider))
{
migrationBuilder.DropColumn(
name: "Facade",
table: "PairedSINData");
}
migrationBuilder.DropColumn(
name: "Facade",
table: "PairedSINData");
migrationBuilder.CreateTable(
name: "U2FDevices",
columns: table => new
{
Id = table.Column<string>(nullable: false, maxLength: maxLength),
Id = table.Column<string>(nullable: false, maxLength: null),
Name = table.Column<string>(nullable: true),
KeyHandle = table.Column<byte[]>(nullable: false),
PublicKey = table.Column<byte[]>(nullable: false),
AttestationCert = table.Column<byte[]>(nullable: false),
Counter = table.Column<int>(nullable: false),
ApplicationUserId = table.Column<string>(nullable: true, maxLength: maxLength)
ApplicationUserId = table.Column<string>(nullable: true, maxLength: null)
},
constraints: table =>
{
@ -52,13 +48,10 @@ namespace BTCPayServer.Migrations
migrationBuilder.DropTable(
name: "U2FDevices");
//if it did not support dropping it, then it is still here and re-adding it would throw
if (this.SupportDropColumn(migrationBuilder.ActiveProvider))
{
migrationBuilder.AddColumn<string>(
name: "Facade",
table: "PairedSINData",
nullable: true);
}
migrationBuilder.AddColumn<string>(
name: "Facade",
table: "PairedSINData",
nullable: true);
}
}
}

View file

@ -11,8 +11,7 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.AddColumn<DateTimeOffset>(
migrationBuilder.AddColumn<DateTimeOffset>(
name: "Created",
table: "PaymentRequests",
nullable: false,

View file

@ -10,12 +10,11 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.CreateTable(
migrationBuilder.CreateTable(
name: "Wallets",
columns: table => new
{
Id = table.Column<string>(nullable: false, maxLength: maxLength),
Id = table.Column<string>(nullable: false, maxLength: null),
Blob = table.Column<byte[]>(nullable: true)
},
constraints: table =>
@ -27,8 +26,8 @@ namespace BTCPayServer.Migrations
name: "WalletTransactions",
columns: table => new
{
WalletDataId = table.Column<string>(nullable: false, maxLength: maxLength),
TransactionId = table.Column<string>(nullable: false, maxLength: maxLength),
WalletDataId = table.Column<string>(nullable: false, maxLength: null),
TransactionId = table.Column<string>(nullable: false, maxLength: null),
Labels = table.Column<string>(nullable: true),
Blob = table.Column<byte[]>(nullable: true)
},

View file

@ -11,91 +11,21 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
if (!migrationBuilder.IsSqlite())
{
migrationBuilder.AlterColumn<string>(
name: "Subject",
table: "OpenIddictTokens",
maxLength: 450,
nullable: true,
oldClrType: typeof(string),
oldMaxLength: 450);
migrationBuilder.AlterColumn<string>(
name: "Subject",
table: "OpenIddictTokens",
maxLength: 450,
nullable: true,
oldClrType: typeof(string),
oldMaxLength: 450);
migrationBuilder.AlterColumn<string>(
name: "Subject",
table: "OpenIddictAuthorizations",
maxLength: 450,
nullable: true,
oldClrType: typeof(string),
oldMaxLength: 450);
}
else
{
ReplaceOldTable(migrationBuilder, s =>
{
migrationBuilder.CreateTable(
name: s,
columns: table => new
{
ApplicationId = table.Column<string>(nullable: true, maxLength: null),
AuthorizationId = table.Column<string>(nullable: true, maxLength: null),
ConcurrencyToken = table.Column<string>(maxLength: 50, nullable: true),
CreationDate = table.Column<DateTimeOffset>(nullable: true),
ExpirationDate = table.Column<DateTimeOffset>(nullable: true),
Id = table.Column<string>(nullable: false, maxLength: null),
Payload = table.Column<string>(nullable: true),
Properties = table.Column<string>(nullable: true),
ReferenceId = table.Column<string>(maxLength: 100, nullable: true),
Status = table.Column<string>(maxLength: 25, nullable: false),
Subject = table.Column<string>(maxLength: 450, nullable: true),
Type = table.Column<string>(maxLength: 25, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_OpenIddictTokens", x => x.Id);
table.ForeignKey(
name: "FK_OpenIddictTokens_OpenIddictApplications_ApplicationId",
column: x => x.ApplicationId,
principalTable: "OpenIddictApplications",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_OpenIddictTokens_OpenIddictAuthorizations_AuthorizationId",
column: x => x.AuthorizationId,
principalTable: "OpenIddictAuthorizations",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
}, "OpenIddictTokens");
ReplaceOldTable(migrationBuilder, s =>
{
migrationBuilder.CreateTable(
name: s,
columns: table => new
{
ApplicationId = table.Column<string>(nullable: true, maxLength: null),
ConcurrencyToken = table.Column<string>(maxLength: 50, nullable: true),
Id = table.Column<string>(nullable: false, maxLength: null),
Properties = table.Column<string>(nullable: true),
Scopes = table.Column<string>(nullable: true),
Status = table.Column<string>(maxLength: 25, nullable: false),
Subject = table.Column<string>(maxLength: 450, nullable: true),
Type = table.Column<string>(maxLength: 25, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_OpenIddictAuthorizations", x => x.Id);
table.ForeignKey(
name: "FK_OpenIddictAuthorizations_OpenIddictApplications_ApplicationId",
column: x => x.ApplicationId,
principalTable: "OpenIddictApplications",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
}, "OpenIddictAuthorizations");
}
migrationBuilder.AlterColumn<string>(
name: "Subject",
table: "OpenIddictAuthorizations",
maxLength: 450,
nullable: true,
oldClrType: typeof(string),
oldMaxLength: 450);
migrationBuilder.AddColumn<string>(
name: "Requirements",
@ -105,140 +35,27 @@ namespace BTCPayServer.Migrations
protected override void Down(MigrationBuilder migrationBuilder)
{
if (!migrationBuilder.IsSqlite())
{
migrationBuilder.DropColumn(
name: "Requirements",
table: "OpenIddictApplications");
migrationBuilder.DropColumn(
name: "Requirements",
table: "OpenIddictApplications");
migrationBuilder.AlterColumn<string>(
name: "Subject",
table: "OpenIddictTokens",
maxLength: 450,
nullable: false,
oldClrType: typeof(string),
oldMaxLength: 450,
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "Subject",
table: "OpenIddictTokens",
maxLength: 450,
nullable: false,
oldClrType: typeof(string),
oldMaxLength: 450,
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "Subject",
table: "OpenIddictAuthorizations",
maxLength: 450,
nullable: false,
oldClrType: typeof(string),
oldMaxLength: 450,
oldNullable: true);
}
else
{
ReplaceOldTable(migrationBuilder, s =>
{
migrationBuilder.CreateTable(
name: s,
columns: table => new
{
ApplicationId = table.Column<string>(nullable: true, maxLength: null),
AuthorizationId = table.Column<string>(nullable: true, maxLength: null),
ConcurrencyToken = table.Column<string>(maxLength: 50, nullable: true),
CreationDate = table.Column<DateTimeOffset>(nullable: true),
ExpirationDate = table.Column<DateTimeOffset>(nullable: true),
Id = table.Column<string>(nullable: false, maxLength: null),
Payload = table.Column<string>(nullable: true),
Properties = table.Column<string>(nullable: true),
ReferenceId = table.Column<string>(maxLength: 100, nullable: true),
Status = table.Column<string>(maxLength: 25, nullable: false),
Subject = table.Column<string>(maxLength: 450, nullable: false),
Type = table.Column<string>(maxLength: 25, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_OpenIddictTokens", x => x.Id);
table.ForeignKey(
name: "FK_OpenIddictTokens_OpenIddictApplications_ApplicationId",
column: x => x.ApplicationId,
principalTable: "OpenIddictApplications",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_OpenIddictTokens_OpenIddictAuthorizations_AuthorizationId",
column: x => x.AuthorizationId,
principalTable: "OpenIddictAuthorizations",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
}, "OpenIddictTokens", "WHERE Subject IS NOT NULL");
ReplaceOldTable(migrationBuilder, s =>
{
migrationBuilder.CreateTable(
name: s,
columns: table => new
{
ApplicationId = table.Column<string>(nullable: true, maxLength: null),
ConcurrencyToken = table.Column<string>(maxLength: 50, nullable: true),
Id = table.Column<string>(nullable: false, maxLength: null),
Properties = table.Column<string>(nullable: true),
Scopes = table.Column<string>(nullable: true),
Status = table.Column<string>(maxLength: 25, nullable: false),
Subject = table.Column<string>(maxLength: 450, nullable: false),
Type = table.Column<string>(maxLength: 25, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_OpenIddictAuthorizations", x => x.Id);
table.ForeignKey(
name: "FK_OpenIddictAuthorizations_OpenIddictApplications_ApplicationId",
column: x => x.ApplicationId,
principalTable: "OpenIddictApplications",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
}, "OpenIddictAuthorizations", "WHERE Subject IS NOT NULL");
ReplaceOldTable(migrationBuilder, s =>
{
migrationBuilder.CreateTable(
name: s,
columns: table => new
{
ClientId = table.Column<string>(maxLength: 100, nullable: false),
ClientSecret = table.Column<string>(nullable: true),
ConcurrencyToken = table.Column<string>(maxLength: 50, nullable: true),
ConsentType = table.Column<string>(nullable: true),
DisplayName = table.Column<string>(nullable: true),
Id = table.Column<string>(nullable: false, maxLength: null),
Permissions = table.Column<string>(nullable: true),
PostLogoutRedirectUris = table.Column<string>(nullable: true),
Properties = table.Column<string>(nullable: true),
RedirectUris = table.Column<string>(nullable: true),
Type = table.Column<string>(maxLength: 25, nullable: false),
ApplicationUserId = table.Column<string>(nullable: true, maxLength: null)
},
constraints: table =>
{
table.PrimaryKey("PK_OpenIddictApplications", x => x.Id);
table.ForeignKey(
name: "FK_OpenIddictApplications_AspNetUsers_ApplicationUserId",
column: x => x.ApplicationUserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
}, "OpenIddictApplications", "",
"ClientId, ClientSecret, ConcurrencyToken, ConsentType, DisplayName, Id, Permissions, PostLogoutRedirectUris, Properties, RedirectUris, Type, ApplicationUserId");
}
}
private void ReplaceOldTable(MigrationBuilder migrationBuilder, Action<string> createTable, string tableName,
string whereClause = "", string columns = "*")
{
createTable.Invoke($"New_{tableName}");
migrationBuilder.Sql(
$"INSERT INTO New_{tableName} {(columns == "*" ? string.Empty : $"({columns})")}SELECT {columns} FROM {tableName} {whereClause};");
migrationBuilder.Sql("PRAGMA foreign_keys=\"0\"", true);
migrationBuilder.Sql($"DROP TABLE {tableName}", true);
migrationBuilder.Sql($"ALTER TABLE New_{tableName} RENAME TO {tableName}", true);
migrationBuilder.Sql("PRAGMA foreign_keys=\"1\"", true);
migrationBuilder.AlterColumn<string>(
name: "Subject",
table: "OpenIddictAuthorizations",
maxLength: 450,
nullable: false,
oldClrType: typeof(string),
oldMaxLength: 450,
oldNullable: true);
}
}
}

View file

@ -31,44 +31,37 @@ namespace BTCPayServer.Migrations
name: "IX_ApiKeys_UserId",
table: "ApiKeys",
column: "UserId");
if (this.SupportAddForeignKey(migrationBuilder.ActiveProvider))
{
migrationBuilder.AddForeignKey(
name: "FK_ApiKeys_AspNetUsers_UserId",
table: "ApiKeys",
column: "UserId",
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
migrationBuilder.AddForeignKey(
name: "FK_ApiKeys_AspNetUsers_UserId",
table: "ApiKeys",
column: "UserId",
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
if (this.SupportDropForeignKey(migrationBuilder.ActiveProvider))
{
migrationBuilder.DropForeignKey(
name: "FK_ApiKeys_AspNetUsers_UserId",
table: "ApiKeys");
}
migrationBuilder.DropForeignKey(
name: "FK_ApiKeys_AspNetUsers_UserId",
table: "ApiKeys");
migrationBuilder.DropIndex(
name: "IX_ApiKeys_UserId",
table: "ApiKeys");
if (this.SupportDropColumn(migrationBuilder.ActiveProvider))
{
migrationBuilder.DropColumn(
name: "Permissions",
table: "ApiKeys");
migrationBuilder.DropColumn(
name: "Type",
table: "ApiKeys");
migrationBuilder.DropColumn(
name: "Permissions",
table: "ApiKeys");
migrationBuilder.DropColumn(
name: "UserId",
table: "ApiKeys");
}
migrationBuilder.DropColumn(
name: "Type",
table: "ApiKeys");
migrationBuilder.DropColumn(
name: "UserId",
table: "ApiKeys");
}
}
}

View file

@ -26,13 +26,12 @@ namespace BTCPayServer.Migrations
protected override void Down(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.CreateTable(
migrationBuilder.CreateTable(
name: "OpenIddictApplications",
columns: table => new
{
Id = table.Column<string>(type: "TEXT", nullable: false, maxLength: maxLength),
ApplicationUserId = table.Column<string>(type: "TEXT", nullable: true, maxLength: maxLength),
Id = table.Column<string>(type: "TEXT", nullable: false, maxLength: null),
ApplicationUserId = table.Column<string>(type: "TEXT", nullable: true, maxLength: null),
ClientId = table.Column<string>(type: "TEXT", maxLength: 100, nullable: false),
ClientSecret = table.Column<string>(type: "TEXT", nullable: true),
ConcurrencyToken = table.Column<string>(type: "TEXT", maxLength: 50, nullable: true),
@ -60,7 +59,7 @@ namespace BTCPayServer.Migrations
name: "OpenIddictScopes",
columns: table => new
{
Id = table.Column<string>(type: "TEXT", nullable: false, maxLength: maxLength),
Id = table.Column<string>(type: "TEXT", nullable: false, maxLength: null),
ConcurrencyToken = table.Column<string>(type: "TEXT", maxLength: 50, nullable: true),
Description = table.Column<string>(type: "TEXT", nullable: true),
DisplayName = table.Column<string>(type: "TEXT", nullable: true),
@ -77,8 +76,8 @@ namespace BTCPayServer.Migrations
name: "OpenIddictAuthorizations",
columns: table => new
{
Id = table.Column<string>(type: "TEXT", nullable: false, maxLength: maxLength),
ApplicationId = table.Column<string>(type: "TEXT", nullable: true, maxLength: maxLength),
Id = table.Column<string>(type: "TEXT", nullable: false, maxLength: null),
ApplicationId = table.Column<string>(type: "TEXT", nullable: true, maxLength: null),
ConcurrencyToken = table.Column<string>(type: "TEXT", maxLength: 50, nullable: true),
Properties = table.Column<string>(type: "TEXT", nullable: true),
Scopes = table.Column<string>(type: "TEXT", nullable: true),
@ -101,9 +100,9 @@ namespace BTCPayServer.Migrations
name: "OpenIddictTokens",
columns: table => new
{
Id = table.Column<string>(type: "TEXT", nullable: false, maxLength: maxLength),
ApplicationId = table.Column<string>(type: "TEXT", nullable: true, maxLength: maxLength),
AuthorizationId = table.Column<string>(type: "TEXT", nullable: true, maxLength: maxLength),
Id = table.Column<string>(type: "TEXT", nullable: false, maxLength: null),
ApplicationId = table.Column<string>(type: "TEXT", nullable: true, maxLength: null),
AuthorizationId = table.Column<string>(type: "TEXT", nullable: true, maxLength: null),
ConcurrencyToken = table.Column<string>(type: "TEXT", maxLength: 50, nullable: true),
CreationDate = table.Column<DateTimeOffset>(type: "TEXT", nullable: true),
ExpirationDate = table.Column<DateTimeOffset>(type: "TEXT", nullable: true),

View file

@ -10,12 +10,9 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
if (this.SupportDropColumn(migrationBuilder.ActiveProvider))
{
migrationBuilder.DropColumn(
name: "Permissions",
table: "ApiKeys");
}
migrationBuilder.DropColumn(
name: "Permissions",
table: "ApiKeys");
migrationBuilder.AddColumn<byte[]>(
name: "Blob",
@ -25,12 +22,9 @@ namespace BTCPayServer.Migrations
protected override void Down(MigrationBuilder migrationBuilder)
{
if (this.SupportDropColumn(migrationBuilder.ActiveProvider))
{
migrationBuilder.DropColumn(
name: "Blob",
table: "ApiKeys");
}
migrationBuilder.DropColumn(
name: "Blob",
table: "ApiKeys");
migrationBuilder.AddColumn<string>(
name: "Permissions",

View file

@ -24,15 +24,12 @@ namespace BTCPayServer.Migrations
protected override void Down(MigrationBuilder migrationBuilder)
{
if (this.SupportDropColumn(migrationBuilder.ActiveProvider))
{
migrationBuilder.DropColumn(
name: "Archived",
table: "Invoices");
migrationBuilder.DropColumn(
name: "Archived",
table: "PaymentRequests");
}
migrationBuilder.DropColumn(
name: "Archived",
table: "Invoices");
migrationBuilder.DropColumn(
name: "Archived",
table: "PaymentRequests");
}
}
}

View file

@ -11,8 +11,7 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.DropTable(
name: "RefundAddresses");
@ -20,7 +19,7 @@ namespace BTCPayServer.Migrations
name: "CurrentRefundId",
table: "Invoices",
nullable: true,
maxLength: maxLength);
maxLength: null);
migrationBuilder.CreateTable(
name: "Notifications",
@ -76,7 +75,7 @@ namespace BTCPayServer.Migrations
PullPaymentDataId = table.Column<string>(maxLength: 30, nullable: true),
State = table.Column<string>(maxLength: 20, nullable: false),
PaymentMethodId = table.Column<string>(maxLength: 20, nullable: false),
Destination = table.Column<string>(maxLength: maxLength, nullable: true),
Destination = table.Column<string>(maxLength: null, nullable: true),
Blob = table.Column<byte[]>(nullable: true),
Proof = table.Column<byte[]>(nullable: true)
},
@ -95,8 +94,8 @@ namespace BTCPayServer.Migrations
name: "Refunds",
columns: table => new
{
InvoiceDataId = table.Column<string>(maxLength: maxLength, nullable: false),
PullPaymentDataId = table.Column<string>(maxLength: maxLength, nullable: false)
InvoiceDataId = table.Column<string>(maxLength: null, nullable: false),
PullPaymentDataId = table.Column<string>(maxLength: null, nullable: false)
},
constraints: table =>
{
@ -151,14 +150,13 @@ namespace BTCPayServer.Migrations
table: "Refunds",
column: "PullPaymentDataId");
if (this.SupportAddForeignKey(this.ActiveProvider))
migrationBuilder.AddForeignKey(
name: "FK_Invoices_Refunds_Id_CurrentRefundId",
table: "Invoices",
columns: new[] { "Id", "CurrentRefundId" },
principalTable: "Refunds",
principalColumns: new[] { "InvoiceDataId", "PullPaymentDataId" },
onDelete: ReferentialAction.Restrict);
migrationBuilder.AddForeignKey(
name: "FK_Invoices_Refunds_Id_CurrentRefundId",
table: "Invoices",
columns: new[] { "Id", "CurrentRefundId" },
principalTable: "Refunds",
principalColumns: new[] { "InvoiceDataId", "PullPaymentDataId" },
onDelete: ReferentialAction.Restrict);
}
protected override void Down(MigrationBuilder migrationBuilder)

View file

@ -20,13 +20,9 @@ namespace BTCPayServer.Migrations
protected override void Down(MigrationBuilder migrationBuilder)
{
if (this.SupportDropColumn(migrationBuilder.ActiveProvider))
{
migrationBuilder.DropColumn(
name: "Severity",
table: "InvoiceEvents");
}
migrationBuilder.DropColumn(
name: "Severity",
table: "InvoiceEvents");
}
}
}

View file

@ -19,13 +19,9 @@ namespace BTCPayServer.Migrations
protected override void Down(MigrationBuilder migrationBuilder)
{
if (this.SupportDropColumn(migrationBuilder.ActiveProvider))
{
migrationBuilder.DropColumn(
name: "Created",
table: "AspNetUsers");
}
migrationBuilder.DropColumn(
name: "Created",
table: "AspNetUsers");
}
}
}

View file

@ -10,38 +10,32 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
if (this.SupportDropForeignKey(migrationBuilder.ActiveProvider))
{
migrationBuilder.DropForeignKey(
name: "FK_U2FDevices_AspNetUsers_ApplicationUserId",
table: "U2FDevices");
migrationBuilder.DropForeignKey(
name: "FK_U2FDevices_AspNetUsers_ApplicationUserId",
table: "U2FDevices");
migrationBuilder.AddForeignKey(
name: "FK_U2FDevices_AspNetUsers_ApplicationUserId",
table: "U2FDevices",
column: "ApplicationUserId",
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
migrationBuilder.AddForeignKey(
name: "FK_U2FDevices_AspNetUsers_ApplicationUserId",
table: "U2FDevices",
column: "ApplicationUserId",
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
if (this.SupportDropForeignKey(migrationBuilder.ActiveProvider))
{
migrationBuilder.DropForeignKey(
name: "FK_U2FDevices_AspNetUsers_ApplicationUserId",
table: "U2FDevices");
migrationBuilder.DropForeignKey(
name: "FK_U2FDevices_AspNetUsers_ApplicationUserId",
table: "U2FDevices");
migrationBuilder.AddForeignKey(
name: "FK_U2FDevices_AspNetUsers_ApplicationUserId",
table: "U2FDevices",
column: "ApplicationUserId",
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
}
migrationBuilder.AddForeignKey(
name: "FK_U2FDevices_AspNetUsers_ApplicationUserId",
table: "U2FDevices",
column: "ApplicationUserId",
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
}
}
}

View file

@ -10,15 +10,12 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
if (!migrationBuilder.IsSqlite())
{
migrationBuilder.AlterColumn<string>(
name: "OrderId",
table: "Invoices",
maxLength: 100,
nullable: true,
oldClrType: typeof(string));
}
migrationBuilder.AlterColumn<string>(
name: "OrderId",
table: "Invoices",
maxLength: 100,
nullable: true,
oldClrType: typeof(string));
migrationBuilder.CreateIndex(
name: "IX_Invoices_OrderId",

View file

@ -11,14 +11,13 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.CreateTable(
migrationBuilder.CreateTable(
name: "Fido2Credentials",
columns: table => new
{
Id = table.Column<string>(nullable: false, maxLength: maxLength),
Id = table.Column<string>(nullable: false, maxLength: null),
Name = table.Column<string>(nullable: true),
ApplicationUserId = table.Column<string>(nullable: true, maxLength: maxLength),
ApplicationUserId = table.Column<string>(nullable: true, maxLength: null),
Blob = table.Column<byte[]>(nullable: true),
Type = table.Column<int>(nullable: false)
},

View file

@ -17,19 +17,18 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.AddColumn<string>(
name: "StoreDataId",
table: "Payouts",
nullable: true,
maxLength: maxLength);
name: "StoreDataId",
table: "Payouts",
nullable: true,
maxLength: null);
migrationBuilder.CreateTable(
name: "PayoutProcessors",
columns: table => new
{
Id = table.Column<string>(nullable: false, maxLength: maxLength),
StoreId = table.Column<string>(nullable: true, maxLength: maxLength),
Id = table.Column<string>(nullable: false, maxLength: null),
StoreId = table.Column<string>(nullable: true, maxLength: null),
PaymentMethod = table.Column<string>(nullable: true),
Processor = table.Column<string>(nullable: true),
Blob = table.Column<byte[]>(nullable: true)
@ -54,40 +53,31 @@ namespace BTCPayServer.Migrations
name: "IX_PayoutProcessors_StoreId",
table: "PayoutProcessors",
column: "StoreId");
if (this.SupportAddForeignKey(ActiveProvider))
{
migrationBuilder.AddForeignKey(
name: "FK_Payouts_Stores_StoreDataId",
table: "Payouts",
column: "StoreDataId",
principalTable: "Stores",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
migrationBuilder.AddForeignKey(
name: "FK_Payouts_Stores_StoreDataId",
table: "Payouts",
column: "StoreDataId",
principalTable: "Stores",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
if (this.SupportDropForeignKey(ActiveProvider))
{
migrationBuilder.DropForeignKey(
name: "FK_Payouts_Stores_StoreDataId",
table: "Payouts");
migrationBuilder.DropForeignKey(
name: "FK_Payouts_Stores_StoreDataId",
table: "Payouts");
migrationBuilder.DropTable(
name: "PayoutProcessors");
migrationBuilder.DropIndex(
name: "IX_Payouts_StoreDataId",
table: "Payouts");
}
if(this.SupportDropColumn(ActiveProvider))
{
migrationBuilder.DropColumn(
name: "StoreDataId",
table: "Payouts");
}
}
}
}

View file

@ -17,13 +17,12 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.CreateTable(
migrationBuilder.CreateTable(
name: "LightningAddresses",
columns: table => new
{
Username = table.Column<string>(nullable: false, maxLength: maxLength),
StoreDataId = table.Column<string>(nullable: false, maxLength: maxLength),
Username = table.Column<string>(nullable: false, maxLength: null),
StoreDataId = table.Column<string>(nullable: false, maxLength: null),
Blob = table.Column<byte[]>( nullable: true)
},
constraints: table =>

View file

@ -14,14 +14,13 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxlength = migrationBuilder.IsMySql() ? 255 : null;
migrationBuilder.CreateTable(
name: "StoreSettings",
columns: table => new
{
Name = table.Column<string>(nullable: false, maxLength: maxlength),
StoreId = table.Column<string>(nullable: false, maxLength: maxlength),
Value = table.Column<string>(type: migrationBuilder.IsNpgsql() ? "JSONB" : "TEXT", nullable: true)
Name = table.Column<string>(nullable: false, maxLength: null),
StoreId = table.Column<string>(nullable: false, maxLength: null),
Value = table.Column<string>(type: "JSONB", nullable: true)
},
constraints: table =>
{

View file

@ -17,16 +17,14 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxlength = migrationBuilder.IsMySql() ? 255 : null;
migrationBuilder.CreateTable(
name: "WalletObjects",
columns: table => new
{
WalletId = table.Column<string>(nullable: false, maxLength: maxlength),
Type = table.Column<string>(nullable: false, maxLength: maxlength),
Id = table.Column<string>(nullable: false, maxLength: maxlength),
Data = table.Column<string>(type: migrationBuilder.IsNpgsql() ? "JSONB" : "TEXT", nullable: true)
WalletId = table.Column<string>(nullable: false, maxLength: null),
Type = table.Column<string>(nullable: false, maxLength: null),
Id = table.Column<string>(nullable: false, maxLength: null),
Data = table.Column<string>(type: "JSONB", nullable: true)
},
constraints: table =>
{
@ -37,18 +35,16 @@ namespace BTCPayServer.Migrations
table: "WalletObjects",
columns: new[] { "Type", "Id" });
maxlength = migrationBuilder.IsMySql() ? 100 : null;
migrationBuilder.CreateTable(
name: "WalletObjectLinks",
columns: table => new
{
WalletId = table.Column<string>(nullable: false, maxLength: maxlength),
AType = table.Column<string>(nullable: false, maxLength: maxlength),
AId = table.Column<string>(nullable: false, maxLength: maxlength),
BType = table.Column<string>(nullable: false, maxLength: maxlength),
BId = table.Column<string>(nullable: false, maxLength: maxlength),
Data = table.Column<string>(type: migrationBuilder.IsNpgsql() ? "JSONB" : "TEXT", nullable: true)
WalletId = table.Column<string>(nullable: false, maxLength: null),
AType = table.Column<string>(nullable: false, maxLength: null),
AId = table.Column<string>(nullable: false, maxLength: null),
BType = table.Column<string>(nullable: false, maxLength: null),
BId = table.Column<string>(nullable: false, maxLength: null),
Data = table.Column<string>(type: "JSONB", nullable: true)
},
constraints: table =>
{

View file

@ -16,11 +16,8 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
if (migrationBuilder.IsNpgsql())
{
migrationBuilder.Sql("ALTER TABLE \"Settings\" ALTER COLUMN \"Value\" TYPE JSONB USING \"Value\"::JSONB");
migrationBuilder.Sql("ALTER TABLE \"Stores\" ALTER COLUMN \"StoreBlob\" TYPE JSONB USING regexp_replace(convert_from(\"StoreBlob\",'UTF8'), '\\\\u0000', '', 'g')::JSONB");
}
migrationBuilder.Sql("ALTER TABLE \"Settings\" ALTER COLUMN \"Value\" TYPE JSONB USING \"Value\"::JSONB");
migrationBuilder.Sql("ALTER TABLE \"Stores\" ALTER COLUMN \"StoreBlob\" TYPE JSONB USING regexp_replace(convert_from(\"StoreBlob\",'UTF8'), '\\\\u0000', '', 'g')::JSONB");
}
protected override void Down(MigrationBuilder migrationBuilder)

View file

@ -16,10 +16,7 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
if (migrationBuilder.IsNpgsql())
{
migrationBuilder.Sql("UPDATE \"Stores\" SET \"StoreBlob\"=jsonb_set(\"StoreBlob\", \'{preferredExchange}\', \'{\"oasis_trade\": \"oasisdev\", \"gdax\":\"coinbasepro\", \"coinaverage\":\"coingecko\"}\'::jsonb->(\"StoreBlob\"->>\'preferredExchange\')) WHERE \"StoreBlob\"->>\'preferredExchange\' = ANY (ARRAY[\'oasis_trade\', \'gdax\', \'coinaverage\']);");
}
migrationBuilder.Sql("UPDATE \"Stores\" SET \"StoreBlob\"=jsonb_set(\"StoreBlob\", \'{preferredExchange}\', \'{\"oasis_trade\": \"oasisdev\", \"gdax\":\"coinbasepro\", \"coinaverage\":\"coingecko\"}\'::jsonb->(\"StoreBlob\"->>\'preferredExchange\')) WHERE \"StoreBlob\"->>\'preferredExchange\' = ANY (ARRAY[\'oasis_trade\', \'gdax\', \'coinaverage\']);");
}
protected override void Down(MigrationBuilder migrationBuilder)

View file

@ -16,15 +16,14 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxlength = migrationBuilder.IsMySql() ? 255 : null;
migrationBuilder.CreateTable(
name: "Forms",
columns: table => new
{
Id = table.Column<string>(type: "TEXT", nullable: false, maxLength: maxlength),
Name = table.Column<string>(type: "TEXT", nullable: true, maxLength: maxlength),
StoreId = table.Column<string>(type: "TEXT", nullable: true, maxLength: maxlength),
Config = table.Column<string>(type: migrationBuilder.IsNpgsql() ? "JSONB" : "TEXT", nullable: true),
Id = table.Column<string>(type: "TEXT", nullable: false, maxLength: null),
Name = table.Column<string>(type: "TEXT", nullable: true, maxLength: null),
StoreId = table.Column<string>(type: "TEXT", nullable: true, maxLength: null),
Config = table.Column<string>(type: "JSONB", nullable: true),
Public = table.Column<bool>(nullable: false)
},
constraints: table =>

View file

@ -14,76 +14,75 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
var type = migrationBuilder.IsNpgsql() ? "JSONB" : "TEXT";
migrationBuilder.AddColumn<string>(
name: "Blob2",
table: "Webhooks",
type: type,
type: "JSONB",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "Blob2",
table: "WebhookDeliveries",
type: type,
type: "JSONB",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "Blob2",
table: "PaymentRequests",
type: type,
type: "JSONB",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "Blob2",
table: "Notifications",
type: type,
type: "JSONB",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "Blob2",
table: "LightningAddresses",
type: type,
type: "JSONB",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "Blob2",
table: "Fido2Credentials",
type: type,
type: "JSONB",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "Blob2",
table: "AspNetUsers",
type: type,
type: "JSONB",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "Blob2",
table: "ApiKeys",
type: type,
type: "JSONB",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "Blob2",
table: "Invoices",
type: type,
type: "JSONB",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "Blob2",
table: "Payments",
type: type,
type: "JSONB",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "Blob2",
table: "PayoutProcessors",
type: type,
type: "JSONB",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "Blob2",
table: "CustodianAccount",
type: type,
type: "JSONB",
nullable: true);
migrationBuilder.AddColumn<string>(

View file

@ -16,10 +16,7 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
if (migrationBuilder.IsNpgsql())
{
migrationBuilder.Sql("ALTER TABLE \"Stores\" ALTER COLUMN \"DerivationStrategies\" TYPE JSONB USING \"DerivationStrategies\"::JSONB");
}
migrationBuilder.Sql("ALTER TABLE \"Stores\" ALTER COLUMN \"DerivationStrategies\" TYPE JSONB USING \"DerivationStrategies\"::JSONB");
}
protected override void Down(MigrationBuilder migrationBuilder)

View file

@ -16,11 +16,8 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
if (migrationBuilder.IsNpgsql())
{
migrationBuilder.Sql("ALTER TABLE \"InvoiceSearches\" ALTER COLUMN \"Value\" TYPE TEXT USING \"Value\"::TEXT;");
migrationBuilder.Sql("ALTER TABLE \"Invoices\" ALTER COLUMN \"OrderId\" TYPE TEXT USING \"OrderId\"::TEXT;");
}
migrationBuilder.Sql("ALTER TABLE \"InvoiceSearches\" ALTER COLUMN \"Value\" TYPE TEXT USING \"Value\"::TEXT;");
migrationBuilder.Sql("ALTER TABLE \"Invoices\" ALTER COLUMN \"OrderId\" TYPE TEXT USING \"OrderId\"::TEXT;");
}
protected override void Down(MigrationBuilder migrationBuilder)

View file

@ -17,7 +17,6 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
var permissionsType = migrationBuilder.IsNpgsql() ? "TEXT[]" : "TEXT";
migrationBuilder.CreateTable(
name: "StoreRoles",
columns: table => new
@ -25,7 +24,7 @@ namespace BTCPayServer.Migrations
Id = table.Column<string>(type: "TEXT", nullable: false),
StoreDataId = table.Column<string>(type: "TEXT", nullable: true),
Role = table.Column<string>(type: "TEXT", nullable: false),
Permissions = table.Column<string>(type: permissionsType, nullable: false)
Permissions = table.Column<string>(type: "TEXT[]", nullable: false)
},
constraints: table =>
{
@ -44,60 +43,45 @@ namespace BTCPayServer.Migrations
columns: new[] { "StoreDataId", "Role" },
unique: true);
object GetPermissionsData(string[] permissions)
{
if (migrationBuilder.IsNpgsql())
return permissions;
return JsonConvert.SerializeObject(permissions);
}
migrationBuilder.InsertData(
"StoreRoles",
columns: new[] { "Id", "Role", "Permissions" },
columnTypes: new[] { "TEXT", "TEXT", permissionsType },
columnTypes: new[] { "TEXT", "TEXT", "TEXT[]" },
values: new object[,]
{
{
"Owner", "Owner", GetPermissionsData(new[]
"Owner", "Owner", new[]
{
"btcpay.store.canmodifystoresettings",
"btcpay.store.cantradecustodianaccount",
"btcpay.store.canwithdrawfromcustodianaccount",
"btcpay.store.candeposittocustodianaccount"
})
}
},
{
"Guest", "Guest", GetPermissionsData(new[]
"Guest", "Guest", new[]
{
"btcpay.store.canviewstoresettings",
"btcpay.store.canmodifyinvoices",
"btcpay.store.canviewcustodianaccounts",
"btcpay.store.candeposittocustodianaccount"
})
}
}
});
if (this.SupportAddForeignKey(migrationBuilder.ActiveProvider))
{
migrationBuilder.AddForeignKey(
name: "FK_UserStore_StoreRoles_Role",
table: "UserStore",
column: "Role",
principalTable: "StoreRoles",
principalColumn: "Id");
}
migrationBuilder.AddForeignKey(
name: "FK_UserStore_StoreRoles_Role",
table: "UserStore",
column: "Role",
principalTable: "StoreRoles",
principalColumn: "Id");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
if (this.SupportDropForeignKey(migrationBuilder.ActiveProvider))
{
migrationBuilder.DropForeignKey(
name: "FK_UserStore_StoreRoles_Role",
table: "UserStore");
}
migrationBuilder.DropForeignKey(
name: "FK_UserStore_StoreRoles_Role",
table: "UserStore");
migrationBuilder.DropTable(
name: "StoreRoles");

View file

@ -1,28 +0,0 @@
using Microsoft.EntityFrameworkCore.Infrastructure;
namespace BTCPayServer.Migrations
{
public static class MigrationsExtensions
{
public static bool SupportDropColumn(this Microsoft.EntityFrameworkCore.Migrations.Migration migration, string activeProvider)
{
return activeProvider != "Microsoft.EntityFrameworkCore.Sqlite";
}
public static bool SupportAddForeignKey(this Microsoft.EntityFrameworkCore.Migrations.Migration migration, string activeProvider)
{
return activeProvider != "Microsoft.EntityFrameworkCore.Sqlite";
}
public static bool SupportDropForeignKey(this Microsoft.EntityFrameworkCore.Migrations.Migration migration, string activeProvider)
{
return activeProvider != "Microsoft.EntityFrameworkCore.Sqlite";
}
public static bool SupportDropForeignKey(this DatabaseFacade facade)
{
return facade.ProviderName != "Microsoft.EntityFrameworkCore.Sqlite";
}
public static bool IsMySql(this Microsoft.EntityFrameworkCore.Migrations.Migration migration, string activeProvider)
{
return activeProvider == "Pomelo.EntityFrameworkCore.MySql";
}
}
}

View file

@ -1270,17 +1270,6 @@ bc1qfzu57kgu5jthl934f9xrdzzx8mmemx7gn07tf0grnvz504j6kzusu2v0ku
Assert.Contains("originalDeliveryId", serialized);
}
[Fact]
public async Task CanCreateSqlitedb()
{
if (File.Exists("temp.db"))
File.Delete("temp.db");
// This test sqlite can migrate
var builder = new DbContextOptionsBuilder<ApplicationDbContext>();
builder.UseSqlite("Data Source=temp.db");
await new ApplicationDbContext(builder.Options).Database.MigrateAsync();
}
[Fact]
public void CanUsePermission()
{

View file

@ -4179,20 +4179,20 @@ namespace BTCPayServer.Tests
Assert.Single(testObj.Links.Where(l => l.Id == "test1" && l.LinkData["testData"]?.Value<string>() == "lol"));
Assert.Single(testObj.Links.Where(l => l.Id == "test1" && l.ObjectData is null));
async Task TestWalletRepository(bool useInefficient)
async Task TestWalletRepository()
{
// We should have 4 nodes, two `test` type and one `newtype`
// Only the node `test` `test` is connected to `test1`
var wid = new WalletId(admin.StoreId, "BTC");
var repo = tester.PayTester.GetService<WalletRepository>();
var allObjects = await repo.GetWalletObjects(new(wid) { UseInefficientPath = useInefficient });
var allObjectsNoWallet = await repo.GetWalletObjects((new() { UseInefficientPath = useInefficient }));
var allObjectsNoWalletAndType = await repo.GetWalletObjects((new() { Type = "test", UseInefficientPath = useInefficient }));
var allTests = await repo.GetWalletObjects((new(wid, "test") { UseInefficientPath = useInefficient }));
var twoTests2 = await repo.GetWalletObjects((new(wid, "test", new[] { "test1", "test2", "test-unk" }) { UseInefficientPath = useInefficient }));
var oneTest = await repo.GetWalletObjects((new(wid, "test", new[] { "test" }) { UseInefficientPath = useInefficient }));
var oneTestWithoutData = await repo.GetWalletObjects((new(wid, "test", new[] { "test" }) { UseInefficientPath = useInefficient, IncludeNeighbours = false }));
var idsTypes = await repo.GetWalletObjects((new(wid) { TypesIds = new[] { new ObjectTypeId("test", "test1"), new ObjectTypeId("test", "test2") }, UseInefficientPath = useInefficient }));
var allObjects = await repo.GetWalletObjects(new(wid));
var allObjectsNoWallet = await repo.GetWalletObjects((new()));
var allObjectsNoWalletAndType = await repo.GetWalletObjects((new() { Type = "test" }));
var allTests = await repo.GetWalletObjects((new(wid, "test")));
var twoTests2 = await repo.GetWalletObjects((new(wid, "test", new[] { "test1", "test2", "test-unk" })));
var oneTest = await repo.GetWalletObjects((new(wid, "test", new[] { "test" })));
var oneTestWithoutData = await repo.GetWalletObjects((new(wid, "test", new[] { "test" }) { IncludeNeighbours = false }));
var idsTypes = await repo.GetWalletObjects((new(wid) { TypesIds = new[] { new ObjectTypeId("test", "test1"), new ObjectTypeId("test", "test2") }}));
Assert.Equal(4, allObjects.Count);
// We are reusing a db in this test, as such we may have other wallets here.
@ -4206,8 +4206,7 @@ namespace BTCPayServer.Tests
Assert.Null(oneTestWithoutData.First().Value.GetNeighbours().Select(n => n.Data).FirstOrDefault());
Assert.Equal(2, idsTypes.Count);
}
await TestWalletRepository(false);
await TestWalletRepository(true);
await TestWalletRepository();
{
var allObjects = await client.GetOnChainWalletObjects(admin.StoreId, "BTC");

View file

@ -36,9 +36,7 @@ namespace BTCPayServer.Configuration
app.Option("--signet | -signet", $"Use signet (deprecated, use --network instead)", CommandOptionType.BoolValue);
app.Option("--chains | -c", $"Chains to support as a comma separated (default: btc; available: {chains})", CommandOptionType.SingleValue);
app.Option("--postgres", $"Connection string to a PostgreSQL database", CommandOptionType.SingleValue);
app.Option("--mysql", $"DEPRECATED: Connection string to a MySQL database", CommandOptionType.SingleValue);
app.Option("--nocsp", $"Disable CSP (default false)", CommandOptionType.BoolValue);
app.Option("--sqlitefile", $"DEPRECATED: File name to an SQLite database file inside the data directory", CommandOptionType.SingleValue);
app.Option("--deprecated", $"Allow deprecated settings (default:false)", CommandOptionType.BoolValue);
app.Option("--externalservices", $"Links added to external services inside Server Settings / Services under the format service1:path2;service2:path2.(default: empty)", CommandOptionType.SingleValue);
app.Option("--rootpath", "The root path in the URL to access BTCPay (default: /)", CommandOptionType.SingleValue);
@ -139,8 +137,6 @@ namespace BTCPayServer.Configuration
builder.AppendLine();
builder.AppendLine("### Database ###");
builder.AppendLine("#postgres=User ID=root;Password=myPassword;Host=localhost;Port=5432;Database=myDataBase;");
builder.AppendLine("#mysql=User ID=root;Password=myPassword;Host=localhost;Port=3306;Database=myDataBase;");
builder.AppendLine("#sqlitefile=sqlite.db");
builder.AppendLine();
builder.AppendLine("### NBXplorer settings ###");
foreach (var n in CreateBTCPayNetworkProvider(networkType).GetAll().OfType<BTCPayNetwork>())

View file

@ -63,12 +63,6 @@ namespace BTCPayServer.Controllers.Greenfield
{
return StoreNotFound();
}
if (!_storeRepository.CanDeleteStores())
{
return this.CreateAPIError("unsupported",
"BTCPay Server is using a database server that does not allow you to remove stores.");
}
await _storeRepository.RemoveStore(storeId, _userManager.GetUserId(User));
return Ok();
}

View file

@ -40,8 +40,7 @@ public partial class UIStoresController
InvoiceExpiration = (int)storeBlob.InvoiceExpiration.TotalMinutes,
DefaultCurrency = storeBlob.DefaultCurrency,
BOLT11Expiration = (long)storeBlob.RefundBOLT11Expiration.TotalDays,
Archived = store.Archived,
CanDelete = _storeRepo.CanDeleteStores()
Archived = store.Archived
};
return View(vm);

View file

@ -24,8 +24,6 @@ namespace BTCPayServer.HostedServices
public async Task Do(CancellationToken cancellationToken)
{
await using var ctx = DbContextFactory.CreateContext();
if (!ctx.Database.IsNpgsql())
return;
var conn = ctx.Database.GetDbConnection();
bool pruned = false;
int offset = 0;

View file

@ -145,9 +145,7 @@ namespace BTCPayServer.Hosting
services.TryAddSingleton<InvoicePaymentNotification>();
services.TryAddSingleton<BTCPayServerOptions>(o =>
o.GetRequiredService<IOptions<BTCPayServerOptions>>().Value);
// Don't move this StartupTask, we depend on it being right here
if (configuration["POSTGRES"] != null && (configuration["SQLITEFILE"] != null || configuration["MYSQL"] != null))
services.AddStartupTask<ToPostgresMigrationStartupTask>();
services.AddStartupTask<MigrationStartupTask>();
//
@ -184,24 +182,10 @@ namespace BTCPayServer.Hosting
(options, datadirs) =>
{
var postgresConnectionString = configuration["postgres"];
var mySQLConnectionString = configuration["mysql"];
var sqliteFileName = configuration["sqlitefile"];
if (!string.IsNullOrEmpty(postgresConnectionString))
{
options.DatabaseType = DatabaseType.Postgres;
options.ConnectionString = postgresConnectionString;
}
else if (!string.IsNullOrEmpty(mySQLConnectionString))
{
options.DatabaseType = DatabaseType.MySQL;
options.ConnectionString = mySQLConnectionString;
}
else if (!string.IsNullOrEmpty(sqliteFileName))
{
options.DatabaseType = DatabaseType.Sqlite;
options.ConnectionString = "Data Source=" + datadirs.Value.ToDatadirFullPath(sqliteFileName);
}
else
{
throw new InvalidOperationException("No database option was configured.");

View file

@ -372,12 +372,21 @@ namespace BTCPayServer.Hosting
private async Task FixSeqAfterSqliteMigration()
{
await using var ctx = _DBContextFactory.CreateContext();
if (!ctx.Database.IsNpgsql())
return;
var state = await ToPostgresMigrationStartupTask.GetMigrationState(ctx);
var state = await GetMigrationState(ctx);
if (state != "complete")
return;
await ToPostgresMigrationStartupTask.UpdateSequenceInvoiceSearch(ctx);
await UpdateSequenceInvoiceSearch(ctx);
}
static async Task<string> GetMigrationState(ApplicationDbContext postgresContext)
{
var o = (await postgresContext.Settings.FromSqlRaw("SELECT \"Id\", \"Value\" FROM \"Settings\" WHERE \"Id\"='MigrationData'").AsNoTracking().FirstOrDefaultAsync())?.Value;
if (o is null)
return null;
return JObject.Parse(o)["state"]?.Value<string>();
}
static async Task UpdateSequenceInvoiceSearch(ApplicationDbContext postgresContext)
{
await postgresContext.Database.ExecuteSqlRawAsync("SELECT SETVAL('\"InvoiceSearches_Id_seq\"', (SELECT max(\"Id\") FROM \"InvoiceSearches\"));");
}
private async Task MigrateAppYmlToJson()
{
@ -646,9 +655,7 @@ namespace BTCPayServer.Hosting
{
await using var ctx = _DBContextFactory.CreateContext();
if (ctx.Database.IsNpgsql())
{
await ctx.Database.ExecuteSqlRawAsync(@"
await ctx.Database.ExecuteSqlRawAsync(@"
WITH cte AS (
SELECT DISTINCT p.""Id"", pp.""StoreId"" FROM ""Payouts"" p
JOIN ""PullPayments"" pp ON pp.""Id"" = p.""PullPaymentDataId""
@ -659,22 +666,6 @@ SET ""StoreDataId""=cte.""StoreId""
FROM cte
WHERE cte.""Id""=p.""Id""
");
}
else
{
var queryable = ctx.Payouts.Where(data => data.StoreDataId == null);
var count = await queryable.CountAsync();
_logger.LogInformation($"Migrating {count} payouts to have a store id explicitly");
for (int i = 0; i < count; i += 1000)
{
await queryable.Include(data => data.PullPaymentData).Skip(i).Take(1000)
.ForEachAsync(data => data.StoreDataId = data.PullPaymentData.StoreId);
await ctx.SaveChangesAsync();
_logger.LogInformation($"Migrated {i + 1000}/{count} payouts to have a store id explicitly");
}
}
}
private async Task AddInitialUserBlob()
@ -944,11 +935,6 @@ retry:
{
var db = _DBContextFactory.CreateContext();
await db.Database.MigrateAsync();
if (db.Database.IsNpgsql())
{
if (await db.GetMigrationState() == "pending")
throw new ConfigException("This database hasn't been completely migrated, please retry migration by setting the BTCPAY_SQLITEFILE or BTCPAY_MYSQL setting on top of BTCPAY_POSTGRES");
}
}
// Starting up
catch (ConfigException) { throw; }

View file

@ -1,296 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Amazon.Runtime.Internal.Util;
using AngleSharp.Text;
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Configuration;
using BTCPayServer.Data;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MySqlConnector;
using NBXplorer;
using Newtonsoft.Json.Linq;
using Npgsql;
namespace BTCPayServer.Hosting
{
static class TopologySort
{
public static IEnumerable<ITable> OrderByTopology(this IEnumerable<ITable> tables)
{
var comparer = Comparer<ITable>.Create((a, b) => a.Name.CompareTo(b.Name));
return OrderByTopology(
tables,
t =>
{
if (t.Name == "Invoices")
return t.ForeignKeyConstraints.Select(f => f.PrincipalTable.Name).Where(f => f != "Refunds");
else
return t.ForeignKeyConstraints.Select(f => f.PrincipalTable.Name);
},
t => t.Name,
t => t,
comparer);
}
public static IEnumerable<TValue> OrderByTopology<T, TDepend, TValue>(
this IEnumerable<T> values,
Func<T, IEnumerable<TDepend>> dependsOn,
Func<T, TDepend> getKey,
Func<T, TValue> getValue,
IComparer<T>? solveTies = null) where T : notnull
{
var v = values.ToList();
return TopologicalSort(v, dependsOn, getKey, getValue, solveTies);
}
static List<TValue> TopologicalSort<T, TDepend, TValue>(this IReadOnlyCollection<T> nodes,
Func<T, IEnumerable<TDepend>> dependsOn,
Func<T, TDepend> getKey,
Func<T, TValue> getValue,
IComparer<T>? solveTies = null) where T : notnull
{
if (nodes.Count == 0)
return new List<TValue>();
if (getKey == null)
throw new ArgumentNullException(nameof(getKey));
if (getValue == null)
throw new ArgumentNullException(nameof(getValue));
solveTies = solveTies ?? Comparer<T>.Default;
List<TValue> result = new List<TValue>(nodes.Count);
HashSet<TDepend> allKeys = new HashSet<TDepend>(nodes.Count);
var noDependencies = new SortedDictionary<T, HashSet<TDepend>>(solveTies);
foreach (var node in nodes)
allKeys.Add(getKey(node));
var dependenciesByValues = nodes.ToDictionary(node => node,
node => new HashSet<TDepend>(dependsOn(node).Where(n => allKeys.Contains(n))));
foreach (var e in dependenciesByValues.Where(x => x.Value.Count == 0))
{
noDependencies.Add(e.Key, e.Value);
}
if (noDependencies.Count == 0)
{
throw new InvalidOperationException("Impossible to topologically sort a cyclic graph");
}
while (noDependencies.Count > 0)
{
var nodep = noDependencies.First();
noDependencies.Remove(nodep.Key);
dependenciesByValues.Remove(nodep.Key);
var elemKey = getKey(nodep.Key);
result.Add(getValue(nodep.Key));
foreach (var selem in dependenciesByValues)
{
if (selem.Value.Remove(elemKey) && selem.Value.Count == 0)
noDependencies.Add(selem.Key, selem.Value);
}
}
if (dependenciesByValues.Count != 0)
{
throw new InvalidOperationException("Impossible to topologically sort a cyclic graph");
}
return result;
}
}
public class ToPostgresMigrationStartupTask : IStartupTask
{
public ToPostgresMigrationStartupTask(
IConfiguration configuration,
IOptions<DataDirectories> datadirs,
ILogger<ToPostgresMigrationStartupTask> logger,
IWebHostEnvironment environment,
ApplicationDbContextFactory dbContextFactory)
{
Configuration = configuration;
Datadirs = datadirs;
Logger = logger;
Environment = environment;
DbContextFactory = dbContextFactory;
}
public IConfiguration Configuration { get; }
public IOptions<DataDirectories> Datadirs { get; }
public ILogger<ToPostgresMigrationStartupTask> Logger { get; }
public IWebHostEnvironment Environment { get; }
public ApplicationDbContextFactory DbContextFactory { get; }
public bool HasError { get; private set; }
public async Task ExecuteAsync(CancellationToken cancellationToken = default)
{
var p = Configuration.GetOrDefault<string?>("POSTGRES", null);
var sqlite = Configuration.GetOrDefault<string?>("SQLITEFILE", null);
var mysql = Configuration.GetOrDefault<string?>("MYSQL", null);
string migratingFrom;
ApplicationDbContext otherContext;
if (string.IsNullOrEmpty(p))
{
return;
}
else if (!string.IsNullOrEmpty(sqlite))
{
migratingFrom = "SQLite";
sqlite = Datadirs.Value.ToDatadirFullPath(sqlite);
if (!File.Exists(sqlite))
return;
otherContext = new ApplicationDbContext(new DbContextOptionsBuilder<ApplicationDbContext>().UseSqlite("Data Source=" + sqlite, o => o.CommandTimeout(60 * 60 * 10)).Options);
}
else if (!string.IsNullOrEmpty(mysql))
{
migratingFrom = "MySQL";
otherContext = new ApplicationDbContext(new DbContextOptionsBuilder<ApplicationDbContext>().UseMySql(mysql, ServerVersion.AutoDetect(mysql), o => o.CommandTimeout(60 * 60 * 10)).Options);
try
{
await otherContext.Settings.FirstOrDefaultAsync();
}
catch (MySqlException ex) when (ex.SqlState == "42000") // DB doesn't exists
{
return;
}
}
else
{
return;
}
if (await otherContext.Settings.FirstOrDefaultAsync() == null)
return;
{
using var postgresContext = new ApplicationDbContext(new DbContextOptionsBuilder<ApplicationDbContext>().UseNpgsql(p, o =>
{
o.CommandTimeout(60 * 60 * 10);
o.SetPostgresVersion(12, 0);
}).Options);
string? state;
try
{
state = await GetMigrationState(postgresContext);
if (state == "complete")
return;
if (state == null)
throw new ConfigException("This postgres database isn't created during a migration. Please use an empty database for postgres when migrating. If it's not a migration, remove --sqlitefile or --mysql settings.");
}
catch (NpgsqlException ex) when (ex.SqlState == PostgresErrorCodes.InvalidCatalogName || ex.SqlState == PostgresErrorCodes.UndefinedTable) // DB doesn't exists
{
await postgresContext.Database.MigrateAsync();
state = "pending";
await SetMigrationState(postgresContext, migratingFrom, "pending");
}
Logger.LogInformation($"Migrating from {migratingFrom} to Postgres...");
if (state == "pending")
{
Logger.LogInformation($"There is a unfinished migration in postgres... dropping all tables");
foreach (var t in postgresContext.Model.GetRelationalModel().Tables.OrderByTopology())
{
await postgresContext.Database.ExecuteSqlRawAsync($"DROP TABLE IF EXISTS \"{t.Name}\" CASCADE");
}
await postgresContext.Database.ExecuteSqlRawAsync($"DROP TABLE IF EXISTS \"__EFMigrationsHistory\" CASCADE");
await postgresContext.Database.MigrateAsync();
}
else
{
throw new ConfigException("This database isn't created during a migration. Please use an empty database for postgres when migrating.");
}
await otherContext.Database.MigrateAsync();
await SetMigrationState(postgresContext, migratingFrom, "pending");
foreach (var t in postgresContext.Model.GetRelationalModel().Tables.OrderByTopology())
{
var typeMapping = t.EntityTypeMappings.Single();
var query = (IQueryable<object>)otherContext.GetType().GetMethod("Set", new Type[0])!.MakeGenericMethod(typeMapping.TypeBase.ClrType).Invoke(otherContext, null)!;
if (t.Name == "WebhookDeliveries" ||
t.Name == "InvoiceWebhookDeliveries" ||
t.Name == "StoreRoles")
continue;
Logger.LogInformation($"Migrating table: " + t.Name);
List<PropertyInfo> datetimeProperties = new List<PropertyInfo>();
foreach (var col in t.Columns)
if (col.PropertyMappings.Single().Property.ClrType == typeof(DateTime))
{
datetimeProperties.Add(col.PropertyMappings.Single().Property.PropertyInfo!);
}
List<PropertyInfo> datetimeoffsetProperties = new List<PropertyInfo>();
foreach (var col in t.Columns)
if (col.PropertyMappings.Single().Property.ClrType == typeof(DateTimeOffset))
{
datetimeoffsetProperties.Add(col.PropertyMappings.Single().Property.PropertyInfo!);
}
var rows = await query.ToListAsync();
foreach (var row in rows)
{
foreach (var prop in datetimeProperties)
{
var v = (DateTime)prop.GetValue(row)!;
if (v.Kind == DateTimeKind.Unspecified)
{
v = new DateTime(v.Ticks, DateTimeKind.Utc);
prop.SetValue(row, v);
}
else if (v.Kind == DateTimeKind.Local)
{
prop.SetValue(row, v.ToUniversalTime());
}
}
foreach (var prop in datetimeoffsetProperties)
{
var v = (DateTimeOffset)prop.GetValue(row)!;
if (v.Offset != TimeSpan.Zero)
{
prop.SetValue(row, v.ToOffset(TimeSpan.Zero));
}
}
postgresContext.Entry(row).State = EntityState.Added;
}
await postgresContext.SaveChangesAsync();
postgresContext.ChangeTracker.Clear();
}
await postgresContext.SaveChangesAsync();
postgresContext.ChangeTracker.Clear();
await UpdateSequenceInvoiceSearch(postgresContext);
await SetMigrationState(postgresContext, migratingFrom, "complete");
}
otherContext.Dispose();
SqliteConnection.ClearAllPools();
MySqlConnection.ClearAllPools();
Logger.LogInformation($"Migration to postgres from {migratingFrom} successful");
}
internal static async Task UpdateSequenceInvoiceSearch(ApplicationDbContext postgresContext)
{
await postgresContext.Database.ExecuteSqlRawAsync("SELECT SETVAL('\"InvoiceSearches_Id_seq\"', (SELECT max(\"Id\") FROM \"InvoiceSearches\"));");
}
internal static async Task<string?> GetMigrationState(ApplicationDbContext postgresContext)
{
var o = (await postgresContext.Settings.FromSqlRaw("SELECT \"Id\", \"Value\" FROM \"Settings\" WHERE \"Id\"='MigrationData'").AsNoTracking().FirstOrDefaultAsync())?.Value;
if (o is null)
return null;
return JObject.Parse(o)["state"]?.Value<string>();
}
private static async Task SetMigrationState(ApplicationDbContext postgresContext, string migratingFrom, string state)
{
await postgresContext.Database.ExecuteSqlRawAsync(
"INSERT INTO \"Settings\" VALUES ('MigrationData', @p0::JSONB) ON CONFLICT (\"Id\") DO UPDATE SET \"Value\"=@p0::JSONB",
new[] { $"{{ \"from\": \"{migratingFrom}\", \"state\": \"{state}\" }}" });
}
}
}

View file

@ -33,8 +33,6 @@ namespace BTCPayServer.Models.StoreViewModels
public IFormFile CssFile { get; set; }
public string CssFileId { get; set; }
public bool CanDelete { get; set; }
public bool Archived { get; set; }
[Display(Name = "Allow anyone to create invoice")]

View file

@ -335,8 +335,6 @@ namespace BTCPayServer.Services.Stores
public async Task CleanUnreachableStores()
{
await using var ctx = _ContextFactory.CreateContext();
if (!ctx.Database.SupportDropForeignKey())
return;
var events = new List<Events.StoreRemovedEvent>();
foreach (var store in await ctx.Stores.Include(data => data.UserStores)
.ThenInclude(store => store.StoreRole).Where(s =>
@ -368,17 +366,14 @@ namespace BTCPayServer.Services.Stores
private async Task DeleteStoreIfOrphan(string storeId)
{
await using var ctx = _ContextFactory.CreateContext();
if (ctx.Database.SupportDropForeignKey())
if (!await ctx.UserStore.Where(u => u.StoreDataId == storeId && u.StoreRole.Permissions.Contains(Policies.CanModifyStoreSettings)).AnyAsync())
{
if (!await ctx.UserStore.Where(u => u.StoreDataId == storeId && u.StoreRole.Permissions.Contains(Policies.CanModifyStoreSettings)).AnyAsync())
var store = await ctx.Stores.FindAsync(storeId);
if (store != null)
{
var store = await ctx.Stores.FindAsync(storeId);
if (store != null)
{
ctx.Stores.Remove(store);
await ctx.SaveChangesAsync();
_eventAggregator.Publish(new StoreRemovedEvent(store.Id));
}
ctx.Stores.Remove(store);
await ctx.SaveChangesAsync();
_eventAggregator.Publish(new StoreRemovedEvent(store.Id));
}
}
}
@ -559,8 +554,6 @@ namespace BTCPayServer.Services.Stores
{
int retry = 0;
using var ctx = _ContextFactory.CreateContext();
if (!ctx.Database.SupportDropForeignKey())
return false;
var store = await ctx.Stores.FindAsync(storeId);
if (store == null)
return false;
@ -641,12 +634,6 @@ retry:
{
return ex.InnerException is Npgsql.PostgresException postgres && postgres.SqlState == "40P01";
}
public bool CanDeleteStores()
{
using var ctx = _ContextFactory.CreateContext();
return ctx.Database.SupportDropForeignKey();
}
}
public record StoreRoleId

View file

@ -55,7 +55,6 @@ namespace BTCPayServer.Services
public string? Type { get; set; }
public string[]? Ids { get; set; }
public bool IncludeNeighbours { get; set; } = true;
public bool UseInefficientPath { get; set; }
public static IEnumerable<ObjectTypeId> Get(ReceivedCoin coin)
{
@ -96,151 +95,118 @@ namespace BTCPayServer.Services
// pg_stat_statements output, making it impossible to analyze the performance impact of this query.
// On top of this, the entity version is doing 2 left join to satisfy the Include queries, resulting in n*m row returned for each transaction.
// n being the number of children, m the number of parents.
if (ctx.Database.IsNpgsql() && !queryObject.UseInefficientPath)
var connection = ctx.Database.GetDbConnection();
if (connection.State != System.Data.ConnectionState.Open)
await connection.OpenAsync();
string walletIdFilter = queryObject.WalletId is not null ? " AND wos.\"WalletId\"=@walletId" : "";
string typeFilter = queryObject.Type is not null ? " AND wos.\"Type\"=@type" : "";
var cmd = connection.CreateCommand();
var selectWalletObjects =
queryObject.TypesIds is not null ?
$"SELECT wos.* FROM unnest(@ids, @types) t(i,t) JOIN \"WalletObjects\" wos ON true{walletIdFilter} AND wos.\"Type\"=t AND wos.\"Id\"=i" :
queryObject.Ids is null ?
$"SELECT wos.* FROM \"WalletObjects\" wos WHERE true{walletIdFilter}{typeFilter} " :
queryObject.Ids.Length == 1 ?
$"SELECT wos.* FROM \"WalletObjects\" wos WHERE true{walletIdFilter} AND wos.\"Type\"=@type AND wos.\"Id\"=@id" :
$"SELECT wos.* FROM unnest(@ids) t JOIN \"WalletObjects\" wos ON true{walletIdFilter} AND wos.\"Type\"=@type AND wos.\"Id\"=t";
var includeNeighbourSelect = queryObject.IncludeNeighbours ? ", wos2.\"Data\" AS \"Data2\"" : "";
var includeNeighbourJoin = queryObject.IncludeNeighbours ? "LEFT JOIN \"WalletObjects\" wos2 ON wos.\"WalletId\"=wos2.\"WalletId\" AND wol.\"Type2\"=wos2.\"Type\" AND wol.\"Id2\"=wos2.\"Id\"" : "";
var query =
$"SELECT wos.\"WalletId\", wos.\"Id\", wos.\"Type\", wos.\"Data\", wol.\"LinkData\", wol.\"Type2\", wol.\"Id2\"{includeNeighbourSelect} FROM ({selectWalletObjects}) wos " +
$"LEFT JOIN LATERAL ( " +
"SELECT \"AType\" AS \"Type2\", \"AId\" AS \"Id2\", \"Data\" AS \"LinkData\" FROM \"WalletObjectLinks\" WHERE \"WalletId\"=wos.\"WalletId\" AND \"BType\"=wos.\"Type\" AND \"BId\"=wos.\"Id\" " +
"UNION " +
"SELECT \"BType\" AS \"Type2\", \"BId\" AS \"Id2\", \"Data\" AS \"LinkData\" FROM \"WalletObjectLinks\" WHERE \"WalletId\"=wos.\"WalletId\" AND \"AType\"=wos.\"Type\" AND \"AId\"=wos.\"Id\"" +
$" ) wol ON true " + includeNeighbourJoin;
cmd.CommandText = query;
if (queryObject.WalletId is not null)
{
var connection = ctx.Database.GetDbConnection();
if (connection.State != System.Data.ConnectionState.Open)
await connection.OpenAsync();
string walletIdFilter = queryObject.WalletId is not null ? " AND wos.\"WalletId\"=@walletId" : "";
string typeFilter = queryObject.Type is not null ? " AND wos.\"Type\"=@type" : "";
var cmd = connection.CreateCommand();
var selectWalletObjects =
queryObject.TypesIds is not null ?
$"SELECT wos.* FROM unnest(@ids, @types) t(i,t) JOIN \"WalletObjects\" wos ON true{walletIdFilter} AND wos.\"Type\"=t AND wos.\"Id\"=i" :
queryObject.Ids is null ?
$"SELECT wos.* FROM \"WalletObjects\" wos WHERE true{walletIdFilter}{typeFilter} " :
queryObject.Ids.Length == 1 ?
$"SELECT wos.* FROM \"WalletObjects\" wos WHERE true{walletIdFilter} AND wos.\"Type\"=@type AND wos.\"Id\"=@id" :
$"SELECT wos.* FROM unnest(@ids) t JOIN \"WalletObjects\" wos ON true{walletIdFilter} AND wos.\"Type\"=@type AND wos.\"Id\"=t";
var includeNeighbourSelect = queryObject.IncludeNeighbours ? ", wos2.\"Data\" AS \"Data2\"" : "";
var includeNeighbourJoin = queryObject.IncludeNeighbours ? "LEFT JOIN \"WalletObjects\" wos2 ON wos.\"WalletId\"=wos2.\"WalletId\" AND wol.\"Type2\"=wos2.\"Type\" AND wol.\"Id2\"=wos2.\"Id\"" : "";
var query =
$"SELECT wos.\"WalletId\", wos.\"Id\", wos.\"Type\", wos.\"Data\", wol.\"LinkData\", wol.\"Type2\", wol.\"Id2\"{includeNeighbourSelect} FROM ({selectWalletObjects}) wos " +
$"LEFT JOIN LATERAL ( " +
"SELECT \"AType\" AS \"Type2\", \"AId\" AS \"Id2\", \"Data\" AS \"LinkData\" FROM \"WalletObjectLinks\" WHERE \"WalletId\"=wos.\"WalletId\" AND \"BType\"=wos.\"Type\" AND \"BId\"=wos.\"Id\" " +
"UNION " +
"SELECT \"BType\" AS \"Type2\", \"BId\" AS \"Id2\", \"Data\" AS \"LinkData\" FROM \"WalletObjectLinks\" WHERE \"WalletId\"=wos.\"WalletId\" AND \"AType\"=wos.\"Type\" AND \"AId\"=wos.\"Id\"" +
$" ) wol ON true " + includeNeighbourJoin;
cmd.CommandText = query;
if (queryObject.WalletId is not null)
{
var walletIdParam = cmd.CreateParameter();
walletIdParam.ParameterName = "walletId";
walletIdParam.Value = queryObject.WalletId.ToString();
walletIdParam.DbType = System.Data.DbType.String;
cmd.Parameters.Add(walletIdParam);
}
if (queryObject.Type != null)
{
var typeParam = cmd.CreateParameter();
typeParam.ParameterName = "type";
typeParam.Value = queryObject.Type;
typeParam.DbType = System.Data.DbType.String;
cmd.Parameters.Add(typeParam);
}
if (queryObject.TypesIds != null)
{
var typesParam = cmd.CreateParameter();
typesParam.ParameterName = "types";
typesParam.Value = queryObject.TypesIds.Select(t => t.Type).ToList();
typesParam.DbType = System.Data.DbType.Object;
cmd.Parameters.Add(typesParam);
var idParam = cmd.CreateParameter();
idParam.ParameterName = "ids";
idParam.Value = queryObject.TypesIds.Select(t => t.Id).ToList();
idParam.DbType = System.Data.DbType.Object;
cmd.Parameters.Add(idParam);
}
if (queryObject.Ids != null)
{
if (queryObject.Ids.Length == 1)
{
var txIdParam = cmd.CreateParameter();
txIdParam.ParameterName = "id";
txIdParam.Value = queryObject.Ids[0];
txIdParam.DbType = System.Data.DbType.String;
cmd.Parameters.Add(txIdParam);
}
else
{
var txIdsParam = cmd.CreateParameter();
txIdsParam.ParameterName = "ids";
txIdsParam.Value = queryObject.Ids.ToList();
txIdsParam.DbType = System.Data.DbType.Object;
cmd.Parameters.Add(txIdsParam);
}
}
await using var reader = await cmd.ExecuteReaderAsync();
var wosById = new Dictionary<WalletObjectId, WalletObjectData>();
while (await reader.ReadAsync())
{
WalletObjectData wo = new WalletObjectData();
wo.WalletId = (string)reader["WalletId"];
wo.Type = (string)reader["Type"];
wo.Id = (string)reader["Id"];
var id = new WalletObjectId(WalletId.Parse(wo.WalletId), wo.Type, wo.Id);
wo.Data = reader["Data"] is DBNull ? null : (string)reader["Data"];
if (wosById.TryGetValue(id, out var wo2))
wo = wo2;
else
{
wosById.Add(id, wo);
wo.Bs = new List<WalletObjectLinkData>();
}
if (reader["Type2"] is not DBNull)
{
var l = new WalletObjectLinkData()
{
BType = (string)reader["Type2"],
BId = (string)reader["Id2"],
Data = reader["LinkData"] is DBNull ? null : (string)reader["LinkData"]
};
wo.Bs.Add(l);
l.B = new WalletObjectData()
{
Type = l.BType,
Id = l.BId,
Data = (!queryObject.IncludeNeighbours || reader["Data2"] is DBNull) ? null : (string)reader["Data2"]
};
}
}
return wosById;
var walletIdParam = cmd.CreateParameter();
walletIdParam.ParameterName = "walletId";
walletIdParam.Value = queryObject.WalletId.ToString();
walletIdParam.DbType = System.Data.DbType.String;
cmd.Parameters.Add(walletIdParam);
}
else // Unefficient path
if (queryObject.Type != null)
{
IQueryable<WalletObjectData> q;
if (queryObject.TypesIds is not null)
var typeParam = cmd.CreateParameter();
typeParam.ParameterName = "type";
typeParam.Value = queryObject.Type;
typeParam.DbType = System.Data.DbType.String;
cmd.Parameters.Add(typeParam);
}
if (queryObject.TypesIds != null)
{
var typesParam = cmd.CreateParameter();
typesParam.ParameterName = "types";
typesParam.Value = queryObject.TypesIds.Select(t => t.Type).ToList();
typesParam.DbType = System.Data.DbType.Object;
cmd.Parameters.Add(typesParam);
var idParam = cmd.CreateParameter();
idParam.ParameterName = "ids";
idParam.Value = queryObject.TypesIds.Select(t => t.Id).ToList();
idParam.DbType = System.Data.DbType.Object;
cmd.Parameters.Add(idParam);
}
if (queryObject.Ids != null)
{
if (queryObject.Ids.Length == 1)
{
// Note this is problematic if the type contains '##', but I don't see how to do it properly with entity framework
var idTypes = queryObject.TypesIds.Select(o => $"{o.Type}##{o.Id}").ToArray();
q = ctx.WalletObjects
.Where(w => (queryObject.WalletId == null || w.WalletId == queryObject.WalletId.ToString()) && idTypes.Contains(w.Type + "##" + w.Id));
var txIdParam = cmd.CreateParameter();
txIdParam.ParameterName = "id";
txIdParam.Value = queryObject.Ids[0];
txIdParam.DbType = System.Data.DbType.String;
cmd.Parameters.Add(txIdParam);
}
else
{
q = ctx.WalletObjects
.Where(w => (queryObject.WalletId == null || w.WalletId == queryObject.WalletId.ToString()) && (queryObject.Type == null || w.Type == queryObject.Type) && (queryObject.Ids == null || queryObject.Ids.Contains(w.Id)));
var txIdsParam = cmd.CreateParameter();
txIdsParam.ParameterName = "ids";
txIdsParam.Value = queryObject.Ids.ToList();
txIdsParam.DbType = System.Data.DbType.Object;
cmd.Parameters.Add(txIdsParam);
}
if (queryObject.IncludeNeighbours)
{
q = q.Include(o => o.Bs).ThenInclude(o => o.B)
.Include(o => o.As).ThenInclude(o => o.A);
}
q = q.AsNoTracking();
var wosById = new Dictionary<WalletObjectId, WalletObjectData>();
foreach (var row in await q.ToListAsync())
{
var id = new WalletObjectId(WalletId.Parse(row.WalletId), row.Type, row.Id);
wosById.TryAdd(id, row);
}
return wosById;
}
await using var reader = await cmd.ExecuteReaderAsync();
var wosById = new Dictionary<WalletObjectId, WalletObjectData>();
while (await reader.ReadAsync())
{
WalletObjectData wo = new WalletObjectData();
wo.WalletId = (string)reader["WalletId"];
wo.Type = (string)reader["Type"];
wo.Id = (string)reader["Id"];
var id = new WalletObjectId(WalletId.Parse(wo.WalletId), wo.Type, wo.Id);
wo.Data = reader["Data"] is DBNull ? null : (string)reader["Data"];
if (wosById.TryGetValue(id, out var wo2))
wo = wo2;
else
{
wosById.Add(id, wo);
wo.Bs = new List<WalletObjectLinkData>();
}
if (reader["Type2"] is not DBNull)
{
var l = new WalletObjectLinkData()
{
BType = (string)reader["Type2"],
BId = (string)reader["Id2"],
Data = reader["LinkData"] is DBNull ? null : (string)reader["LinkData"]
};
wo.Bs.Add(l);
l.B = new WalletObjectData()
{
Type = l.BType,
Id = l.BId,
Data = (!queryObject.IncludeNeighbours || reader["Data2"] is DBNull) ? null : (string)reader["Data2"]
};
}
}
return wosById;
}
#nullable restore

View file

@ -176,10 +176,7 @@
}
</button>
</form>
@if (Model.CanDelete)
{
<a id="DeleteStore" class="btn btn-outline-danger" asp-action="DeleteStore" asp-route-storeId="@Model.Id" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="The store <strong>@Html.Encode(Model.StoreName)</strong> will be permanently deleted. This action will also delete all invoices, apps and data associated with the store." data-confirm-input="DELETE">Delete this store</a>
}
<a id="DeleteStore" class="btn btn-outline-danger" asp-action="DeleteStore" asp-route-storeId="@Model.Id" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="The store <strong>@Html.Encode(Model.StoreName)</strong> will be permanently deleted. This action will also delete all invoices, apps and data associated with the store." data-confirm-input="DELETE">Delete this store</a>
</div>
</div>
</div>