Skip to content

Commit 25cdb9b

Browse files
feat: Add support for converting dy.Struct to postgres JSONB (#257)
1 parent b5819ed commit 25cdb9b

File tree

3 files changed

+14
-7
lines changed

3 files changed

+14
-7
lines changed

dataframely/_compat.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) QuantCo 2025-2025
1+
# Copyright (c) QuantCo 2025-2026
22
# SPDX-License-Identifier: BSD-3-Clause
33

44

@@ -29,13 +29,15 @@ class DeltaTable: # type: ignore # noqa: N801
2929
try:
3030
import sqlalchemy as sa
3131
import sqlalchemy.dialects.mssql as sa_mssql
32+
import sqlalchemy.dialects.postgresql as sa_postgresql
3233
from sqlalchemy import Dialect
3334
from sqlalchemy.dialects.mssql.pyodbc import MSDialect_pyodbc
3435
from sqlalchemy.dialects.postgresql.psycopg2 import PGDialect_psycopg2
3536
from sqlalchemy.sql.type_api import TypeEngine as sa_TypeEngine
3637
except ImportError:
3738
sa = _DummyModule("sqlalchemy") # type: ignore
3839
sa_mssql = _DummyModule("sqlalchemy") # type: ignore
40+
sa_postgresql = _DummyModule("sqlalchemy") # type: ignore
3941

4042
class sa_TypeEngine: # type: ignore # noqa: N801
4143
pass
@@ -81,6 +83,7 @@ class Dialect: # type: ignore # noqa: N801
8183
"pydantic_core_schema",
8284
"pydantic",
8385
"sa_mssql",
86+
"sa_postgresql",
8487
"sa_TypeEngine",
8588
"sa",
8689
]

dataframely/columns/struct.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) QuantCo 2025-2025
1+
# Copyright (c) QuantCo 2025-2026
22
# SPDX-License-Identifier: BSD-3-Clause
33

44
from __future__ import annotations
@@ -8,7 +8,7 @@
88

99
import polars as pl
1010

11-
from dataframely._compat import pa, sa, sa_TypeEngine
11+
from dataframely._compat import pa, sa, sa_postgresql, sa_TypeEngine
1212
from dataframely._polars import PolarsDataType
1313
from dataframely.random import Generator
1414

@@ -107,8 +107,11 @@ def validation_rules(self, expr: pl.Expr) -> dict[str, pl.Expr]:
107107
}
108108

109109
def sqlalchemy_dtype(self, dialect: sa.Dialect) -> sa_TypeEngine:
110-
# NOTE: We might want to add support for PostgreSQL's JSON in the future.
111-
raise NotImplementedError("SQL column cannot have 'Struct' type.")
110+
match dialect.name:
111+
case "postgresql":
112+
return sa_postgresql.JSONB()
113+
case _:
114+
raise NotImplementedError("SQL column cannot have 'Struct' type.")
112115

113116
@property
114117
def pyarrow_dtype(self) -> pa.DataType:

tests/columns/test_sqlalchemy_columns.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) QuantCo 2025-2025
1+
# Copyright (c) QuantCo 2025-2026
22
# SPDX-License-Identifier: BSD-3-Clause
33

44
import pytest
@@ -95,6 +95,7 @@ def test_mssql_datatype(column: Column, datatype: str) -> None:
9595
(dy.String(regex="^[abc]{1,3}d$"), "VARCHAR(4)"),
9696
(dy.Enum(["foo", "bar"]), "CHAR(3)"),
9797
(dy.Enum(["a", "abc"]), "VARCHAR(3)"),
98+
(dy.Struct({"a": dy.String(nullable=True)}), "JSONB"),
9899
],
99100
)
100101
def test_postgres_datatype(column: Column, datatype: str) -> None:
@@ -152,7 +153,7 @@ def test_raise_for_array_column(dialect: Dialect) -> None:
152153
dy.Array(dy.String(nullable=True), 1).sqlalchemy_dtype(dialect)
153154

154155

155-
@pytest.mark.parametrize("dialect", [MSDialect_pyodbc(), PGDialect_psycopg2()])
156+
@pytest.mark.parametrize("dialect", [MSDialect_pyodbc()])
156157
def test_raise_for_struct_column(dialect: Dialect) -> None:
157158
with pytest.raises(
158159
NotImplementedError, match="SQL column cannot have 'Struct' type."

0 commit comments

Comments
 (0)