Fix SQLAlchemy 2.x compatibility: wrap raw SQL in text()

SQLAlchemy 2.x requires raw SQL strings to be explicitly wrapped
in text(). Fixed 16 instances across 5 route files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Sharang Parnerkar
2026-03-10 13:56:38 +01:00
parent 033fa52e5b
commit 86588aff09
3 changed files with 28 additions and 25 deletions

View File

@@ -15,6 +15,7 @@ from typing import Any, Optional
from fastapi import APIRouter, HTTPException, Header from fastapi import APIRouter, HTTPException, Header
from pydantic import BaseModel from pydantic import BaseModel
from sqlalchemy import text
from database import SessionLocal from database import SessionLocal
@@ -75,13 +76,13 @@ async def get_compliance_scope(
db = SessionLocal() db = SessionLocal()
try: try:
row = db.execute( row = db.execute(
"""SELECT tenant_id, text("""SELECT tenant_id,
state->'compliance_scope' AS scope, state->'compliance_scope' AS scope,
created_at, created_at,
updated_at updated_at
FROM sdk_states FROM sdk_states
WHERE tenant_id = :tid WHERE tenant_id = :tid
AND state ? 'compliance_scope'""", AND state ? 'compliance_scope'"""),
{"tid": tid}, {"tid": tid},
).fetchone() ).fetchone()
@@ -106,22 +107,22 @@ async def upsert_compliance_scope(
db = SessionLocal() db = SessionLocal()
try: try:
db.execute( db.execute(
"""INSERT INTO sdk_states (tenant_id, state) text("""INSERT INTO sdk_states (tenant_id, state)
VALUES (:tid, jsonb_build_object('compliance_scope', :scope::jsonb)) VALUES (:tid, jsonb_build_object('compliance_scope', :scope::jsonb))
ON CONFLICT (tenant_id) DO UPDATE ON CONFLICT (tenant_id) DO UPDATE
SET state = sdk_states.state || jsonb_build_object('compliance_scope', :scope::jsonb), SET state = sdk_states.state || jsonb_build_object('compliance_scope', :scope::jsonb),
updated_at = NOW()""", updated_at = NOW()"""),
{"tid": tid, "scope": scope_json}, {"tid": tid, "scope": scope_json},
) )
db.commit() db.commit()
row = db.execute( row = db.execute(
"""SELECT tenant_id, text("""SELECT tenant_id,
state->'compliance_scope' AS scope, state->'compliance_scope' AS scope,
created_at, created_at,
updated_at updated_at
FROM sdk_states FROM sdk_states
WHERE tenant_id = :tid""", WHERE tenant_id = :tid"""),
{"tid": tid}, {"tid": tid},
).fetchone() ).fetchone()

View File

