diff --git a/.gitignore b/.gitignore index 7226094e1..54dce8582 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,6 @@ launchSettings.json /frontend/app-config/localhost-key.pem /frontend/app-config/localhost.pem + +# C# language server cache +*.lscache diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/Migrations/DatabaseCreator.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Migrations/DatabaseCreator.cs deleted file mode 100644 index 852f918a4..000000000 --- a/backend/src/Squidex.Data.EntityFramework/Infrastructure/Migrations/DatabaseCreator.cs +++ /dev/null @@ -1,42 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage; -using Squidex.Hosting; - -#pragma warning disable RECS0108 // Warns about static fields in generic types - -namespace Squidex.Infrastructure.Migrations; - -public sealed class DatabaseCreator(IDbContextFactory dbContextFactory) : IInitializable - where TContext : DbContext -{ - private static readonly TimeSpan WaitTime = TimeSpan.FromSeconds(30); - - public int Order => -1000; - - public async Task InitializeAsync( - CancellationToken ct) - { - await using var dbContext = await dbContextFactory.CreateDbContextAsync(ct); - - using var cts = new CancellationTokenSource(WaitTime); - while (!await dbContext.Database.CanConnectAsync(cts.Token)) - { - await Task.Delay(100, cts.Token); - } - - if (dbContext.Database.GetService() is not RelationalDatabaseCreator relationalDatabaseCreator) - { - return; - } - - await relationalDatabaseCreator.EnsureCreatedAsync(ct); - } -} diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/Queries/SqlDialectInitializer.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Queries/SqlDialectInitializer.cs deleted file mode 100644 index 13a5efe71..000000000 --- a/backend/src/Squidex.Data.EntityFramework/Infrastructure/Queries/SqlDialectInitializer.cs +++ /dev/null @@ -1,26 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using Microsoft.EntityFrameworkCore; -using Squidex.Hosting; - -namespace Squidex.Infrastructure.Queries; - -public sealed class SqlDialectInitializer(IDbContextFactory dbContextFactory) - : IInitializable where TContext : DbContext -{ - public async Task InitializeAsync(CancellationToken ct) - { - await using var dbContext = await dbContextFactory.CreateDbContextAsync(ct); - if (dbContext is not IDbContextWithDialect withDialect) - { - return; - } - - await withDialect.Dialect.InitializeAsync(dbContext, ct); - } -} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/MySql/App/Migrations/20260512000000_AddJsonFunctions.Designer.cs b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/App/Migrations/20260512000000_AddJsonFunctions.Designer.cs new file mode 100644 index 000000000..7b85f9a41 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/App/Migrations/20260512000000_AddJsonFunctions.Designer.cs @@ -0,0 +1,1628 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NetTopologySuite.Geometries; +using Squidex.Providers.MySql.App; + +#nullable disable + +namespace Squidex.Providers.MySql.App.Migrations +{ + [DbContext(typeof(MySqlAppDbContext))] + [Migration("20260512000000_AddJsonFunctions")] + partial class AddJsonFunctions + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("longtext"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.Property("RoleId") + .HasColumnType("varchar(255)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("varchar(255)"); + + b.Property("ApplicationId") + .HasColumnType("varchar(255)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Properties") + .HasColumnType("longtext"); + + b.Property("Scopes") + .HasColumnType("longtext"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("varchar(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("varchar(255)"); + + b.Property("ApplicationId") + .HasColumnType("varchar(255)"); + + b.Property("AuthorizationId") + .HasColumnType("varchar(255)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("Payload") + .HasColumnType("longtext"); + + b.Property("Properties") + .HasColumnType("longtext"); + + b.Property("RedemptionDate") + .HasColumnType("datetime(6)"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("varchar(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", (string)null); + }); + + modelBuilder.Entity("Squidex.AI.EntityFramework.EFChatEntity", b => + { + b.Property("Id") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("LastUpdated") + .HasColumnType("datetime(6)"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Version") + .IsConcurrencyToken() + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("LastUpdated"); + + b.ToTable("Chats", (string)null); + }); + + modelBuilder.Entity("Squidex.Assets.EntityFramework.EFAssetKeyValueEntity", b => + { + b.Property("Key") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Expires") + .HasColumnType("datetime(6)"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Key"); + + b.HasIndex("Expires"); + + b.ToTable("AssetKeyValueStore_TusMetadata", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Apps.EFAppEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("IndexedCreated") + .HasColumnType("datetime(6)") + .HasColumnName("Created"); + + b.Property("IndexedDeleted") + .HasColumnType("tinyint(1)") + .HasColumnName("Deleted"); + + b.Property("IndexedName") + .IsRequired() + .HasColumnType("longtext") + .HasColumnName("Name"); + + b.Property("IndexedTeamId") + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("TeamId"); + + b.Property("IndexedUserIds") + .IsRequired() + .HasColumnType("longtext") + .HasColumnName("UserIds"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_App", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Assets.EFAssetEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Created") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("FileHash") + .HasColumnType("longtext"); + + b.Property("FileName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("FileVersion") + .HasColumnType("bigint"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)"); + + b.Property("IsProtected") + .HasColumnType("tinyint(1)"); + + b.Property("LastModified") + .HasColumnType("datetime(6)"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Metadata") + .IsRequired() + .HasColumnType("json"); + + b.Property("MimeType") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ParentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Slug") + .HasColumnType("longtext"); + + b.Property("Tags") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("TotalSize") + .HasColumnType("bigint"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.HasIndex("IndexedAppId", "Id"); + + b.ToTable("Assets"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Assets.EFAssetFolderEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Created") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("FolderName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)"); + + b.Property("LastModified") + .HasColumnType("datetime(6)"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("ParentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.HasIndex("IndexedAppId", "Id"); + + b.ToTable("AssetFolders"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFContentCompleteEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Created") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("json"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IndexedSchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)"); + + b.Property("LastModified") + .HasColumnType("datetime(6)"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("NewData") + .HasColumnType("json"); + + b.Property("NewStatus") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("ScheduleJob") + .HasColumnType("json"); + + b.Property("ScheduledAt") + .HasColumnType("datetime(6)"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("TranslationStatus") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("ContentsAll", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFContentPublishedEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Created") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("json"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IndexedSchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)"); + + b.Property("LastModified") + .HasColumnType("datetime(6)"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("NewData") + .HasColumnType("json"); + + b.Property("NewStatus") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("ScheduleJob") + .HasColumnType("json"); + + b.Property("ScheduledAt") + .HasColumnType("datetime(6)"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("TranslationStatus") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("ContentsPublished", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFContentTableEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("AppId", "SchemaId") + .IsUnique(); + + b.ToTable("ContentTables", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFReferenceCompleteEntity", b => + { + b.Property("AppId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("FromKey") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("ToId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("FromSchema") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasDefaultValue("00000000-0000-0000-0000-000000000000"); + + b.HasKey("AppId", "FromKey", "ToId"); + + b.ToTable("ContentReferencesAll", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFReferencePublishedEntity", b => + { + b.Property("AppId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("FromKey") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("ToId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("FromSchema") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasDefaultValue("00000000-0000-0000-0000-000000000000"); + + b.HasKey("AppId", "FromKey", "ToId"); + + b.ToTable("ContentReferencesPublished", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.Text.EFTextIndexGeoEntity", b => + { + b.Property("Id") + .HasMaxLength(400) + .HasColumnType("varchar(400)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("ContentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("GeoField") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("GeoObject") + .IsRequired() + .HasColumnType("geometry"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("ServeAll") + .HasColumnType("tinyint(1)"); + + b.Property("ServePublished") + .HasColumnType("tinyint(1)"); + + b.Property("Stage") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.ToTable("Geos"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.Text.EFTextIndexTextEntity", b => + { + b.Property("Id") + .HasMaxLength(400) + .HasColumnType("varchar(400)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("ContentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("ServeAll") + .HasColumnType("tinyint(1)"); + + b.Property("ServePublished") + .HasColumnType("tinyint(1)"); + + b.Property("Stage") + .HasColumnType("tinyint unsigned"); + + b.Property("Texts") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Texts"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.Text.EFTextIndexUserInfoEntity", b => + { + b.Property("Id") + .HasMaxLength(400) + .HasColumnType("varchar(400)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("ContentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("ServeAll") + .HasColumnType("tinyint(1)"); + + b.Property("ServePublished") + .HasColumnType("tinyint(1)"); + + b.Property("Stage") + .HasColumnType("tinyint unsigned"); + + b.Property("UserInfoApiKey") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("UserInfoRole") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("UserInfoApiKey"); + + b.ToTable("UserInfos"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.Text.State.TextContentState", b => + { + b.Property("UniqueContentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("State") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("UniqueContentId"); + + b.ToTable("TextState", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.History.HistoryEvent", b => + { + b.Property("Id") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Channel") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Created") + .HasColumnType("datetime(6)"); + + b.Property("EventType") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Parameters") + .IsRequired() + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("HistoryEvent"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Rules.EFRuleEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("AppId"); + + b.Property("IndexedDeleted") + .HasColumnType("tinyint(1)") + .HasColumnName("Deleted"); + + b.Property("IndexedId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("Id"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Rule", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Schemas.EFSchemaEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("AppId"); + + b.Property("IndexedDeleted") + .HasColumnType("tinyint(1)") + .HasColumnName("Deleted"); + + b.Property("IndexedId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("Id"); + + b.Property("IndexedName") + .IsRequired() + .HasColumnType("longtext") + .HasColumnName("Name"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Schema", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Teams.EFTeamEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("IndexedAuthDomain") + .HasColumnType("longtext") + .HasColumnName("AuthDomain"); + + b.Property("IndexedDeleted") + .HasColumnType("tinyint(1)") + .HasColumnName("Deleted"); + + b.Property("IndexedUserIds") + .IsRequired() + .HasColumnType("longtext") + .HasColumnName("UserIds"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Team", (string)null); + }); + + modelBuilder.Entity("Squidex.Events.EntityFramework.EFEventCommit", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("EventStream") + .IsRequired() + .HasMaxLength(750) + .HasColumnType("varchar(750)"); + + b.Property("EventStreamOffset") + .HasColumnType("bigint"); + + b.PrimitiveCollection("Events") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("EventsCount") + .HasColumnType("bigint"); + + b.Property("Position") + .HasColumnType("bigint"); + + b.Property("Timestamp") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("EventStream", "EventStreamOffset") + .IsUnique(); + + b.HasIndex("EventStream", "Position"); + + b.HasIndex("EventStream", "Timestamp"); + + b.ToTable("Events"); + }); + + modelBuilder.Entity("Squidex.Events.EntityFramework.EFPosition", b => + { + b.Property("Id") + .HasColumnType("bigint"); + + b.Property("Position") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("EventPosition"); + }); + + modelBuilder.Entity("Squidex.Flows.EntityFramework.EFCronJobEntity", b => + { + b.Property("Id") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("DueTime") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("DueTime"); + + b.ToTable("CronJobs", (string)null); + }); + + modelBuilder.Entity("Squidex.Flows.EntityFramework.EFFlowStateEntity", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Created") + .HasColumnType("datetime(6)"); + + b.Property("DefinitionId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("DueTime") + .HasColumnType("datetime(6)"); + + b.Property("OwnerId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("SchedulePartition") + .HasColumnType("int"); + + b.Property("State") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("DueTime", "SchedulePartition"); + + b.ToTable("Flows", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Caching.EFCacheEntity", b => + { + b.Property("Key") + .HasColumnType("varchar(255)"); + + b.Property("Expires") + .HasColumnType("datetime(6)"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longblob"); + + b.HasKey("Key"); + + b.ToTable("Cache", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Log.EFRequestEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Key") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("Properties") + .IsRequired() + .HasColumnType("json"); + + b.Property("Timestamp") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("Key"); + + b.ToTable("Requests", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Migrations.EFMigrationEntity", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("IsLocked") + .HasColumnType("tinyint(1)"); + + b.Property("Version") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Migrations", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UISettings", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Index_TagHistory", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UsageNotifications", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Counters", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_JobsState", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UsageTracker", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Index_Tags", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Identity_Keys", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Identity_Xml", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_EventConsumerState", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Names", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.UsageTracking.EFUsageCounterEntity", b => + { + b.Property("Key") + .HasColumnType("varchar(255)"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("Category") + .HasColumnType("varchar(255)"); + + b.Property("CounterKey") + .HasColumnType("varchar(255)"); + + b.Property("CounterValue") + .HasColumnType("double"); + + b.HasKey("Key", "Date", "Category", "CounterKey"); + + b.ToTable("Counter", (string)null); + }); + + modelBuilder.Entity("Squidex.Messaging.EntityFramework.EFMessage", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("ChannelName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("MessageData") + .IsRequired() + .HasColumnType("longblob"); + + b.Property("MessageHeaders") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("varchar(2000)"); + + b.Property("QueueName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("TimeHandled") + .HasColumnType("datetime(6)"); + + b.Property("TimeToLive") + .HasColumnType("datetime(6)"); + + b.Property("Version") + .IsConcurrencyToken() + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ChannelName", "TimeHandled"); + + b.ToTable("Messages", (string)null); + }); + + modelBuilder.Entity("Squidex.Messaging.EntityFramework.EFMessagingDataEntity", b => + { + b.Property("Group") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Key") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Expiration") + .HasColumnType("datetime(6)"); + + b.Property("ValueData") + .IsRequired() + .HasColumnType("longblob"); + + b.Property("ValueFormat") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("ValueType") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.HasKey("Group", "Key"); + + b.HasIndex("Expiration"); + + b.ToTable("MessagingData", (string)null); + }); + + modelBuilder.Entity("YDotNet.Server.EntityFramework.YDotNetDocument", b => + { + b.Property("Id") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("longblob"); + + b.Property("Expiration") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.ToTable("YDotNetDocument", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/MySql/App/Migrations/20260512000000_AddJsonFunctions.cs b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/App/Migrations/20260512000000_AddJsonFunctions.cs new file mode 100644 index 000000000..9eabada7b --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/App/Migrations/20260512000000_AddJsonFunctions.cs @@ -0,0 +1,67 @@ +using System.IO; +using Microsoft.EntityFrameworkCore.Migrations; +using Squidex.Providers.MySql; + +#nullable disable + +namespace Squidex.Providers.MySql.App.Migrations +{ + /// + public partial class AddJsonFunctions : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + var assembly = typeof(MySqlDialect).Assembly; + + using var sqlStream = assembly.GetManifestResourceStream("Squidex.Providers.MySql.json_function.sql")!; + using var reader = new StreamReader(sqlStream); + + var sqlText = reader.ReadToEnd(); + var statements = sqlText.Split(";;", System.StringSplitOptions.RemoveEmptyEntries | System.StringSplitOptions.TrimEntries); + + foreach (var statement in statements) + { + migrationBuilder.Sql(statement, suppressTransaction: true); + } + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + var functions = new[] + { + "json_empty", + "json_exists", + "json_null_equals", + "json_null_notequals", + "json_text_contains", + "json_text_endswith", + "json_text_equals", + "json_text_greaterthan", + "json_text_greaterthanorequal", + "json_text_in", + "json_text_lessthan", + "json_text_lessthanorequal", + "json_text_matchs", + "json_text_notequals", + "json_text_startswith", + "json_boolean_equals", + "json_boolean_in", + "json_boolean_notequals", + "json_number_equals", + "json_number_greaterthan", + "json_number_greaterthanorequal", + "json_number_in", + "json_number_lessthan", + "json_number_lessthanorequal", + "json_number_notequals", + }; + + foreach (var function in functions) + { + migrationBuilder.Sql($"DROP FUNCTION IF EXISTS {function}", suppressTransaction: true); + } + } + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/MySql/JsonFunction.cs b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/JsonFunction.cs index 425d78703..b75f041bb 100644 --- a/backend/src/Squidex.Data.EntityFramework/Providers/MySql/JsonFunction.cs +++ b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/JsonFunction.cs @@ -57,15 +57,8 @@ public static class JsonFunction sqlText = sqlText.Replace("}", "}}", StringComparison.Ordinal); var statements = sqlText.Split(";;", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); - // We want to filter out the drop statements and multiple function creations are not supported. foreach (var statement in statements) { -#if RELEASE - if (statement.StartsWith("DROP", StringComparison.Ordinal)) - { - continue; - } -#endif await dbContext.Database.ExecuteSqlRawAsync(statement, ct); } } diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/App/Migrations/20260512000002_AddJsonFunctions.Designer.cs b/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/App/Migrations/20260512000002_AddJsonFunctions.Designer.cs new file mode 100644 index 000000000..2a288f4a4 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/App/Migrations/20260512000002_AddJsonFunctions.Designer.cs @@ -0,0 +1,1629 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NetTopologySuite.Geometries; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Squidex.Providers.Postgres.App; + +#nullable disable + +namespace Squidex.Providers.Postgres.App.Migrations +{ + [DbContext(typeof(PostgresAppDbContext))] + [Migration("20260512000002_AddJsonFunctions")] + partial class AddJsonFunctions + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "postgis"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("text"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", (string)null); + }); + + modelBuilder.Entity("Squidex.AI.EntityFramework.EFChatEntity", b => + { + b.Property("Id") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("LastUpdated") + .HasColumnType("timestamp with time zone"); + + b.Property("Value") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("LastUpdated"); + + b.ToTable("Chats", (string)null); + }); + + modelBuilder.Entity("Squidex.Assets.EntityFramework.EFAssetKeyValueEntity", b => + { + b.Property("Key") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Expires") + .HasColumnType("timestamp with time zone"); + + b.Property("Value") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Key"); + + b.HasIndex("Expires"); + + b.ToTable("AssetKeyValueStore_TusMetadata", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Apps.EFAppEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("IndexedCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("Created"); + + b.Property("IndexedDeleted") + .HasColumnType("boolean") + .HasColumnName("Deleted"); + + b.Property("IndexedName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Name"); + + b.Property("IndexedTeamId") + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("TeamId"); + + b.Property("IndexedUserIds") + .IsRequired() + .HasColumnType("text") + .HasColumnName("UserIds"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_App", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Assets.EFAssetEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("FileHash") + .HasColumnType("text"); + + b.Property("FileName") + .IsRequired() + .HasColumnType("text"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("FileVersion") + .HasColumnType("bigint"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("IsProtected") + .HasColumnType("boolean"); + + b.Property("LastModified") + .HasColumnType("timestamp with time zone"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Metadata") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("MimeType") + .IsRequired() + .HasColumnType("text"); + + b.Property("ParentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Slug") + .HasColumnType("text"); + + b.Property("Tags") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("TotalSize") + .HasColumnType("bigint"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.HasIndex("IndexedAppId", "Id"); + + b.ToTable("Assets"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Assets.EFAssetFolderEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("FolderName") + .IsRequired() + .HasColumnType("text"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastModified") + .HasColumnType("timestamp with time zone"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ParentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.HasIndex("IndexedAppId", "Id"); + + b.ToTable("AssetFolders"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFContentCompleteEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IndexedSchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastModified") + .HasColumnType("timestamp with time zone"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("NewData") + .HasColumnType("jsonb"); + + b.Property("NewStatus") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ScheduleJob") + .HasColumnType("jsonb"); + + b.Property("ScheduledAt") + .HasColumnType("timestamp with time zone"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TranslationStatus") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("ContentsAll", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFContentPublishedEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IndexedSchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastModified") + .HasColumnType("timestamp with time zone"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("NewData") + .HasColumnType("jsonb"); + + b.Property("NewStatus") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ScheduleJob") + .HasColumnType("jsonb"); + + b.Property("ScheduledAt") + .HasColumnType("timestamp with time zone"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TranslationStatus") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("ContentsPublished", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFContentTableEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.HasIndex("AppId", "SchemaId") + .IsUnique(); + + b.ToTable("ContentTables", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFReferenceCompleteEntity", b => + { + b.Property("AppId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("FromKey") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ToId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("FromSchema") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasDefaultValue("00000000-0000-0000-0000-000000000000"); + + b.HasKey("AppId", "FromKey", "ToId"); + + b.ToTable("ContentReferencesAll", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFReferencePublishedEntity", b => + { + b.Property("AppId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("FromKey") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ToId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("FromSchema") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasDefaultValue("00000000-0000-0000-0000-000000000000"); + + b.HasKey("AppId", "FromKey", "ToId"); + + b.ToTable("ContentReferencesPublished", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.Text.EFTextIndexGeoEntity", b => + { + b.Property("Id") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ContentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("GeoField") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("GeoObject") + .IsRequired() + .HasColumnType("geometry"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ServeAll") + .HasColumnType("boolean"); + + b.Property("ServePublished") + .HasColumnType("boolean"); + + b.Property("Stage") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.ToTable("Geos"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.Text.EFTextIndexTextEntity", b => + { + b.Property("Id") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ContentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ServeAll") + .HasColumnType("boolean"); + + b.Property("ServePublished") + .HasColumnType("boolean"); + + b.Property("Stage") + .HasColumnType("smallint"); + + b.Property("Texts") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Texts"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.Text.EFTextIndexUserInfoEntity", b => + { + b.Property("Id") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ContentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ServeAll") + .HasColumnType("boolean"); + + b.Property("ServePublished") + .HasColumnType("boolean"); + + b.Property("Stage") + .HasColumnType("smallint"); + + b.Property("UserInfoApiKey") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("UserInfoRole") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("UserInfoApiKey"); + + b.ToTable("UserInfos"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.Text.State.TextContentState", b => + { + b.Property("UniqueContentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("State") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("UniqueContentId"); + + b.ToTable("TextState", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.History.HistoryEvent", b => + { + b.Property("Id") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Channel") + .IsRequired() + .HasColumnType("text"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("EventType") + .IsRequired() + .HasColumnType("text"); + + b.Property("OwnerId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Parameters") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("HistoryEvent"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Rules.EFRuleEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("AppId"); + + b.Property("IndexedDeleted") + .HasColumnType("boolean") + .HasColumnName("Deleted"); + + b.Property("IndexedId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Id"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Rule", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Schemas.EFSchemaEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("AppId"); + + b.Property("IndexedDeleted") + .HasColumnType("boolean") + .HasColumnName("Deleted"); + + b.Property("IndexedId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Id"); + + b.Property("IndexedName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Name"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Schema", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Teams.EFTeamEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("IndexedAuthDomain") + .HasColumnType("text") + .HasColumnName("AuthDomain"); + + b.Property("IndexedDeleted") + .HasColumnType("boolean") + .HasColumnName("Deleted"); + + b.Property("IndexedUserIds") + .IsRequired() + .HasColumnType("text") + .HasColumnName("UserIds"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Team", (string)null); + }); + + modelBuilder.Entity("Squidex.Events.EntityFramework.EFEventCommit", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("EventStream") + .IsRequired() + .HasMaxLength(750) + .HasColumnType("character varying(750)"); + + b.Property("EventStreamOffset") + .HasColumnType("bigint"); + + b.PrimitiveCollection("Events") + .IsRequired() + .HasColumnType("text[]"); + + b.Property("EventsCount") + .HasColumnType("bigint"); + + b.Property("Position") + .HasColumnType("bigint"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("EventStream", "EventStreamOffset") + .IsUnique(); + + b.HasIndex("EventStream", "Position"); + + b.HasIndex("EventStream", "Timestamp"); + + b.ToTable("Events"); + }); + + modelBuilder.Entity("Squidex.Events.EntityFramework.EFPosition", b => + { + b.Property("Id") + .HasColumnType("bigint"); + + b.Property("Position") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("EventPosition"); + }); + + modelBuilder.Entity("Squidex.Flows.EntityFramework.EFCronJobEntity", b => + { + b.Property("Id") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("text"); + + b.Property("DueTime") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("DueTime"); + + b.ToTable("CronJobs", (string)null); + }); + + modelBuilder.Entity("Squidex.Flows.EntityFramework.EFFlowStateEntity", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("DefinitionId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("DueTime") + .HasColumnType("timestamp with time zone"); + + b.Property("OwnerId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SchedulePartition") + .HasColumnType("integer"); + + b.Property("State") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DueTime", "SchedulePartition"); + + b.ToTable("Flows", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Caching.EFCacheEntity", b => + { + b.Property("Key") + .HasColumnType("text"); + + b.Property("Expires") + .HasColumnType("timestamp with time zone"); + + b.Property("Value") + .IsRequired() + .HasColumnType("bytea"); + + b.HasKey("Key"); + + b.ToTable("Cache", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Log.EFRequestEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Key") + .IsRequired() + .HasColumnType("text"); + + b.Property("Properties") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("Key"); + + b.ToTable("Requests", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Migrations.EFMigrationEntity", b => + { + b.Property("Id") + .HasColumnType("integer"); + + b.Property("IsLocked") + .HasColumnType("boolean"); + + b.Property("Version") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("Migrations", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UISettings", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Index_TagHistory", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UsageNotifications", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Counters", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_JobsState", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UsageTracker", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Index_Tags", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Identity_Keys", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Identity_Xml", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_EventConsumerState", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Names", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.UsageTracking.EFUsageCounterEntity", b => + { + b.Property("Key") + .HasColumnType("text"); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("Category") + .HasColumnType("text"); + + b.Property("CounterKey") + .HasColumnType("text"); + + b.Property("CounterValue") + .HasColumnType("double precision"); + + b.HasKey("Key", "Date", "Category", "CounterKey"); + + b.ToTable("Counter", (string)null); + }); + + modelBuilder.Entity("Squidex.Messaging.EntityFramework.EFMessage", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ChannelName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("MessageData") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("MessageHeaders") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("QueueName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("TimeHandled") + .HasColumnType("timestamp with time zone"); + + b.Property("TimeToLive") + .HasColumnType("timestamp with time zone"); + + b.Property("Version") + .IsConcurrencyToken() + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ChannelName", "TimeHandled"); + + b.ToTable("Messages", (string)null); + }); + + modelBuilder.Entity("Squidex.Messaging.EntityFramework.EFMessagingDataEntity", b => + { + b.Property("Group") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Key") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Expiration") + .HasColumnType("timestamp with time zone"); + + b.Property("ValueData") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("ValueFormat") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ValueType") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Group", "Key"); + + b.HasIndex("Expiration"); + + b.ToTable("MessagingData", (string)null); + }); + + modelBuilder.Entity("YDotNet.Server.EntityFramework.YDotNetDocument", b => + { + b.Property("Id") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("Expiration") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("YDotNetDocument", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/App/Migrations/20260512000002_AddJsonFunctions.cs b/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/App/Migrations/20260512000002_AddJsonFunctions.cs new file mode 100644 index 000000000..988c75f5b --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/App/Migrations/20260512000002_AddJsonFunctions.cs @@ -0,0 +1,59 @@ +using System.IO; +using Microsoft.EntityFrameworkCore.Migrations; +using Squidex.Providers.Postgres; + +#nullable disable + +namespace Squidex.Providers.Postgres.App.Migrations +{ + /// + public partial class AddJsonFunctions : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + var assembly = typeof(PostgresDialect).Assembly; + + using var sqlStream = assembly.GetManifestResourceStream("Squidex.Providers.Postgres.json_function.sql")!; + using var reader = new StreamReader(sqlStream); + + var sqlText = reader.ReadToEnd(); + var statements = sqlText.Split(";;", System.StringSplitOptions.RemoveEmptyEntries | System.StringSplitOptions.TrimEntries); + + foreach (var statement in statements) + { + migrationBuilder.Sql(statement); + } + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql("DROP FUNCTION IF EXISTS jsonb_empty(jsonb)"); + migrationBuilder.Sql("DROP FUNCTION IF EXISTS jsonb_exists(jsonb)"); + migrationBuilder.Sql("DROP FUNCTION IF EXISTS jsonb_null_equals(jsonb)"); + migrationBuilder.Sql("DROP FUNCTION IF EXISTS jsonb_null_notequals(jsonb)"); + migrationBuilder.Sql("DROP FUNCTION IF EXISTS jsonb_text_equals(jsonb, text)"); + migrationBuilder.Sql("DROP FUNCTION IF EXISTS jsonb_text_notequals(jsonb, text)"); + migrationBuilder.Sql("DROP FUNCTION IF EXISTS jsonb_text_lessthan(jsonb, text)"); + migrationBuilder.Sql("DROP FUNCTION IF EXISTS jsonb_text_lessthanorequal(jsonb, text)"); + migrationBuilder.Sql("DROP FUNCTION IF EXISTS jsonb_text_greaterthan(jsonb, text)"); + migrationBuilder.Sql("DROP FUNCTION IF EXISTS jsonb_text_greaterthanorequal(jsonb, text)"); + migrationBuilder.Sql("DROP FUNCTION IF EXISTS jsonb_text_contains(jsonb, text)"); + migrationBuilder.Sql("DROP FUNCTION IF EXISTS jsonb_text_startswith(jsonb, text)"); + migrationBuilder.Sql("DROP FUNCTION IF EXISTS jsonb_text_endswith(jsonb, text)"); + migrationBuilder.Sql("DROP FUNCTION IF EXISTS jsonb_text_matchs(jsonb, text)"); + migrationBuilder.Sql("DROP FUNCTION IF EXISTS jsonb_text_in(jsonb, text[])"); + migrationBuilder.Sql("DROP FUNCTION IF EXISTS jsonb_number_equals(jsonb, numeric)"); + migrationBuilder.Sql("DROP FUNCTION IF EXISTS jsonb_number_notequals(jsonb, numeric)"); + migrationBuilder.Sql("DROP FUNCTION IF EXISTS jsonb_number_lessthan(jsonb, numeric)"); + migrationBuilder.Sql("DROP FUNCTION IF EXISTS jsonb_number_lessthanorequal(jsonb, numeric)"); + migrationBuilder.Sql("DROP FUNCTION IF EXISTS jsonb_number_greaterthan(jsonb, numeric)"); + migrationBuilder.Sql("DROP FUNCTION IF EXISTS jsonb_number_greaterthanorequal(jsonb, numeric)"); + migrationBuilder.Sql("DROP FUNCTION IF EXISTS jsonb_number_in(jsonb, numeric[])"); + migrationBuilder.Sql("DROP FUNCTION IF EXISTS jsonb_boolean_equals(jsonb, boolean)"); + migrationBuilder.Sql("DROP FUNCTION IF EXISTS jsonb_boolean_notequals(jsonb, boolean)"); + migrationBuilder.Sql("DROP FUNCTION IF EXISTS jsonb_boolean_in(jsonb, boolean[])"); + } + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/App/Migrations/20260512000001_AddJsonFunctions.Designer.cs b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/App/Migrations/20260512000001_AddJsonFunctions.Designer.cs new file mode 100644 index 000000000..9834a7d41 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/App/Migrations/20260512000001_AddJsonFunctions.Designer.cs @@ -0,0 +1,1631 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NetTopologySuite.Geometries; +using Squidex.Providers.SqlServer.App; + +#nullable disable + +namespace Squidex.Providers.SqlServer.App.Migrations +{ + [DbContext(typeof(SqlServerAppDbContext))] + [Migration("20260512000001_AddJsonFunctions")] + partial class AddJsonFunctions + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("nvarchar(450)"); + + b.Property("ApplicationId") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("Properties") + .HasColumnType("nvarchar(max)"); + + b.Property("Scopes") + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("nvarchar(450)"); + + b.Property("ApplicationId") + .HasColumnType("nvarchar(450)"); + + b.Property("AuthorizationId") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("ExpirationDate") + .HasColumnType("datetime2"); + + b.Property("Payload") + .HasColumnType("nvarchar(max)"); + + b.Property("Properties") + .HasColumnType("nvarchar(max)"); + + b.Property("RedemptionDate") + .HasColumnType("datetime2"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique() + .HasFilter("[ReferenceId] IS NOT NULL"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", (string)null); + }); + + modelBuilder.Entity("Squidex.AI.EntityFramework.EFChatEntity", b => + { + b.Property("Id") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("LastUpdated") + .HasColumnType("datetime2"); + + b.Property("Value") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .IsConcurrencyToken() + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("LastUpdated"); + + b.ToTable("Chats", (string)null); + }); + + modelBuilder.Entity("Squidex.Assets.EntityFramework.EFAssetKeyValueEntity", b => + { + b.Property("Key") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Expires") + .HasColumnType("datetimeoffset"); + + b.Property("Value") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Key"); + + b.HasIndex("Expires"); + + b.ToTable("AssetKeyValueStore_TusMetadata", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Apps.EFAppEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("IndexedCreated") + .HasColumnType("datetimeoffset") + .HasColumnName("Created"); + + b.Property("IndexedDeleted") + .HasColumnType("bit") + .HasColumnName("Deleted"); + + b.Property("IndexedName") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("Name"); + + b.Property("IndexedTeamId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("TeamId"); + + b.Property("IndexedUserIds") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("UserIds"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_App", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Assets.EFAssetEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("FileHash") + .HasColumnType("nvarchar(max)"); + + b.Property("FileName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("FileVersion") + .HasColumnType("bigint"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("IsProtected") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetimeoffset"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Metadata") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("MimeType") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ParentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Slug") + .HasColumnType("nvarchar(max)"); + + b.Property("Tags") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("TotalSize") + .HasColumnType("bigint"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.HasIndex("IndexedAppId", "Id"); + + b.ToTable("Assets"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Assets.EFAssetFolderEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("FolderName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetimeoffset"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ParentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.HasIndex("IndexedAppId", "Id"); + + b.ToTable("AssetFolders"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFContentCompleteEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IndexedSchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetimeoffset"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("NewData") + .HasColumnType("nvarchar(max)"); + + b.Property("NewStatus") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ScheduleJob") + .HasColumnType("nvarchar(max)"); + + b.Property("ScheduledAt") + .HasColumnType("datetimeoffset"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("TranslationStatus") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("ContentsAll", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFContentPublishedEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IndexedSchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetimeoffset"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("NewData") + .HasColumnType("nvarchar(max)"); + + b.Property("NewStatus") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ScheduleJob") + .HasColumnType("nvarchar(max)"); + + b.Property("ScheduledAt") + .HasColumnType("datetimeoffset"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("TranslationStatus") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("ContentsPublished", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFContentTableEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("AppId", "SchemaId") + .IsUnique(); + + b.ToTable("ContentTables", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFReferenceCompleteEntity", b => + { + b.Property("AppId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("FromKey") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("ToId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("FromSchema") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasDefaultValue("00000000-0000-0000-0000-000000000000"); + + b.HasKey("AppId", "FromKey", "ToId"); + + b.ToTable("ContentReferencesAll", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFReferencePublishedEntity", b => + { + b.Property("AppId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("FromKey") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("ToId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("FromSchema") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasDefaultValue("00000000-0000-0000-0000-000000000000"); + + b.HasKey("AppId", "FromKey", "ToId"); + + b.ToTable("ContentReferencesPublished", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.Text.EFTextIndexGeoEntity", b => + { + b.Property("Id") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("ContentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("GeoField") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("GeoObject") + .IsRequired() + .HasColumnType("geography"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("ServeAll") + .HasColumnType("bit"); + + b.Property("ServePublished") + .HasColumnType("bit"); + + b.Property("Stage") + .HasColumnType("tinyint"); + + b.HasKey("Id"); + + b.ToTable("Geos"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.Text.EFTextIndexTextEntity", b => + { + b.Property("Id") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("ContentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("ServeAll") + .HasColumnType("bit"); + + b.Property("ServePublished") + .HasColumnType("bit"); + + b.Property("Stage") + .HasColumnType("tinyint"); + + b.Property("Texts") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Texts"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.Text.EFTextIndexUserInfoEntity", b => + { + b.Property("Id") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("ContentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("ServeAll") + .HasColumnType("bit"); + + b.Property("ServePublished") + .HasColumnType("bit"); + + b.Property("Stage") + .HasColumnType("tinyint"); + + b.Property("UserInfoApiKey") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("UserInfoRole") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("UserInfoApiKey"); + + b.ToTable("UserInfos"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.Text.State.TextContentState", b => + { + b.Property("UniqueContentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("State") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("UniqueContentId"); + + b.ToTable("TextState", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.History.HistoryEvent", b => + { + b.Property("Id") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Channel") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("EventType") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("OwnerId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Parameters") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("HistoryEvent"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Rules.EFRuleEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("AppId"); + + b.Property("IndexedDeleted") + .HasColumnType("bit") + .HasColumnName("Deleted"); + + b.Property("IndexedId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Id"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Rule", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Schemas.EFSchemaEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("AppId"); + + b.Property("IndexedDeleted") + .HasColumnType("bit") + .HasColumnName("Deleted"); + + b.Property("IndexedId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Id"); + + b.Property("IndexedName") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("Name"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Schema", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Teams.EFTeamEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("IndexedAuthDomain") + .HasColumnType("nvarchar(max)") + .HasColumnName("AuthDomain"); + + b.Property("IndexedDeleted") + .HasColumnType("bit") + .HasColumnName("Deleted"); + + b.Property("IndexedUserIds") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("UserIds"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Team", (string)null); + }); + + modelBuilder.Entity("Squidex.Events.EntityFramework.EFEventCommit", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("EventStream") + .IsRequired() + .HasMaxLength(750) + .HasColumnType("nvarchar(750)"); + + b.Property("EventStreamOffset") + .HasColumnType("bigint"); + + b.PrimitiveCollection("Events") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("EventsCount") + .HasColumnType("bigint"); + + b.Property("Position") + .HasColumnType("bigint"); + + b.Property("Timestamp") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.HasIndex("EventStream", "EventStreamOffset") + .IsUnique(); + + b.HasIndex("EventStream", "Position"); + + b.HasIndex("EventStream", "Timestamp"); + + b.ToTable("Events"); + }); + + modelBuilder.Entity("Squidex.Events.EntityFramework.EFPosition", b => + { + b.Property("Id") + .HasColumnType("bigint"); + + b.Property("Position") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("EventPosition"); + }); + + modelBuilder.Entity("Squidex.Flows.EntityFramework.EFCronJobEntity", b => + { + b.Property("Id") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("DueTime") + .HasColumnType("datetimeoffset"); + + b.HasKey("Id"); + + b.HasIndex("DueTime"); + + b.ToTable("CronJobs", (string)null); + }); + + modelBuilder.Entity("Squidex.Flows.EntityFramework.EFFlowStateEntity", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("DefinitionId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("DueTime") + .HasColumnType("datetimeoffset"); + + b.Property("OwnerId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("SchedulePartition") + .HasColumnType("int"); + + b.Property("State") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("DueTime", "SchedulePartition"); + + b.ToTable("Flows", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Caching.EFCacheEntity", b => + { + b.Property("Key") + .HasColumnType("nvarchar(450)"); + + b.Property("Expires") + .HasColumnType("datetime2"); + + b.Property("Value") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.HasKey("Key"); + + b.ToTable("Cache", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Log.EFRequestEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Key") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("Properties") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Timestamp") + .HasColumnType("datetimeoffset"); + + b.HasKey("Id"); + + b.HasIndex("Key"); + + b.ToTable("Requests", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Migrations.EFMigrationEntity", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("IsLocked") + .HasColumnType("bit"); + + b.Property("Version") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Migrations", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UISettings", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Index_TagHistory", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UsageNotifications", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Counters", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_JobsState", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UsageTracker", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Index_Tags", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Identity_Keys", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Identity_Xml", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_EventConsumerState", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Names", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.UsageTracking.EFUsageCounterEntity", b => + { + b.Property("Key") + .HasColumnType("nvarchar(450)"); + + b.Property("Date") + .HasColumnType("datetime2"); + + b.Property("Category") + .HasColumnType("nvarchar(450)"); + + b.Property("CounterKey") + .HasColumnType("nvarchar(450)"); + + b.Property("CounterValue") + .HasColumnType("float"); + + b.HasKey("Key", "Date", "Category", "CounterKey"); + + b.ToTable("Counter", (string)null); + }); + + modelBuilder.Entity("Squidex.Messaging.EntityFramework.EFMessage", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ChannelName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("MessageData") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("MessageHeaders") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("QueueName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("TimeHandled") + .HasColumnType("datetime2"); + + b.Property("TimeToLive") + .HasColumnType("datetime2"); + + b.Property("Version") + .IsConcurrencyToken() + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("ChannelName", "TimeHandled"); + + b.ToTable("Messages", (string)null); + }); + + modelBuilder.Entity("Squidex.Messaging.EntityFramework.EFMessagingDataEntity", b => + { + b.Property("Group") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Key") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Expiration") + .HasColumnType("datetime2"); + + b.Property("ValueData") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("ValueFormat") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("ValueType") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.HasKey("Group", "Key"); + + b.HasIndex("Expiration"); + + b.ToTable("MessagingData", (string)null); + }); + + modelBuilder.Entity("YDotNet.Server.EntityFramework.YDotNetDocument", b => + { + b.Property("Id") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("Expiration") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("YDotNetDocument", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/App/Migrations/20260512000001_AddJsonFunctions.cs b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/App/Migrations/20260512000001_AddJsonFunctions.cs new file mode 100644 index 000000000..b33425c26 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/App/Migrations/20260512000001_AddJsonFunctions.cs @@ -0,0 +1,64 @@ +using System.IO; +using Microsoft.EntityFrameworkCore.Migrations; +using Squidex.Providers.SqlServer; + +#nullable disable + +namespace Squidex.Providers.SqlServer.App.Migrations +{ + /// + public partial class AddJsonFunctions : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + var assembly = typeof(SqlServerDialect).Assembly; + + using var sqlStream = assembly.GetManifestResourceStream("Squidex.Providers.SqlServer.json_function.sql")!; + using var reader = new StreamReader(sqlStream); + + var sqlText = reader.ReadToEnd(); + var statements = sqlText.Split(";;", System.StringSplitOptions.RemoveEmptyEntries | System.StringSplitOptions.TrimEntries); + + foreach (var statement in statements) + { + migrationBuilder.Sql(statement); + } + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + var functions = new[] + { + "dbo.json_empty", + "dbo.json_exists", + "dbo.json_null_equals", + "dbo.json_null_notequals", + "dbo.json_text_contains", + "dbo.json_text_endswith", + "dbo.json_text_equals", + "dbo.json_text_greaterthan", + "dbo.json_text_greaterthanorequal", + "dbo.json_text_lessthan", + "dbo.json_text_lessthanorequal", + "dbo.json_text_matchs", + "dbo.json_text_notequals", + "dbo.json_text_startswith", + "dbo.json_boolean_equals", + "dbo.json_boolean_notequals", + "dbo.json_number_equals", + "dbo.json_number_greaterthan", + "dbo.json_number_greaterthanorequal", + "dbo.json_number_lessthan", + "dbo.json_number_lessthanorequal", + "dbo.json_number_notequals", + }; + + foreach (var function in functions) + { + migrationBuilder.Sql($"DROP FUNCTION IF EXISTS {function}"); + } + } + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/json_function.sql b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/json_function.sql index 4f198081c..5d06efc94 100644 --- a/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/json_function.sql +++ b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/json_function.sql @@ -1,8 +1,7 @@ -- ============================================================================= -- TYPE-AGNOSTIC -- ============================================================================= -DROP FUNCTION IF EXISTS dbo.json_empty;; -CREATE FUNCTION dbo.json_empty(@col NVARCHAR(MAX), @path NVARCHAR(500)) +CREATE OR ALTER FUNCTION dbo.json_empty(@col NVARCHAR(MAX), @path NVARCHAR(500)) RETURNS BIT AS BEGIN @@ -17,8 +16,7 @@ BEGIN RETURN 0; END;; -DROP FUNCTION IF EXISTS dbo.json_exists;; -CREATE FUNCTION dbo.json_exists(@col NVARCHAR(MAX), @path NVARCHAR(500)) +CREATE OR ALTER FUNCTION dbo.json_exists(@col NVARCHAR(MAX), @path NVARCHAR(500)) RETURNS BIT AS BEGIN @@ -29,8 +27,7 @@ END;; -- ============================================================================= -- NULL -- ============================================================================= -DROP FUNCTION IF EXISTS dbo.json_null_equals;; -CREATE FUNCTION dbo.json_null_equals(@col NVARCHAR(MAX), @path NVARCHAR(500)) +CREATE OR ALTER FUNCTION dbo.json_null_equals(@col NVARCHAR(MAX), @path NVARCHAR(500)) RETURNS BIT AS BEGIN @@ -42,8 +39,7 @@ BEGIN RETURN CASE WHEN JSON_VALUE(@col, @path) IS NULL THEN 1 ELSE 0 END; END;; -DROP FUNCTION IF EXISTS dbo.json_null_notequals;; -CREATE FUNCTION dbo.json_null_notequals(@col NVARCHAR(MAX), @path NVARCHAR(500)) +CREATE OR ALTER FUNCTION dbo.json_null_notequals(@col NVARCHAR(MAX), @path NVARCHAR(500)) RETURNS BIT AS BEGIN @@ -54,8 +50,7 @@ END;; -- ============================================================================= -- TEXT -- ============================================================================= -DROP FUNCTION IF EXISTS dbo.json_text_equals;; -CREATE FUNCTION dbo.json_text_equals(@col NVARCHAR(MAX), @path NVARCHAR(500), @target NVARCHAR(2000)) +CREATE OR ALTER FUNCTION dbo.json_text_equals(@col NVARCHAR(MAX), @path NVARCHAR(500), @target NVARCHAR(2000)) RETURNS BIT AS BEGIN @@ -67,8 +62,7 @@ BEGIN RETURN CASE WHEN JSON_VALUE(@col, @path) = @target THEN 1 ELSE 0 END; END;; -DROP FUNCTION IF EXISTS dbo.json_text_notequals;; -CREATE FUNCTION dbo.json_text_notequals(@col NVARCHAR(MAX), @path NVARCHAR(500), @target NVARCHAR(2000)) +CREATE OR ALTER FUNCTION dbo.json_text_notequals(@col NVARCHAR(MAX), @path NVARCHAR(500), @target NVARCHAR(2000)) RETURNS BIT AS BEGIN @@ -80,8 +74,7 @@ BEGIN RETURN CASE WHEN JSON_VALUE(@col, @path) != @target THEN 1 ELSE 0 END; END;; -DROP FUNCTION IF EXISTS dbo.json_text_lessthan;; -CREATE FUNCTION dbo.json_text_lessthan(@col NVARCHAR(MAX), @path NVARCHAR(500), @target NVARCHAR(2000)) +CREATE OR ALTER FUNCTION dbo.json_text_lessthan(@col NVARCHAR(MAX), @path NVARCHAR(500), @target NVARCHAR(2000)) RETURNS BIT AS BEGIN @@ -93,8 +86,7 @@ BEGIN RETURN CASE WHEN JSON_VALUE(@col, @path) < @target THEN 1 ELSE 0 END; END;; -DROP FUNCTION IF EXISTS dbo.json_text_lessthanorequal;; -CREATE FUNCTION dbo.json_text_lessthanorequal(@col NVARCHAR(MAX), @path NVARCHAR(500), @target NVARCHAR(2000)) +CREATE OR ALTER FUNCTION dbo.json_text_lessthanorequal(@col NVARCHAR(MAX), @path NVARCHAR(500), @target NVARCHAR(2000)) RETURNS BIT AS BEGIN @@ -106,8 +98,7 @@ BEGIN RETURN CASE WHEN JSON_VALUE(@col, @path) <= @target THEN 1 ELSE 0 END; END;; -DROP FUNCTION IF EXISTS dbo.json_text_greaterthan;; -CREATE FUNCTION dbo.json_text_greaterthan(@col NVARCHAR(MAX), @path NVARCHAR(500), @target NVARCHAR(2000)) +CREATE OR ALTER FUNCTION dbo.json_text_greaterthan(@col NVARCHAR(MAX), @path NVARCHAR(500), @target NVARCHAR(2000)) RETURNS BIT AS BEGIN @@ -119,8 +110,7 @@ BEGIN RETURN CASE WHEN JSON_VALUE(@col, @path) > @target THEN 1 ELSE 0 END; END;; -DROP FUNCTION IF EXISTS dbo.json_text_greaterthanorequal;; -CREATE FUNCTION dbo.json_text_greaterthanorequal(@col NVARCHAR(MAX), @path NVARCHAR(500), @target NVARCHAR(2000)) +CREATE OR ALTER FUNCTION dbo.json_text_greaterthanorequal(@col NVARCHAR(MAX), @path NVARCHAR(500), @target NVARCHAR(2000)) RETURNS BIT AS BEGIN @@ -132,8 +122,7 @@ BEGIN RETURN CASE WHEN JSON_VALUE(@col, @path) >= @target THEN 1 ELSE 0 END; END;; -DROP FUNCTION IF EXISTS dbo.json_text_contains;; -CREATE FUNCTION dbo.json_text_contains(@col NVARCHAR(MAX), @path NVARCHAR(500), @target NVARCHAR(2000)) +CREATE OR ALTER FUNCTION dbo.json_text_contains(@col NVARCHAR(MAX), @path NVARCHAR(500), @target NVARCHAR(2000)) RETURNS BIT AS BEGIN @@ -145,8 +134,7 @@ BEGIN RETURN CASE WHEN JSON_VALUE(@col, @path) LIKE '%' + @target + '%' THEN 1 ELSE 0 END; END;; -DROP FUNCTION IF EXISTS dbo.json_text_startswith;; -CREATE FUNCTION dbo.json_text_startswith(@col NVARCHAR(MAX), @path NVARCHAR(500), @target NVARCHAR(2000)) +CREATE OR ALTER FUNCTION dbo.json_text_startswith(@col NVARCHAR(MAX), @path NVARCHAR(500), @target NVARCHAR(2000)) RETURNS BIT AS BEGIN @@ -158,8 +146,7 @@ BEGIN RETURN CASE WHEN JSON_VALUE(@col, @path) LIKE @target + '%' THEN 1 ELSE 0 END; END;; -DROP FUNCTION IF EXISTS dbo.json_text_endswith;; -CREATE FUNCTION dbo.json_text_endswith(@col NVARCHAR(MAX), @path NVARCHAR(500), @target NVARCHAR(2000)) +CREATE OR ALTER FUNCTION dbo.json_text_endswith(@col NVARCHAR(MAX), @path NVARCHAR(500), @target NVARCHAR(2000)) RETURNS BIT AS BEGIN @@ -171,8 +158,7 @@ BEGIN RETURN CASE WHEN JSON_VALUE(@col, @path) LIKE '%' + @target THEN 1 ELSE 0 END; END;; -DROP FUNCTION IF EXISTS dbo.json_text_matchs;; -CREATE FUNCTION dbo.json_text_matchs(@col NVARCHAR(MAX), @path NVARCHAR(500), @target NVARCHAR(2000)) +CREATE OR ALTER FUNCTION dbo.json_text_matchs(@col NVARCHAR(MAX), @path NVARCHAR(500), @target NVARCHAR(2000)) RETURNS BIT AS BEGIN @@ -188,8 +174,7 @@ END;; -- ============================================================================= -- NUMBER -- ============================================================================= -DROP FUNCTION IF EXISTS dbo.json_number_equals;; -CREATE FUNCTION dbo.json_number_equals(@col NVARCHAR(MAX), @path NVARCHAR(500), @target DECIMAL(38, 10)) +CREATE OR ALTER FUNCTION dbo.json_number_equals(@col NVARCHAR(MAX), @path NVARCHAR(500), @target DECIMAL(38, 10)) RETURNS BIT AS BEGIN @@ -203,8 +188,7 @@ BEGIN RETURN CASE WHEN TRY_CAST(JSON_VALUE(@col, @path) AS DECIMAL(38, 10)) = @target THEN 1 ELSE 0 END; END;; -DROP FUNCTION IF EXISTS dbo.json_number_notequals;; -CREATE FUNCTION dbo.json_number_notequals(@col NVARCHAR(MAX), @path NVARCHAR(500), @target DECIMAL(38, 10)) +CREATE OR ALTER FUNCTION dbo.json_number_notequals(@col NVARCHAR(MAX), @path NVARCHAR(500), @target DECIMAL(38, 10)) RETURNS BIT AS BEGIN @@ -218,8 +202,7 @@ BEGIN RETURN CASE WHEN TRY_CAST(JSON_VALUE(@col, @path) AS DECIMAL(38, 10)) != @target THEN 1 ELSE 0 END; END;; -DROP FUNCTION IF EXISTS dbo.json_number_lessthan;; -CREATE FUNCTION dbo.json_number_lessthan(@col NVARCHAR(MAX), @path NVARCHAR(500), @target DECIMAL(38, 10)) +CREATE OR ALTER FUNCTION dbo.json_number_lessthan(@col NVARCHAR(MAX), @path NVARCHAR(500), @target DECIMAL(38, 10)) RETURNS BIT AS BEGIN @@ -233,8 +216,7 @@ BEGIN RETURN CASE WHEN TRY_CAST(JSON_VALUE(@col, @path) AS DECIMAL(38, 10)) < @target THEN 1 ELSE 0 END; END;; -DROP FUNCTION IF EXISTS dbo.json_number_lessthanorequal;; -CREATE FUNCTION dbo.json_number_lessthanorequal(@col NVARCHAR(MAX), @path NVARCHAR(500), @target DECIMAL(38, 10)) +CREATE OR ALTER FUNCTION dbo.json_number_lessthanorequal(@col NVARCHAR(MAX), @path NVARCHAR(500), @target DECIMAL(38, 10)) RETURNS BIT AS BEGIN @@ -248,8 +230,7 @@ BEGIN RETURN CASE WHEN TRY_CAST(JSON_VALUE(@col, @path) AS DECIMAL(38, 10)) <= @target THEN 1 ELSE 0 END; END;; -DROP FUNCTION IF EXISTS dbo.json_number_greaterthan;; -CREATE FUNCTION dbo.json_number_greaterthan(@col NVARCHAR(MAX), @path NVARCHAR(500), @target DECIMAL(38, 10)) +CREATE OR ALTER FUNCTION dbo.json_number_greaterthan(@col NVARCHAR(MAX), @path NVARCHAR(500), @target DECIMAL(38, 10)) RETURNS BIT AS BEGIN @@ -263,8 +244,7 @@ BEGIN RETURN CASE WHEN TRY_CAST(JSON_VALUE(@col, @path) AS DECIMAL(38, 10)) > @target THEN 1 ELSE 0 END; END;; -DROP FUNCTION IF EXISTS dbo.json_number_greaterthanorequal;; -CREATE FUNCTION dbo.json_number_greaterthanorequal(@col NVARCHAR(MAX), @path NVARCHAR(500), @target DECIMAL(38, 10)) +CREATE OR ALTER FUNCTION dbo.json_number_greaterthanorequal(@col NVARCHAR(MAX), @path NVARCHAR(500), @target DECIMAL(38, 10)) RETURNS BIT AS BEGIN @@ -282,8 +262,7 @@ END;; -- ============================================================================= -- BOOLEAN -- ============================================================================= -DROP FUNCTION IF EXISTS dbo.json_boolean_equals;; -CREATE FUNCTION dbo.json_boolean_equals(@col NVARCHAR(MAX), @path NVARCHAR(500), @target BIT) +CREATE OR ALTER FUNCTION dbo.json_boolean_equals(@col NVARCHAR(MAX), @path NVARCHAR(500), @target BIT) RETURNS BIT AS BEGIN @@ -297,8 +276,7 @@ BEGIN RETURN CASE WHEN IIF(JSON_VALUE(@col, @path) = 'true', 1, IIF(JSON_VALUE(@col, @path) = 'false', 0, NULL)) = @target THEN 1 ELSE 0 END; END;; -DROP FUNCTION IF EXISTS dbo.json_boolean_notequals;; -CREATE FUNCTION dbo.json_boolean_notequals(@col NVARCHAR(MAX), @path NVARCHAR(500), @target BIT) +CREATE OR ALTER FUNCTION dbo.json_boolean_notequals(@col NVARCHAR(MAX), @path NVARCHAR(500), @target BIT) RETURNS BIT AS BEGIN diff --git a/backend/src/Squidex.Data.EntityFramework/ServiceExtensions.cs b/backend/src/Squidex.Data.EntityFramework/ServiceExtensions.cs index 1679cdefb..8c0ad4c61 100644 --- a/backend/src/Squidex.Data.EntityFramework/ServiceExtensions.cs +++ b/backend/src/Squidex.Data.EntityFramework/ServiceExtensions.cs @@ -271,7 +271,6 @@ public static class ServiceExtensions .AddEntityFrameworkStore(); services.AddEntityFrameworkAssetKeyValueStore(); - services.AddSingletonAs>(); } public static void AddSquidexEntityFrameworkEventStore(this IServiceCollection services, IConfiguration config) diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/MySqlMigrationTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/MySqlMigrationTests.cs index 722021ea8..d869f433e 100644 --- a/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/MySqlMigrationTests.cs +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/MySqlMigrationTests.cs @@ -8,7 +8,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Squidex.Domain.Apps.Core.TestHelpers; +using Squidex.Infrastructure; using Squidex.Infrastructure.Migrations; +using Squidex.Infrastructure.Queries; using Squidex.Providers.MySql; using Squidex.Providers.MySql.App; using Testcontainers.MySql; @@ -18,7 +20,9 @@ namespace Squidex.EntityFramework.Migrations; [Trait("Category", "TestContainer")] public class MySqlMigrationTests : IAsyncLifetime { - private readonly MySqlContainer mysql = new MySqlBuilder("mysql:8.0").Build(); + private readonly MySqlContainer mysql = new MySqlBuilder("mysql:8.0") + .WithCommand("--log-bin-trust-function-creators=1") + .Build(); public async ValueTask InitializeAsync() { @@ -60,4 +64,85 @@ public class MySqlMigrationTests : IAsyncLifetime var migrations = await dbContext.Database.GetAppliedMigrationsAsync(); Assert.NotEmpty(migrations); } + + [Fact] + public async Task Should_migrate_idempotent_and_functions_callable() + { + var connectionString = mysql.GetConnectionString(); + + var services = + new ServiceCollection() + .AddDbContextFactory(b => + { + b.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString), options => + { + options.UseNetTopologySuite(); + options.UseMicrosoftJson(MySqlCommonJsonChangeTrackingOptions.FullHierarchyOptimizedSemantically); + }); + }) + .AddSingleton() + .AddSingleton(TestUtils.DefaultSerializer) + .AddSingleton>() + .BuildServiceProvider(); + + var databaseMigrator = services.GetRequiredService>(); + var databaseFactory = services.GetRequiredService>(); + + // Run migrations twice to verify idempotency. + await databaseMigrator.InitializeAsync(default); + await databaseMigrator.InitializeAsync(default); + + // Verify the json_exists function was created and is callable. + // Note: {{ and }} are escaped braces for ExecuteSqlRawAsync's string.Format-style parser. + await using var dbContext = await databaseFactory.CreateDbContextAsync(); + var result = await dbContext.Database.ExecuteSqlRawAsync( + "SELECT json_exists('{{\"a\":1}}', '$.a')"); + Assert.Equal(-1, result); + } + + [Fact] + public async Task Should_migrate_when_functions_already_exist() + { + var connectionString = mysql.GetConnectionString(); + + var services = + new ServiceCollection() + .AddDbContextFactory(b => + { + b.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString), options => + { + options.UseNetTopologySuite(); + options.UseMicrosoftJson(MySqlCommonJsonChangeTrackingOptions.FullHierarchyOptimizedSemantically); + }); + }) + .AddSingleton() + .AddSingleton(TestUtils.DefaultSerializer) + .AddSingleton>() + .BuildServiceProvider(); + + var databaseMigrator = services.GetRequiredService>(); + var databaseFactory = services.GetRequiredService>(); + + // Simulate a pre-migration database: apply all migrations up to (but not including) + // AddJsonFunctions, then create the functions via the old SqlDialectInitializer path. + await using (var dbContext = await databaseFactory.CreateDbContextAsync()) + { + await dbContext.Database.MigrateAsync("20260323154443_MigrateToNet10"); + + if (dbContext is IDbContextWithDialect withDialect) + { + await withDialect.Dialect.InitializeAsync(dbContext, default); + } + } + + // Run the full migration — should apply AddJsonFunctions on top of already-existing functions. + await databaseMigrator.InitializeAsync(default); + + // Verify the functions are still callable after migration. + // Note: {{ and }} are escaped braces for ExecuteSqlRawAsync's string.Format-style parser. + await using var verifyContext = await databaseFactory.CreateDbContextAsync(); + var result = await verifyContext.Database.ExecuteSqlRawAsync( + "SELECT json_exists('{{\"a\":1}}', '$.a')"); + Assert.Equal(-1, result); + } } diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/PostgresMigrationTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/PostgresMigrationTests.cs index d7dd7a589..6e7a7dd70 100644 --- a/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/PostgresMigrationTests.cs +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/PostgresMigrationTests.cs @@ -8,7 +8,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Squidex.Domain.Apps.Core.TestHelpers; +using Squidex.Infrastructure; using Squidex.Infrastructure.Migrations; +using Squidex.Infrastructure.Queries; using Squidex.Providers.Postgres; using Squidex.Providers.Postgres.App; using Testcontainers.PostgreSql; @@ -19,7 +21,7 @@ namespace Squidex.EntityFramework.Migrations; public class PostgresMigrationTests : IAsyncLifetime { private readonly PostgreSqlContainer postgreSql = - new PostgreSqlBuilder("postgis/postgis") + new PostgreSqlBuilder("imresamu/postgis:16-3.4") .Build(); public async ValueTask InitializeAsync() @@ -59,4 +61,79 @@ public class PostgresMigrationTests : IAsyncLifetime var migrations = await dbContext.Database.GetAppliedMigrationsAsync(); Assert.NotEmpty(migrations); } + + [Fact] + public async Task Should_migrate_idempotent_and_functions_callable() + { + var services = + new ServiceCollection() + .AddDbContextFactory(b => + { + b.UseNpgsql(postgreSql.GetConnectionString(), options => + { + options.UseNetTopologySuite(); + }); + }) + .AddSingleton() + .AddSingleton(TestUtils.DefaultSerializer) + .AddSingleton>() + .BuildServiceProvider(); + + var databaseMigrator = services.GetRequiredService>(); + var databaseFactory = services.GetRequiredService>(); + + // Run migrations twice to verify idempotency. + await databaseMigrator.InitializeAsync(default); + await databaseMigrator.InitializeAsync(default); + + // Verify the jsonb_exists function was created and is callable. + // Note: {{ and }} are escaped braces for ExecuteSqlRawAsync's string.Format-style parser. + await using var dbContext = await databaseFactory.CreateDbContextAsync(); + var result = await dbContext.Database.ExecuteSqlRawAsync( + "SELECT jsonb_exists('{{\"a\":1}}'::jsonb)"); + Assert.Equal(-1, result); + } + + [Fact] + public async Task Should_migrate_when_functions_already_exist() + { + var services = + new ServiceCollection() + .AddDbContextFactory(b => + { + b.UseNpgsql(postgreSql.GetConnectionString(), options => + { + options.UseNetTopologySuite(); + }); + }) + .AddSingleton() + .AddSingleton(TestUtils.DefaultSerializer) + .AddSingleton>() + .BuildServiceProvider(); + + var databaseMigrator = services.GetRequiredService>(); + var databaseFactory = services.GetRequiredService>(); + + // Simulate a pre-migration database: apply all migrations up to (but not including) + // AddJsonFunctions, then create the functions via the old SqlDialectInitializer path. + await using (var dbContext = await databaseFactory.CreateDbContextAsync()) + { + await dbContext.Database.MigrateAsync("20260323155026_MigrateToNet10"); + + if (dbContext is IDbContextWithDialect withDialect) + { + await withDialect.Dialect.InitializeAsync(dbContext, default); + } + } + + // Run the full migration — should apply AddJsonFunctions on top of already-existing functions. + await databaseMigrator.InitializeAsync(default); + + // Verify the functions are still callable after migration. + // Note: {{ and }} are escaped braces for ExecuteSqlRawAsync's string.Format-style parser. + await using var verifyContext = await databaseFactory.CreateDbContextAsync(); + var result = await verifyContext.Database.ExecuteSqlRawAsync( + "SELECT jsonb_exists('{{\"a\":1}}'::jsonb)"); + Assert.Equal(-1, result); + } } diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/SqlServerMigrationTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/SqlServerMigrationTests.cs index 38808e059..ec4945191 100644 --- a/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/SqlServerMigrationTests.cs +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/SqlServerMigrationTests.cs @@ -8,7 +8,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Squidex.Domain.Apps.Core.TestHelpers; +using Squidex.Infrastructure; using Squidex.Infrastructure.Migrations; +using Squidex.Infrastructure.Queries; using Squidex.Providers.SqlServer; using Squidex.Providers.SqlServer.App; using Testcontainers.MsSql; @@ -59,4 +61,79 @@ public class SqlServerMigrationTests : IAsyncLifetime var migrations = await dbContext.Database.GetAppliedMigrationsAsync(); Assert.NotEmpty(migrations); } + + [Fact] + public async Task Should_migrate_idempotent_and_functions_callable() + { + var services = + new ServiceCollection() + .AddDbContextFactory(b => + { + b.UseSqlServer(sqlServer.GetConnectionString(), options => + { + options.UseNetTopologySuite(); + }); + }) + .AddSingleton() + .AddSingleton(TestUtils.DefaultSerializer) + .AddSingleton>() + .BuildServiceProvider(); + + var databaseMigrator = services.GetRequiredService>(); + var databaseFactory = services.GetRequiredService>(); + + // Run migrations twice to verify idempotency. + await databaseMigrator.InitializeAsync(default); + await databaseMigrator.InitializeAsync(default); + + // Verify the dbo.json_exists function was created and is callable. + // Note: {{ and }} are escaped braces for ExecuteSqlRawAsync's string.Format-style parser. + await using var dbContext = await databaseFactory.CreateDbContextAsync(); + var result = await dbContext.Database.ExecuteSqlRawAsync( + "SELECT dbo.json_exists('{{\"a\":1}}', '$.a')"); + Assert.Equal(-1, result); + } + + [Fact] + public async Task Should_migrate_when_functions_already_exist() + { + var services = + new ServiceCollection() + .AddDbContextFactory(b => + { + b.UseSqlServer(sqlServer.GetConnectionString(), options => + { + options.UseNetTopologySuite(); + }); + }) + .AddSingleton() + .AddSingleton(TestUtils.DefaultSerializer) + .AddSingleton>() + .BuildServiceProvider(); + + var databaseMigrator = services.GetRequiredService>(); + var databaseFactory = services.GetRequiredService>(); + + // Simulate a pre-migration database: apply all migrations up to (but not including) + // AddJsonFunctions, then create the functions via the old SqlDialectInitializer path. + await using (var dbContext = await databaseFactory.CreateDbContextAsync()) + { + await dbContext.Database.MigrateAsync("20260323155035_MigrateToNet10"); + + if (dbContext is IDbContextWithDialect withDialect) + { + await withDialect.Dialect.InitializeAsync(dbContext, default); + } + } + + // Run the full migration — should apply AddJsonFunctions on top of already-existing functions. + await databaseMigrator.InitializeAsync(default); + + // Verify the functions are still callable after migration. + // Note: {{ and }} are escaped braces for ExecuteSqlRawAsync's string.Format-style parser. + await using var verifyContext = await databaseFactory.CreateDbContextAsync(); + var result = await verifyContext.Database.ExecuteSqlRawAsync( + "SELECT dbo.json_exists('{{\"a\":1}}', '$.a')"); + Assert.Equal(-1, result); + } } diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/MySqlFixture.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/MySqlFixture.cs index b497a6003..12a49042a 100644 --- a/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/MySqlFixture.cs +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/MySqlFixture.cs @@ -13,7 +13,6 @@ using Squidex.Domain.Apps.Core.TestHelpers; using Squidex.Hosting; using Squidex.Infrastructure; using Squidex.Infrastructure.Migrations; -using Squidex.Infrastructure.Queries; using Squidex.Providers.MySql; using Squidex.Providers.MySql.Content; using Testcontainers.MySql; @@ -60,21 +59,27 @@ public class MySqlFixture(string? reuseId = null) : IAsyncLifetime, ISqlContentF builder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString), options => { options.UseMicrosoftJson(MySqlCommonJsonChangeTrackingOptions.FullHierarchyOptimizedSemantically); + options.MigrationsHistoryTable($"{name}MigrationHistory"); }); builder.ConfigureWarnings(w => w.Ignore(RelationalEventId.PendingModelChangesWarning)); }) .AddSingleton() - .AddSingletonAs>().Done() .AddSingleton(TestUtils.DefaultSerializer) - .AddSingleton>() .BuildServiceProvider(); foreach (var service in services.GetRequiredService>()) { await service.InitializeAsync(default); } + + await using var dbContext = await services.GetRequiredService>().CreateDbContextAsync(); + await dbContext.Database.EnsureCreatedAsync(); + if (dbContext is IDbContextWithDialect withDialect) + { + await withDialect.Dialect.InitializeAsync(dbContext, default); + } } public async ValueTask DisposeAsync() diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/PostgresFixture.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/PostgresFixture.cs index 4f613fd46..de1791a25 100644 --- a/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/PostgresFixture.cs +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/PostgresFixture.cs @@ -13,7 +13,6 @@ using Squidex.Domain.Apps.Core.TestHelpers; using Squidex.Hosting; using Squidex.Infrastructure; using Squidex.Infrastructure.Migrations; -using Squidex.Infrastructure.Queries; using Squidex.Providers.Postgres; using Squidex.Providers.Postgres.Content; using Testcontainers.PostgreSql; @@ -23,7 +22,7 @@ namespace Squidex.EntityFramework.TestHelpers; public class PostgresFixture(string? reuseId) : IAsyncLifetime, ISqlContentFixture { private readonly PostgreSqlContainer postgreSql = - new PostgreSqlBuilder("postgis/postgis") + new PostgreSqlBuilder("imresamu/postgis:16-3.4") .WithReuse(true) .WithLabel("reuse-id", reuseId) .Build(); @@ -55,21 +54,29 @@ public class PostgresFixture(string? reuseId) : IAsyncLifetime, ISqlContentFixtu .AddNamedDbContext((builder, name) => { builder.UseBulkInsertPostgreSql(); - builder.UseNpgsql(connectionString); + builder.UseNpgsql(connectionString, options => + { + options.MigrationsHistoryTable($"{name}MigrationHistory"); + }); builder.ConfigureWarnings(w => w.Ignore(RelationalEventId.PendingModelChangesWarning)); }) .AddSingleton() - .AddSingletonAs>().Done() .AddSingleton(TestUtils.DefaultSerializer) - .AddSingleton>() .BuildServiceProvider(); foreach (var service in services.GetRequiredService>()) { await service.InitializeAsync(default); } + + await using var dbContext = await services.GetRequiredService>().CreateDbContextAsync(); + await dbContext.Database.EnsureCreatedAsync(); + if (dbContext is IDbContextWithDialect withDialect) + { + await withDialect.Dialect.InitializeAsync(dbContext, default); + } } public async ValueTask DisposeAsync() diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/SqlServerFixture.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/SqlServerFixture.cs index 26a5608f9..51db44a6d 100644 --- a/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/SqlServerFixture.cs +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/SqlServerFixture.cs @@ -14,7 +14,6 @@ using Squidex.Domain.Apps.Core.TestHelpers; using Squidex.Hosting; using Squidex.Infrastructure; using Squidex.Infrastructure.Migrations; -using Squidex.Infrastructure.Queries; using Squidex.Providers.SqlServer; using Squidex.Providers.SqlServer.Content; using Testcontainers.MsSql; @@ -57,21 +56,29 @@ public class SqlServerFixture(string? reuseId = null) : IAsyncLifetime, ISqlCont .AddNamedDbContext((builder, name) => { builder.UseBulkInsertSqlServer(); - builder.UseSqlServer(connectionString); + builder.UseSqlServer(connectionString, options => + { + options.MigrationsHistoryTable($"{name}MigrationHistory"); + }); builder.ConfigureWarnings(w => w.Ignore(RelationalEventId.PendingModelChangesWarning)); }) .AddSingleton() - .AddSingletonAs>().Done() .AddSingleton(TestUtils.DefaultSerializer) - .AddSingleton>() .BuildServiceProvider(); foreach (var service in services.GetRequiredService>()) { await service.InitializeAsync(default); } + + await using var dbContext = await services.GetRequiredService>().CreateDbContextAsync(); + await dbContext.Database.EnsureCreatedAsync(); + if (dbContext is IDbContextWithDialect withDialect) + { + await withDialect.Dialect.InitializeAsync(dbContext, default); + } } public async ValueTask DisposeAsync()