Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: PostgreSQL compatibility #1413

Merged
merged 1 commit into from
Dec 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions backend/alembic/versions/0009_models_refactor.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import sqlalchemy as sa
from alembic import op
from sqlalchemy.exc import OperationalError
from utils.database import CustomJSON

# revision identifiers, used by Alembic.
revision = "0009_models_refactor"
Expand Down Expand Up @@ -84,13 +85,13 @@ def upgrade() -> None:
)
batch_op.alter_column(
"url_screenshots",
existing_type=sa.JSON(),
existing_type=CustomJSON(),
nullable=True,
existing_server_default=sa.text("(JSON_ARRAY())"),
)
batch_op.alter_column(
"path_screenshots",
existing_type=sa.JSON(),
existing_type=CustomJSON(),
nullable=True,
existing_server_default=sa.text("(JSON_ARRAY())"),
)
Expand Down Expand Up @@ -133,13 +134,13 @@ def downgrade() -> None:

batch_op.alter_column(
"path_screenshots",
existing_type=sa.JSON(),
existing_type=CustomJSON(),
nullable=False,
existing_server_default=sa.text("(JSON_ARRAY())"),
)
batch_op.alter_column(
"url_screenshots",
existing_type=sa.JSON(),
existing_type=CustomJSON(),
nullable=False,
existing_server_default=sa.text("(JSON_ARRAY())"),
)
Expand Down
7 changes: 4 additions & 3 deletions backend/alembic/versions/0012_add_regions_languages.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import sqlalchemy as sa
from alembic import op
from utils.database import CustomJSON, is_postgresql

# revision identifiers, used by Alembic.
revision = "0012_add_regions_languages"
Expand All @@ -19,8 +20,8 @@
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("roms", schema=None) as batch_op:
batch_op.add_column(sa.Column("regions", sa.JSON(), nullable=True))
batch_op.add_column(sa.Column("languages", sa.JSON(), nullable=True))
batch_op.add_column(sa.Column("regions", CustomJSON(), nullable=True))
batch_op.add_column(sa.Column("languages", CustomJSON(), nullable=True))

with op.batch_alter_table("roms", schema=None) as batch_op:
# Set default values for languages and regions
Expand All @@ -38,7 +39,7 @@ def downgrade() -> None:
batch_op.add_column(sa.Column("region", sa.VARCHAR(length=20), nullable=True))

with op.batch_alter_table("roms", schema=None) as batch_op:
if connection.engine.name == "postgresql":
if is_postgresql(connection):
batch_op.execute("UPDATE roms SET region = regions->>0")
else:
batch_op.execute("UPDATE roms SET region = JSON_EXTRACT(regions, '$[0]')")
Expand Down
9 changes: 5 additions & 4 deletions backend/alembic/versions/0014_asset_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from config.config_manager import SQLITE_DB_BASE_PATH, ConfigManager
from sqlalchemy import create_engine, text
from sqlalchemy.orm import sessionmaker
from utils.database import CustomJSON, is_postgresql

# revision identifiers, used by Alembic.
revision = "0014_asset_files"
Expand Down Expand Up @@ -164,7 +165,7 @@ def upgrade() -> None:
batch_op.drop_column("n_roms")

