fix: migration 0019 - use sa.text() with bindparams to avoid SQLAlchemy interpreting JSON colons as bind parameters

This commit is contained in:
Agent Zero
2026-07-15 22:06:24 +02:00
parent b490a62322
commit 7bb0eb1941
+16 -10
View File
@@ -67,29 +67,35 @@ def upgrade() -> None:
# ── Seed default roles per tenant ──
# For each tenant, create admin/editor/viewer role records if they don't exist
op.execute("""
# Use sa.text() with bindparams to avoid SQLAlchemy interpreting :read/:write as bind params
for role_name, perms_json in [
("admin", '{"*:*": true}'),
("editor", '{"core:*:read": true, "core:*:write": true, "core:*:create": true}'),
("viewer", '{"core:*:read": true}'),
]:
op.execute(
sa.text("""
INSERT INTO roles (id, tenant_id, name, permissions, denied_permissions, field_permissions, permission_version, created_at, updated_at)
SELECT
gen_random_uuid(),
t.id,
r.role_name,
r.permissions::jsonb,
:role_name,
:perms::jsonb,
'[]'::jsonb,
'{}'::jsonb,
1,
now(),
now()
FROM tenants t
CROSS JOIN (VALUES
('admin', '{"*:*": true}'::json),
('editor', '{"core:*:read": true, "core:*:write": true, "core:*:create": true}'::json),
('viewer', '{"core:*:read": true}'::json)
) AS r(role_name, permissions)
WHERE NOT EXISTS (
SELECT 1 FROM roles ro
WHERE ro.tenant_id = t.id AND ro.name = r.role_name
WHERE ro.tenant_id = t.id AND ro.name = :role_name
)
""").bindparams(
sa.bindparam("role_name", value=role_name),
sa.bindparam("perms", value=perms_json),
)
)
""")
# ── Migrate existing user.role_id to user_tenants.role_id ──
# For each user_tenants row, set role_id from users table if the user has one