diff --git a/src/util/entities/Application.ts b/src/util/entities/Application.ts index e00b4a988..18a694f91 100644 --- a/src/util/entities/Application.ts +++ b/src/util/entities/Application.ts @@ -38,7 +38,7 @@ export class Application extends BaseClass { @Column({ nullable: true }) summary: string = ""; - @Column({ type: "simple-json", nullable: true }) + @Column({ type: "jsonb", nullable: true }) type?: object; // TODO: this type is bad @Column() @@ -98,7 +98,7 @@ export class Application extends BaseClass { @Column({ nullable: true }) cover_image?: string; // the application's default rich presence invite cover image hash - @Column({ type: "simple-json", nullable: true }) + @Column({ type: "jsonb", nullable: true }) install_params?: { scopes: string[]; permissions: string }; @Column({ nullable: true }) diff --git a/src/util/entities/ApplicationCommand.ts b/src/util/entities/ApplicationCommand.ts index 21153d894..892bb8334 100644 --- a/src/util/entities/ApplicationCommand.ts +++ b/src/util/entities/ApplicationCommand.ts @@ -44,16 +44,16 @@ export class ApplicationCommand extends BaseClass { @Column() name: string; - @Column({ nullable: true, type: "simple-json" }) + @Column({ nullable: true, type: "jsonb" }) name_localizations?: Record; @Column() description: string; - @Column({ nullable: true, type: "simple-json" }) + @Column({ nullable: true, type: "jsonb" }) description_localizations?: Record; - @Column({ type: "simple-json", default: "[]" }) + @Column({ type: "jsonb", default: [] }) options: ApplicationCommandOption[]; @Column({ nullable: true, type: String }) @@ -65,19 +65,19 @@ export class ApplicationCommand extends BaseClass { @Column({ default: true }) dm_permission?: boolean; - @Column({ nullable: true, type: "simple-json" }) + @Column({ nullable: true, type: "jsonb" }) permissions?: ApplicationCommandIndexPermissions; @Column({ default: false }) nsfw?: boolean; - @Column({ nullable: true, type: "simple-json" }) + @Column({ nullable: true, type: "jsonb" }) integration_types?: ApplicationIntegrationType[]; @Column({ default: 0 }) global_popularity_rank?: number; - @Column({ nullable: true, type: "simple-json" }) + @Column({ nullable: true, type: "jsonb" }) contexts?: InteractionContextType[]; @Column({ default: 0 }) diff --git a/src/util/entities/AuditLog.ts b/src/util/entities/AuditLog.ts index c59d50a6b..f47f6dc7a 100644 --- a/src/util/entities/AuditLog.ts +++ b/src/util/entities/AuditLog.ts @@ -40,7 +40,7 @@ export class AuditLog extends BaseClass { @Column({ type: "int" }) action_type: AuditLogEvents; - @Column({ type: "simple-json", nullable: true }) + @Column({ type: "jsonb", nullable: true }) options?: { delete_member_days?: string; members_removed?: string; @@ -53,7 +53,7 @@ export class AuditLog extends BaseClass { }; @Column() - @Column({ type: "simple-json" }) + @Column({ type: "jsonb" }) changes: AuditLogChange[]; @Column({ nullable: true }) diff --git a/src/util/entities/AutomodRule.ts b/src/util/entities/AutomodRule.ts index 4a1976ac2..f57018ff9 100644 --- a/src/util/entities/AutomodRule.ts +++ b/src/util/entities/AutomodRule.ts @@ -54,14 +54,14 @@ export class AutomodRule extends BaseClass { trigger_type: AutomodRuleTriggerType; @Column({ - type: "simple-json", + type: "jsonb", nullable: true, }) trigger_metadata?: // this is null for "Block suspected spam content" AutomodRuleTriggerMetadata; @Column({ - type: "simple-json", + type: "jsonb", }) actions: AutomodAction[]; } diff --git a/src/util/entities/Categories.ts b/src/util/entities/Categories.ts index 2c45217cb..8c7ca78c9 100644 --- a/src/util/entities/Categories.ts +++ b/src/util/entities/Categories.ts @@ -45,7 +45,7 @@ export class Categories extends BaseClassWithoutId { @Column({ nullable: true }) name: string; - @Column({ type: "simple-json" }) + @Column({ type: "jsonb" }) localizations: string; // Whether to show the category prominently (e.g. in a sidebar) instead of only secondary (e.g. in search results) diff --git a/src/util/entities/Channel.ts b/src/util/entities/Channel.ts index 451500cb3..370432d06 100644 --- a/src/util/entities/Channel.ts +++ b/src/util/entities/Channel.ts @@ -101,7 +101,7 @@ export class Channel extends BaseClass { @Column({ nullable: true }) default_auto_archive_duration?: number; - @Column({ type: "simple-json", nullable: true }) + @Column({ type: "jsonb", nullable: true }) permission_overwrites?: ChannelPermissionOverwrite[]; @Column({ nullable: true }) @@ -161,7 +161,7 @@ export class Channel extends BaseClass { @Column({ nullable: true }) default_thread_rate_limit_per_user?: number = 0; - @Column({ type: "simple-json", nullable: true }) + @Column({ type: "jsonb", nullable: true }) thread_metadata?: ThreadMetadata; @Column({ nullable: true }) diff --git a/src/util/entities/ConnectedAccount.ts b/src/util/entities/ConnectedAccount.ts index 6a5fed759..179f152e5 100644 --- a/src/util/entities/ConnectedAccount.ts +++ b/src/util/entities/ConnectedAccount.ts @@ -62,7 +62,7 @@ export class ConnectedAccount extends BaseClass { @Column({ type: "simple-array" }) integrations?: string[] = []; - @Column({ type: "simple-json", name: "metadata", nullable: true }) + @Column({ type: "jsonb", name: "metadata", nullable: true }) // eslint-disable-next-line @typescript-eslint/no-explicit-any metadata_?: any; @@ -72,7 +72,7 @@ export class ConnectedAccount extends BaseClass { @Column() two_way_link?: boolean = false; - @Column({ select: false, nullable: true, type: "simple-json" }) + @Column({ select: false, nullable: true, type: "jsonb" }) token_data?: ConnectedAccountTokenData | null; async revoke() { diff --git a/src/util/entities/EmbedCache.ts b/src/util/entities/EmbedCache.ts index 6a7738052..b34be18c7 100644 --- a/src/util/entities/EmbedCache.ts +++ b/src/util/entities/EmbedCache.ts @@ -27,10 +27,10 @@ export class EmbedCache extends BaseClass { @Column() url: string; - @Column({ type: "simple-json", nullable: true }) + @Column({ type: "jsonb", nullable: true }) embed?: Embed; - @Column({ type: "simple-json", nullable: true }) + @Column({ type: "jsonb", nullable: true }) embeds?: Embed[]; @Column({ name: "created_at", type: "timestamp with time zone" }) diff --git a/src/util/entities/Guild.ts b/src/util/entities/Guild.ts index c7c8899f5..c38f72381 100644 --- a/src/util/entities/Guild.ts +++ b/src/util/entities/Guild.ts @@ -264,7 +264,7 @@ export class Guild extends BaseClass { /** * DEPRECATED: Look at the new Guild onboarding screens. */ - @Column({ type: "simple-json" }) + @Column({ type: "jsonb" }) welcome_screen: GuildWelcomeScreen; @Column({ nullable: true }) diff --git a/src/util/entities/Member.ts b/src/util/entities/Member.ts index 00ac3a352..802338ef7 100644 --- a/src/util/entities/Member.ts +++ b/src/util/entities/Member.ts @@ -109,7 +109,7 @@ export class Member extends BaseClassWithoutId { @Column() pending: boolean; - @Column({ type: "simple-json", select: false }) + @Column({ type: "jsonb", select: false }) settings: UserGuildSettings; @Column({ nullable: true }) @@ -143,16 +143,16 @@ export class Member extends BaseClassWithoutId { communication_disabled_until: Date | null; // TODO: add this when we have proper read receipts - // @Column({ type: "simple-json" }) + // @Column({ type: "jsonb" }) // read_state: ReadState; - @Column({ type: "simple-json", nullable: true }) + @Column({ type: "jsonb", nullable: true }) avatar_decoration_data?: AvatarDecorationData; - @Column({ type: "simple-json", nullable: true }) + @Column({ type: "jsonb", nullable: true }) display_name_styles?: DisplayNameStyle; - @Column({ type: "simple-json", nullable: true }) + @Column({ type: "jsonb", nullable: true }) collectibles?: Collectibles; @Column({ type: "int", default: 0 }) diff --git a/src/util/entities/Message.ts b/src/util/entities/Message.ts index 333f7bd2b..23e699b59 100644 --- a/src/util/entities/Message.ts +++ b/src/util/entities/Message.ts @@ -163,10 +163,10 @@ export class Message extends BaseClass { }) attachments?: Attachment[]; - @Column({ type: "simple-json" }) + @Column({ type: "jsonb" }) embeds: Embed[]; - @Column({ type: "simple-json" }) + @Column({ type: "jsonb" }) @JsonRemoveEmpty reactions: Reaction[]; @@ -184,7 +184,7 @@ export class Message extends BaseClass { @Column({ type: "int" }) type: MessageType; - @Column({ type: "simple-json", nullable: true }) + @Column({ type: "jsonb", nullable: true }) @JsonRemoveEmpty activity?: { type: number; @@ -194,7 +194,7 @@ export class Message extends BaseClass { @Column({ default: 0 }) flags: number; - @Column({ type: "simple-json", nullable: true }) + @Column({ type: "jsonb", nullable: true }) @JsonRemoveEmpty message_reference?: { message_id?: string; @@ -207,7 +207,7 @@ export class Message extends BaseClass { @ManyToOne(() => Message, { onDelete: "SET NULL" }) referenced_message?: Message | null; - @Column({ type: "simple-json", nullable: true }) + @Column({ type: "jsonb", nullable: true }) @JsonRemoveEmpty interaction?: { id: string; @@ -215,7 +215,7 @@ export class Message extends BaseClass { name: string; }; - @Column({ type: "simple-json", nullable: true }) + @Column({ type: "jsonb", nullable: true }) @JsonRemoveEmpty interaction_metadata?: { id: string; @@ -226,10 +226,10 @@ export class Message extends BaseClass { command_type: ApplicationCommandType; }; - @Column({ type: "simple-json", nullable: true }) + @Column({ type: "jsonb", nullable: true }) components?: BaseMessageComponents[]; - @Column({ type: "simple-json", nullable: true }) + @Column({ type: "jsonb", nullable: true }) @JsonRemoveEmpty poll?: Poll; @@ -239,7 +239,7 @@ export class Message extends BaseClass { @Column({ nullable: true }) avatar?: string; - @Column({ default: "[]", type: "simple-json" }) + @Column({ default: "[]", type: "jsonb" }) message_snapshots: MessageSnapshot[]; static async fillReplies(messages: Message[]) { diff --git a/src/util/entities/Role.ts b/src/util/entities/Role.ts index 0647d6da6..e6b2e4590 100644 --- a/src/util/entities/Role.ts +++ b/src/util/entities/Role.ts @@ -63,7 +63,7 @@ export class Role extends BaseClass { @Column({ nullable: true }) unicode_emoji?: string; - @Column({ type: "simple-json", nullable: true }) + @Column({ type: "jsonb", nullable: true }) tags?: { bot_id?: string; integration_id?: string; @@ -73,7 +73,7 @@ export class Role extends BaseClass { @Column({ default: 0 }) flags: number; - @Column({ nullable: false, type: "simple-json" }) + @Column({ nullable: false, type: "jsonb" }) colors: RoleColors; toJSON(): Role { diff --git a/src/util/entities/Session.ts b/src/util/entities/Session.ts index a5cdeb77f..eb28c389d 100644 --- a/src/util/entities/Session.ts +++ b/src/util/entities/Session.ts @@ -42,10 +42,10 @@ export class Session extends BaseClassWithoutId { }) user: User; - @Column({ type: "simple-json", default: "[]" }) + @Column({ type: "jsonb", default: "[]" }) activities: Activity[]; - @Column({ type: "simple-json" }) + @Column({ type: "jsonb" }) client_info: { platform?: string; os?: string; @@ -53,7 +53,7 @@ export class Session extends BaseClassWithoutId { location?: string; }; - @Column({ type: "simple-json" }) + @Column({ type: "jsonb" }) client_status: ClientStatus; @Column({ nullable: false, type: String }) @@ -74,7 +74,7 @@ export class Session extends BaseClassWithoutId { @Column({ nullable: true, type: String }) last_seen_location?: string; - @Column({ nullable: true, type: "simple-json" }) + @Column({ nullable: true, type: "jsonb" }) last_seen_location_info?: ExtendedLocationInfo; @Column({ nullable: true, type: String }) diff --git a/src/util/entities/Template.ts b/src/util/entities/Template.ts index a9e341b3e..79804eedd 100644 --- a/src/util/entities/Template.ts +++ b/src/util/entities/Template.ts @@ -59,6 +59,6 @@ export class Template extends BaseClass { @ManyToOne(() => Guild, { onDelete: "CASCADE" }) source_guild: Guild; - @Column({ type: "simple-json" }) + @Column({ type: "jsonb" }) serialized_source_guild: Guild; } diff --git a/src/util/entities/ThreadMember.ts b/src/util/entities/ThreadMember.ts index a24ba3e99..2c62ae650 100644 --- a/src/util/entities/ThreadMember.ts +++ b/src/util/entities/ThreadMember.ts @@ -71,7 +71,7 @@ export class ThreadMember extends BaseClassWithoutId { @Column() muted: boolean; - @Column({ nullable: true, type: "simple-json" }) + @Column({ nullable: true, type: "jsonb" }) mute_config?: ThreadMemberMuteConfig; @Column() diff --git a/src/util/entities/User.ts b/src/util/entities/User.ts index 65f425ffe..e01da3af3 100644 --- a/src/util/entities/User.ts +++ b/src/util/entities/User.ts @@ -160,7 +160,7 @@ export class User extends BaseClass { }) connected_accounts: ConnectedAccount[]; - @Column({ type: "simple-json", select: false }) + @Column({ type: "jsonb", select: false }) data: { valid_tokens_since: Date; // all tokens with a previous issue date are invalid hash?: string; // hash of the password, salt is saved in password (bcrypt) @@ -183,16 +183,16 @@ export class User extends BaseClass { @Column({ type: "simple-array", nullable: true }) badge_ids?: string[]; - @Column({ type: "simple-json", nullable: true }) + @Column({ type: "jsonb", nullable: true }) avatar_decoration_data?: AvatarDecorationData; - @Column({ type: "simple-json", nullable: true }) + @Column({ type: "jsonb", nullable: true }) display_name_styles?: DisplayNameStyle; - @Column({ type: "simple-json", nullable: true }) + @Column({ type: "jsonb", nullable: true }) collectibles?: Collectibles; - @Column({ type: "simple-json", nullable: true }) + @Column({ type: "jsonb", nullable: true }) primary_guild?: PrimaryGuild; // TODO: I don't like this method? diff --git a/src/util/entities/UserSettings.ts b/src/util/entities/UserSettings.ts index f3f6cb054..7ccb8e1ec 100644 --- a/src/util/entities/UserSettings.ts +++ b/src/util/entities/UserSettings.ts @@ -45,7 +45,7 @@ export class UserSettings extends BaseClassWithoutId { @Column({ nullable: true }) convert_emoticons: boolean = false; - @Column({ nullable: true, type: "simple-json" }) + @Column({ nullable: true, type: "jsonb" }) custom_status: CustomStatus | null = null; @Column({ nullable: true }) @@ -69,7 +69,7 @@ export class UserSettings extends BaseClassWithoutId { @Column({ nullable: true }) friend_discovery_flags: number = 0; - @Column({ nullable: true, type: "simple-json" }) + @Column({ nullable: true, type: "jsonb" }) friend_source_flags: FriendSourceFlags = { all: true }; @Column({ nullable: true }) @@ -78,10 +78,10 @@ export class UserSettings extends BaseClassWithoutId { @Column({ nullable: true }) gif_auto_play: boolean = false; - @Column({ nullable: true, type: "simple-json" }) + @Column({ nullable: true, type: "jsonb" }) guild_folders: GuildFolder[] = []; // every top guild is displayed as a "folder" - @Column({ nullable: true, type: "simple-json" }) + @Column({ nullable: true, type: "jsonb" }) guild_positions: string[] = []; // guild ids ordered by position @Column({ nullable: true }) @@ -105,7 +105,7 @@ export class UserSettings extends BaseClassWithoutId { @Column({ nullable: true }) render_reactions: boolean = true; - @Column({ nullable: true, type: "simple-json" }) + @Column({ nullable: true, type: "jsonb" }) restricted_guilds: string[] = []; @Column({ nullable: true }) diff --git a/src/util/migration/postgres/1776204936000-JsonbForJsonColumns.ts b/src/util/migration/postgres/1776204936000-JsonbForJsonColumns.ts new file mode 100644 index 000000000..03ac9a728 --- /dev/null +++ b/src/util/migration/postgres/1776204936000-JsonbForJsonColumns.ts @@ -0,0 +1,85 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class JsonbForJsonColumns1776204936000 implements MigrationInterface { + name = "JsonbForJsonColumns1776204936000"; + + public async up(queryRunner: QueryRunner): Promise { + await this.convertPks(queryRunner, "jsonb"); + } + + public async down(queryRunner: QueryRunner): Promise { + await this.convertPks(queryRunner, "varchar"); + } + + private async convertPks(queryRunner: QueryRunner, to: string) { + await queryRunner.query(`ALTER TABLE application_commands ALTER COLUMN options SET DEFAULT '[]'::jsonb;`); + await queryRunner.query(`ALTER TABLE application_commands ALTER COLUMN contexts TYPE ${to} USING contexts::${to};`); + await queryRunner.query(`ALTER TABLE application_commands ALTER COLUMN description_localizations TYPE ${to} USING description_localizations::${to};`); + await queryRunner.query(`ALTER TABLE application_commands ALTER COLUMN integration_types TYPE ${to} USING integration_types::${to};`); + await queryRunner.query(`ALTER TABLE application_commands ALTER COLUMN options TYPE ${to} USING options::${to};`); + await queryRunner.query(`ALTER TABLE application_commands ALTER COLUMN permissions TYPE ${to} USING permissions::${to};`); + + await queryRunner.query(`ALTER TABLE applications ALTER COLUMN "type" TYPE ${to} USING "type"::${to};`); + await queryRunner.query(`ALTER TABLE applications ALTER COLUMN install_params TYPE ${to} USING install_params::${to};`); + + await queryRunner.query(`ALTER TABLE audit_logs ALTER COLUMN changes TYPE ${to} USING changes::${to};`); + await queryRunner.query(`ALTER TABLE audit_logs ALTER COLUMN options TYPE ${to} USING options::${to};`); + + await queryRunner.query(`ALTER TABLE automod_rules ALTER COLUMN actions TYPE ${to} USING actions::${to};`); + await queryRunner.query(`ALTER TABLE automod_rules ALTER COLUMN trigger_metadata TYPE ${to} USING trigger_metadata::${to};`); + + await queryRunner.query(`ALTER TABLE categories ALTER COLUMN localizations TYPE ${to} USING localizations::${to};`); + + await queryRunner.query(`ALTER TABLE channels ALTER COLUMN permission_overwrites TYPE ${to} USING permission_overwrites::${to};`); + await queryRunner.query(`ALTER TABLE channels ALTER COLUMN thread_metadata TYPE ${to} USING thread_metadata::${to};`); + + await queryRunner.query(`ALTER TABLE connected_accounts ALTER COLUMN metadata TYPE ${to} USING metadata::${to};`); + await queryRunner.query(`ALTER TABLE connected_accounts ALTER COLUMN token_data TYPE ${to} USING token_data::${to};`); + + await queryRunner.query(`ALTER TABLE embed_cache ALTER COLUMN embed TYPE ${to} USING embed::${to};`); + await queryRunner.query(`ALTER TABLE embed_cache ALTER COLUMN embeds TYPE ${to} USING embeds::${to};`); + + await queryRunner.query(`ALTER TABLE guilds ALTER COLUMN welcome_screen TYPE ${to} USING welcome_screen::${to};`); + + await queryRunner.query(`ALTER TABLE members ALTER COLUMN avatar_decoration_data TYPE ${to} USING avatar_decoration_data::${to};`); + await queryRunner.query(`ALTER TABLE members ALTER COLUMN collectibles TYPE ${to} USING collectibles::${to};`); + await queryRunner.query(`ALTER TABLE members ALTER COLUMN display_name_styles TYPE ${to} USING display_name_styles::${to};`); + await queryRunner.query(`ALTER TABLE members ALTER COLUMN settings TYPE ${to} USING settings::${to};`); + + await queryRunner.query(`ALTER TABLE messages ALTER COLUMN message_snapshots SET DEFAULT '[]'::jsonb;`); + await queryRunner.query(`ALTER TABLE messages ALTER COLUMN activity TYPE ${to} USING activity::${to};`); + await queryRunner.query(`ALTER TABLE messages ALTER COLUMN components TYPE ${to} USING components::${to};`); + await queryRunner.query(`ALTER TABLE messages ALTER COLUMN embeds TYPE ${to} USING embeds::${to};`); + await queryRunner.query(`ALTER TABLE messages ALTER COLUMN interaction TYPE ${to} USING interaction::${to};`); + await queryRunner.query(`ALTER TABLE messages ALTER COLUMN interaction_metadata TYPE ${to} USING interaction_metadata::${to};`); + await queryRunner.query(`ALTER TABLE messages ALTER COLUMN message_reference TYPE ${to} USING message_reference::${to};`); + await queryRunner.query(`ALTER TABLE messages ALTER COLUMN message_snapshots TYPE ${to} USING message_snapshots::${to};`); + await queryRunner.query(`ALTER TABLE messages ALTER COLUMN poll TYPE ${to} USING poll::${to};`); + await queryRunner.query(`ALTER TABLE messages ALTER COLUMN reactions TYPE ${to} USING reactions::${to};`); + + await queryRunner.query(`ALTER TABLE roles ALTER COLUMN colors TYPE ${to} USING colors::${to};`); + await queryRunner.query(`ALTER TABLE roles ALTER COLUMN tags TYPE ${to} USING tags::${to};`); + + await queryRunner.query(`ALTER TABLE sessions ALTER COLUMN activities SET DEFAULT '[]'::jsonb;`); + await queryRunner.query(`ALTER TABLE sessions ALTER COLUMN activities TYPE ${to} USING activities::${to};`); + await queryRunner.query(`ALTER TABLE sessions ALTER COLUMN client_info TYPE ${to} USING client_info::${to};`); + await queryRunner.query(`ALTER TABLE sessions ALTER COLUMN client_status TYPE ${to} USING client_status::${to};`); + await queryRunner.query(`ALTER TABLE sessions ALTER COLUMN last_seen_location_info TYPE ${to} USING last_seen_location_info::${to};`); + + await queryRunner.query(`ALTER TABLE templates ALTER COLUMN serialized_source_guild TYPE ${to} USING serialized_source_guild::${to};`); + + await queryRunner.query(`ALTER TABLE thread_members ALTER COLUMN mute_config TYPE ${to} USING mute_config::${to};`); + + await queryRunner.query(`ALTER TABLE user_settings ALTER COLUMN custom_status TYPE ${to} USING custom_status::${to};`); + await queryRunner.query(`ALTER TABLE user_settings ALTER COLUMN friend_source_flags TYPE ${to} USING friend_source_flags::${to};`); + await queryRunner.query(`ALTER TABLE user_settings ALTER COLUMN guild_folders TYPE ${to} USING guild_folders::${to};`); + await queryRunner.query(`ALTER TABLE user_settings ALTER COLUMN guild_positions TYPE ${to} USING guild_positions::${to};`); + await queryRunner.query(`ALTER TABLE user_settings ALTER COLUMN restricted_guilds TYPE ${to} USING restricted_guilds::${to};`); + + await queryRunner.query(`ALTER TABLE users ALTER COLUMN avatar_decoration_data TYPE ${to} USING avatar_decoration_data::${to};`); + await queryRunner.query(`ALTER TABLE users ALTER COLUMN collectibles TYPE ${to} USING collectibles::${to};`); + await queryRunner.query(`ALTER TABLE users ALTER COLUMN data TYPE ${to} USING data::${to};`); + await queryRunner.query(`ALTER TABLE users ALTER COLUMN display_name_styles TYPE ${to} USING display_name_styles::${to};`); + await queryRunner.query(`ALTER TABLE users ALTER COLUMN primary_guild TYPE ${to} USING primary_guild::${to};`); + } +}