# Switch to new id column as platform primary key
if connection.engine.name == "postgresql":
if is_postgresql(connection):
op.execute("ALTER TABLE platforms ADD COLUMN id SERIAL PRIMARY KEY")
else:
op.execute(
Expand All @@ -179,7 +180,7 @@ def upgrade() -> None:
batch_op.add_column(
sa.Column("file_size_bytes", sa.BigInteger(), nullable=False)
)
batch_op.add_column(sa.Column("igdb_metadata", sa.JSON(), nullable=True))
batch_op.add_column(sa.Column("igdb_metadata", CustomJSON(), nullable=True))
batch_op.add_column(sa.Column("platform_id", sa.Integer(), nullable=False))
batch_op.alter_column(
"revision",
Expand All @@ -197,7 +198,7 @@ def upgrade() -> None:
batch_op.execute(
"update roms set file_name_no_ext = regexp_replace(file_name, '\\.[a-z]{2,}$', '')"
)
if connection.engine.name == "postgresql":
if is_postgresql(connection):
batch_op.execute(
"""
UPDATE roms
Expand Down Expand Up @@ -264,7 +265,7 @@ def downgrade() -> None:
batch_op.drop_constraint("fk_platform_id_roms", type_="foreignkey")

with op.batch_alter_table("roms", schema=None) as batch_op:
if connection.engine.name == "postgresql":
if is_postgresql(connection):
batch_op.execute(
"""
UPDATE roms
Expand Down
3 changes: 2 additions & 1 deletion backend/alembic/versions/0015_mobygames_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import sqlalchemy as sa
from alembic import op
from utils.database import CustomJSON

# revision identifiers, used by Alembic.
revision = "0015_mobygames_data"
Expand All @@ -23,7 +24,7 @@ def upgrade() -> None:

with op.batch_alter_table("roms", schema=None) as batch_op:
batch_op.add_column(sa.Column("moby_id", sa.Integer(), nullable=True))
batch_op.add_column(sa.Column("moby_metadata", sa.JSON(), nullable=True))
batch_op.add_column(sa.Column("moby_metadata", CustomJSON(), nullable=True))

with op.batch_alter_table("roms", schema=None) as batch_op:
batch_op.execute("update roms set moby_metadata = JSON_OBJECT()")
Expand Down
3 changes: 2 additions & 1 deletion backend/alembic/versions/0022_collections_.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from alembic import op
from config import RESOURCES_BASE_PATH
from sqlalchemy import inspect
from utils.database import CustomJSON

# revision identifiers, used by Alembic.
revision = "0022_collections"
Expand Down Expand Up @@ -94,7 +95,7 @@ def upgrade() -> None:
sa.Column("path_cover_l", sa.String(length=1000), nullable=True),
sa.Column("path_cover_s", sa.String(length=1000), nullable=True),
sa.Column("url_cover", sa.Text(), nullable=True),
sa.Column("roms", sa.JSON(), nullable=False),
sa.Column("roms", CustomJSON(), nullable=False),
sa.Column("user_id", sa.Integer(), nullable=False),
sa.Column("is_public", sa.Boolean(), nullable=False),
sa.Column(
Expand Down
3 changes: 2 additions & 1 deletion backend/alembic/versions/0023_make_columns_non_nullable.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import sqlalchemy as sa
from alembic import op
from utils.database import is_postgresql

# revision identifiers, used by Alembic.
revision = "0023_make_columns_non_nullable"
Expand All @@ -18,7 +19,7 @@

def upgrade() -> None:
connection = op.get_bind()
false_value = "FALSE" if connection.engine.name == "postgresql" else "0"
false_value = "FALSE" if is_postgresql(connection) else "0"

with op.batch_alter_table("platforms", schema=None) as batch_op:
batch_op.execute("UPDATE platforms SET name = '' WHERE name IS NULL")
Expand Down
3 changes: 2 additions & 1 deletion backend/alembic/versions/0024_sibling_roms_db_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import sqlalchemy as sa
from alembic import op
from utils.database import is_postgresql

# revision identifiers, used by Alembic.
revision = "0024_sibling_roms_db_view"
Expand All @@ -23,7 +24,7 @@ def upgrade() -> None:

connection = op.get_bind()
null_safe_equal_operator = (
"IS NOT DISTINCT FROM" if connection.engine.name == "postgresql" else "<=>"
"IS NOT DISTINCT FROM" if is_postgresql(connection) else "<=>"
)

connection.execute(
Expand Down
5 changes: 3 additions & 2 deletions backend/alembic/versions/0026_romuser_status_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects.postgresql import ENUM
from utils.database import is_postgresql

# revision identifiers, used by Alembic.
revision = "0026_romuser_status_fields"
Expand All @@ -33,7 +34,7 @@ def upgrade() -> None:
existing_nullable=True,
)

if connection.engine.name == "postgresql":
if is_postgresql(connection):
rom_user_status_enum = ENUM(
"INCOMPLETE",
"FINISHED",
Expand Down Expand Up @@ -80,7 +81,7 @@ def downgrade() -> None:
batch_op.drop_column("backlogged")
batch_op.drop_column("last_played")

if connection.engine.name == "postgresql":
if is_postgresql(connection):
ENUM(name="romuserstatus").drop(connection, checkfirst=False)

with op.batch_alter_table("collections", schema=None) as batch_op:
Expand Down
3 changes: 2 additions & 1 deletion backend/alembic/versions/1.6.2_.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import sqlalchemy as sa
from alembic import op
from sqlalchemy.exc import OperationalError
from utils.database import CustomJSON

# revision identifiers, used by Alembic.
revision = "1.6.2"
Expand Down Expand Up @@ -50,7 +51,7 @@ def upgrade() -> None:
sa.Column("has_cover", sa.Boolean(), nullable=True),
sa.Column("region", sa.String(length=20), nullable=True),
sa.Column("revision", sa.String(length=20), nullable=True),
sa.Column("tags", sa.JSON(), nullable=True),
sa.Column("tags", CustomJSON(), nullable=True),
sa.PrimaryKeyConstraint("p_slug", "file_name"),
)
except OperationalError:
Expand Down
3 changes: 2 additions & 1 deletion backend/alembic/versions/1.6.3_.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import sqlalchemy as sa
from alembic import op
from utils.database import CustomJSON

# revision identifiers, used by Alembic.
revision = "1.6.3"
Expand All @@ -20,7 +21,7 @@ def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("roms") as batch_op:
batch_op.add_column(sa.Column("multi", sa.Boolean(), nullable=True))
batch_op.add_column(sa.Column("files", sa.JSON(), nullable=True))
batch_op.add_column(sa.Column("files", CustomJSON(), nullable=True))
# ### end Alembic commands ###


Expand Down
5 changes: 3 additions & 2 deletions backend/alembic/versions/1.8.1_.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import sqlalchemy as sa
from alembic import op
from utils.database import CustomJSON

# revision identifiers, used by Alembic.
revision = "1.8.1"
Expand All @@ -22,15 +23,15 @@ def upgrade() -> None:
batch_op.add_column(
sa.Column(
"url_screenshots",
sa.JSON(),
CustomJSON(),
nullable=False,
server_default=sa.text("(JSON_ARRAY())"),
)
)
batch_op.add_column(
sa.Column(
"path_screenshots",
sa.JSON(),
CustomJSON(),
nullable=False,
server_default=sa.text("(JSON_ARRAY())"),
)
Expand Down
3 changes: 2 additions & 1 deletion backend/alembic/versions/1.8.3_.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"""

from alembic import op
from utils.database import is_postgresql

# revision identifiers, used by Alembic.
revision = "1.8.3"
Expand All @@ -27,7 +28,7 @@ def upgrade() -> None:
batch_op.execute(
"UPDATE roms SET path_cover_l = REPLACE(path_cover_l, '/romm/resources/', '')"
)
if connection.engine.name == "postgresql":
if is_postgresql(connection):
batch_op.execute(
"UPDATE roms SET path_screenshots = REPLACE(path_screenshots::text, '/romm/resources/', '')::jsonb"
)
Expand Down
5 changes: 3 additions & 2 deletions backend/alembic/versions/1.8_.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import sqlalchemy as sa
from alembic import op
from utils.database import CustomJSON

# revision identifiers, used by Alembic.
revision = "1.8"
Expand Down Expand Up @@ -78,9 +79,9 @@ def upgrade() -> None:
sa.Column("has_cover", sa.Boolean(), nullable=True),
sa.Column("region", sa.String(length=20), nullable=True),
sa.Column("revision", sa.String(length=20), nullable=True),
sa.Column("tags", sa.JSON(), nullable=True),
sa.Column("tags", CustomJSON(), nullable=True),
sa.Column("multi", sa.Boolean(), nullable=True),
sa.Column("files", sa.JSON(), nullable=True),
sa.Column("files", CustomJSON(), nullable=True),
sa.Column("url_cover", sa.Text(), nullable=True),
sa.PrimaryKeyConstraint("id"),
)
Expand Down
3 changes: 2 additions & 1 deletion backend/alembic/versions/2.0.0_.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects.postgresql import ENUM
from utils.database import is_postgresql

# revision identifiers, used by Alembic.
revision = "2.0.0"
Expand Down Expand Up @@ -47,5 +48,5 @@ def downgrade() -> None:

op.drop_table("users")

if connection.engine.name == "postgresql":
if is_postgresql(connection):
ENUM(name="role").drop(connection, checkfirst=False)
7 changes: 6 additions & 1 deletion backend/handler/database/roms_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from models.rom import Rom, RomUser
from sqlalchemy import and_, delete, func, or_, select, update
from sqlalchemy.orm import Query, Session, selectinload
from utils.database import json_array_contains_value

from .base_handler import DBBaseHandler

Expand Down Expand Up @@ -187,7 +188,11 @@ def get_rom_collections(
return (
session.scalars(
select(Collection)
.filter(func.json_contains(Collection.roms, f"{rom.id}"))
.filter(
json_array_contains_value(
Collection.roms, str(rom.id), session=session
)
)
.order_by(Collection.name.asc())
)
.unique()
Expand Down
5 changes: 3 additions & 2 deletions backend/models/collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@

from models.base import BaseModel
from models.user import User
from sqlalchemy import JSON, ForeignKey, String, Text
from sqlalchemy import ForeignKey, String, Text
from sqlalchemy.orm import Mapped, mapped_column, relationship
from utils.database import CustomJSON


class Collection(BaseModel):
Expand All @@ -24,7 +25,7 @@ class Collection(BaseModel):
)

roms: Mapped[set[int]] = mapped_column(
JSON, default=[], doc="Rom id's that belong to this collection"
CustomJSON(), default=[], doc="Rom id's that belong to this collection"
)

user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"))
Expand Down
Loading
Loading