@@ -15,6 +15,7 @@ from typing import Optional
import httpx import httpx
from fastapi import APIRouter, File, Form, Header, UploadFile, HTTPException from fastapi import APIRouter, File, Form, Header, UploadFile, HTTPException
from pydantic import BaseModel from pydantic import BaseModel
from sqlalchemy import text
from database import SessionLocal from database import SessionLocal
@@ -291,11 +292,11 @@ async def analyze_document(
db = SessionLocal() db = SessionLocal()
try: try:
db.execute( db.execute(
"""INSERT INTO compliance_imported_documents text("""INSERT INTO compliance_imported_documents
(id, tenant_id, filename, file_type, file_size, detected_type, detection_confidence, (id, tenant_id, filename, file_type, file_size, detected_type, detection_confidence,
extracted_text, extracted_entities, recommendations, status, analyzed_at) extracted_text, extracted_entities, recommendations, status, analyzed_at)
VALUES (:id, :tenant_id, :filename, :file_type, :file_size, :detected_type, :confidence, VALUES (:id, :tenant_id, :filename, :file_type, :file_size, :detected_type, :confidence,
:text, :entities::jsonb, :recommendations::jsonb, 'analyzed', NOW())""", :text, :entities::jsonb, :recommendations::jsonb, 'analyzed', NOW())"""),
{ {
"id": doc_id, "id": doc_id,
"tenant_id": tenant_id, "tenant_id": tenant_id,
@@ -313,9 +314,9 @@ async def analyze_document(
if total_gaps > 0: if total_gaps > 0:
import json import json
db.execute( db.execute(
"""INSERT INTO compliance_gap_analyses text("""INSERT INTO compliance_gap_analyses
(tenant_id, document_id, total_gaps, critical_gaps, high_gaps, medium_gaps, low_gaps, gaps, recommended_packages) (tenant_id, document_id, total_gaps, critical_gaps, high_gaps, medium_gaps, low_gaps, gaps, recommended_packages)
VALUES (:tenant_id, :document_id, :total, :critical, :high, :medium, :low, :gaps::jsonb, :packages::jsonb)""", VALUES (:tenant_id, :document_id, :total, :critical, :high, :medium, :low, :gaps::jsonb, :packages::jsonb)"""),
{ {
"tenant_id": tenant_id, "tenant_id": tenant_id,
"document_id": doc_id, "document_id": doc_id,
@@ -358,7 +359,7 @@ async def get_gap_analysis(
db = SessionLocal() db = SessionLocal()
try: try:
result = db.execute( result = db.execute(
"SELECT * FROM compliance_gap_analyses WHERE document_id = :doc_id AND tenant_id = :tid", text("SELECT * FROM compliance_gap_analyses WHERE document_id = :doc_id AND tenant_id = :tid"),
{"doc_id": document_id, "tid": tid}, {"doc_id": document_id, "tid": tid},
).fetchone() ).fetchone()
if not result: if not result:
@@ -374,11 +375,11 @@ async def list_documents(tenant_id: str = "default"):
db = SessionLocal() db = SessionLocal()
try: try:
result = db.execute( result = db.execute(
"""SELECT id, filename, file_type, file_size, detected_type, detection_confidence, text("""SELECT id, filename, file_type, file_size, detected_type, detection_confidence,
extracted_entities, recommendations, status, analyzed_at, created_at extracted_entities, recommendations, status, analyzed_at, created_at
FROM compliance_imported_documents FROM compliance_imported_documents
WHERE tenant_id = :tenant_id WHERE tenant_id = :tenant_id
ORDER BY created_at DESC""", ORDER BY created_at DESC"""),
{"tenant_id": tenant_id}, {"tenant_id": tenant_id},
) )
rows = result.fetchall() rows = result.fetchall()
@@ -424,11 +425,11 @@ async def delete_document(
try: try:
# Delete gap analysis first (FK dependency) # Delete gap analysis first (FK dependency)
db.execute( db.execute(
"DELETE FROM compliance_gap_analyses WHERE document_id = :doc_id AND tenant_id = :tid", text("DELETE FROM compliance_gap_analyses WHERE document_id = :doc_id AND tenant_id = :tid"),
{"doc_id": document_id, "tid": tid}, {"doc_id": document_id, "tid": tid},
) )
result = db.execute( result = db.execute(
"DELETE FROM compliance_imported_documents WHERE id = :doc_id AND tenant_id = :tid", text("DELETE FROM compliance_imported_documents WHERE id = :doc_id AND tenant_id = :tid"),
{"doc_id": document_id, "tid": tid}, {"doc_id": document_id, "tid": tid},
) )
db.commit() db.commit()

View File

@@ -17,6 +17,7 @@ from typing import Optional
import httpx import httpx
from fastapi import APIRouter, File, Form, UploadFile, HTTPException from fastapi import APIRouter, File, Form, UploadFile, HTTPException
from pydantic import BaseModel from pydantic import BaseModel
from sqlalchemy import text
from database import SessionLocal from database import SessionLocal
@@ -366,13 +367,13 @@ async def scan_dependencies(
db = SessionLocal() db = SessionLocal()
try: try:
db.execute( db.execute(
"""INSERT INTO compliance_screenings text("""INSERT INTO compliance_screenings
(id, tenant_id, status, sbom_format, sbom_version, (id, tenant_id, status, sbom_format, sbom_version,
total_components, total_issues, critical_issues, high_issues, medium_issues, low_issues, total_components, total_issues, critical_issues, high_issues, medium_issues, low_issues,
sbom_data, started_at, completed_at) sbom_data, started_at, completed_at)
VALUES (:id, :tenant_id, 'completed', 'CycloneDX', '1.5', VALUES (:id, :tenant_id, 'completed', 'CycloneDX', '1.5',
:total_components, :total_issues, :critical, :high, :medium, :low, :total_components, :total_issues, :critical, :high, :medium, :low,
:sbom_data::jsonb, :started_at, :completed_at)""", :sbom_data::jsonb, :started_at, :completed_at)"""),
{ {
"id": screening_id, "id": screening_id,
"tenant_id": tenant_id, "tenant_id": tenant_id,
@@ -391,11 +392,11 @@ async def scan_dependencies(
# Persist security issues # Persist security issues
for issue in issues: for issue in issues:
db.execute( db.execute(
"""INSERT INTO compliance_security_issues text("""INSERT INTO compliance_security_issues
(id, screening_id, severity, title, description, cve, cvss, (id, screening_id, severity, title, description, cve, cvss,
affected_component, affected_version, fixed_in, remediation, status) affected_component, affected_version, fixed_in, remediation, status)
VALUES (:id, :screening_id, :severity, :title, :description, :cve, :cvss, VALUES (:id, :screening_id, :severity, :title, :description, :cve, :cvss,
:component, :version, :fixed_in, :remediation, :status)""", :component, :version, :fixed_in, :remediation, :status)"""),
{ {
"id": issue["id"], "id": issue["id"],
"screening_id": screening_id, "screening_id": screening_id,
@@ -486,10 +487,10 @@ async def get_screening(screening_id: str):
db = SessionLocal() db = SessionLocal()
try: try:
result = db.execute( result = db.execute(
"""SELECT id, status, sbom_format, sbom_version, text("""SELECT id, status, sbom_format, sbom_version,
total_components, total_issues, critical_issues, high_issues, total_components, total_issues, critical_issues, high_issues,
medium_issues, low_issues, sbom_data, started_at, completed_at medium_issues, low_issues, sbom_data, started_at, completed_at
FROM compliance_screenings WHERE id = :id""", FROM compliance_screenings WHERE id = :id"""),
{"id": screening_id}, {"id": screening_id},
) )
row = result.fetchone() row = result.fetchone()
@@ -498,9 +499,9 @@ async def get_screening(screening_id: str):
# Fetch issues # Fetch issues
issues_result = db.execute( issues_result = db.execute(
"""SELECT id, severity, title, description, cve, cvss, text("""SELECT id, severity, title, description, cve, cvss,
affected_component, affected_version, fixed_in, remediation, status affected_component, affected_version, fixed_in, remediation, status
FROM compliance_security_issues WHERE screening_id = :id""", FROM compliance_security_issues WHERE screening_id = :id"""),
{"id": screening_id}, {"id": screening_id},
) )
issues_rows = issues_result.fetchall() issues_rows = issues_result.fetchall()
@@ -566,12 +567,12 @@ async def list_screenings(tenant_id: str = "default"):
db = SessionLocal() db = SessionLocal()
try: try:
result = db.execute( result = db.execute(
"""SELECT id, status, total_components, total_issues, text("""SELECT id, status, total_components, total_issues,
critical_issues, high_issues, medium_issues, low_issues, critical_issues, high_issues, medium_issues, low_issues,
started_at, completed_at, created_at started_at, completed_at, created_at
FROM compliance_screenings FROM compliance_screenings
WHERE tenant_id = :tenant_id WHERE tenant_id = :tenant_id
ORDER BY created_at DESC""", ORDER BY created_at DESC"""),
{"tenant_id": tenant_id}, {"tenant_id": tenant_id},
) )
rows = result.fetchall() rows = result.fetchall()