Closed MIrinkov closed 11 months ago
it's a bit strange because a plain create table does do the parenthesis in that weird "double" way
from sqlalchemy.orm import declarative_base
from sqlalchemy import Column, Integer, Text, Index
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy import create_engine
Base = declarative_base()
class Thing(Base):
__tablename__ = 'thing'
id = Column(Integer, primary_key=True)
data = Column(JSONB(astext_type=Text()))
__table_args__ = (
Index("idx_1", data["file_id"].astext),
)
e = create_engine("postgresql://scott:tiger@localhost/test", echo=True)
Base.metadata.drop_all(e)
Base.metadata.create_all(e)
CREATE TABLE thing (
id SERIAL NOT NULL,
data JSONB,
PRIMARY KEY (id)
)
2023-10-12 13:01:42,808 INFO sqlalchemy.engine.Engine [no key 0.00063s] {}
2023-10-12 13:01:42,835 INFO sqlalchemy.engine.Engine CREATE INDEX idx_1 ON thing ((data ->> 'file_id'))
obviously the text rendering for alembic is trying to normalize parens out and is failing for some reason here
OK well the PG dialect in SQLAlchemy just calls self_group() on these expressions. so you can work around like this:
class TestTable(Base):
__tablename__ = "test_table"
id: Mapped[int] = mapped_column(primary_key=True)
meta: Mapped[dict] = mapped_column(sqlalchemy.dialects.postgresql.JSONB)
__table_args__ = (
sqlalchemy.Index("idx_meta_file_id", meta["file_id"].astext.self_group()),
)
that should resolve the issue here
the thing is , autogenerate on the second run will fail anyway as we dont do a good job comparing these indexes, so you'd really need to define the text like this probably:
sa.text('(data ->> \'$."file_id"\'::text)')
Mike Bayer has proposed a fix for this issue in the main branch:
apply PG ddl paren rules to index expressions https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/4921
the thing is , autogenerate on the second run will fail anyway as we dont do a good job comparing these indexes, so you'd really need to define the text like this probably:
sa.text('(data ->> \'$."file_id"\'::text)')
so pg returns these like that? wow that's unfortunate.
Do you want to add a rule to ignore any index that contains an expression with ->
and ->>
?
Describe the bug Alembic fails to generate a syntactically correct index migration when running
alembic revision --autogenerate
when creating an index on a field in a JSONB column using format likesqlalchemy.Index("idx_meta_file_id", meta["file_id"].astext)
(wheremeta
is a column in a model class,meta: Mapped[dict] = mapped_column(sqlalchemy.dialects.postgresql.JSONB)
).The constructed migration operations are:
Running those with
alembic upgrade head
results inExpected behavior The
[sa.text("meta ->> 'file_id'")],
is missing parentheses, it is supposed to be[sa.text("(meta ->> 'file_id')")],
because the correct postgres syntax for this index isI can currently achieve this by replacing the
.astext
version withMy question is - is this a known bug? Did I miss some configuration that's required for dialect JSONB to work properly? Or am I expected to use
text()
expressions in such cases?To Reproduce
Then run
alembic revision --autogenerate
, and the results will be as stated above - the required "extra" parentheses will be missing. Runningalembic upgrade head
will then result in the following error.Error
Versions.
Have a nice day!