Skip to content

Commit

Permalink
monetdb-dialect now passes 2.0.34 sqlalchemy test framework
Browse files Browse the repository at this point in the history
  • Loading branch information
aris-koning committed Sep 12, 2024
1 parent 1873545 commit 8631ee5
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 58 deletions.
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
pymonetdb==1.7.1
sqlalchemy==2.0.20
pymonetdb==1.8.2
sqlalchemy==2.0.34
4 changes: 2 additions & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ license=MIT
[options]
packages = find:
install_requires =
pymonetdb >= 1.7.1
sqlalchemy > 2.0.0
pymonetdb >= 1.8.2
sqlalchemy > 2.0.34
python_requires = >=3.8
zip_safe = no
test_suite = test.test_suite
Expand Down
51 changes: 16 additions & 35 deletions sqlalchemy_monetdb/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,6 @@ def get_column_specification(self, column, **kwargs):
colspec += " NOT NULL"
return colspec

def visit_check_constraint(self, constraint, **kwargs):
# TODO: this turns out to be an error in pytest
# util.warn("Skipped unsupported check constraint %s" % constraint.name)
return None

def visit_column_check_constraint(self, constraint, **kw):
# TODO: this turns out to be an error in pytest
# util.warn("Skipped unsupported check constraint %s" % constraint.name)
return None

def visit_create_index(self, create, **kw):
preparer = self.preparer
index = create.element
Expand Down Expand Up @@ -346,32 +336,23 @@ def visit_regexp_replace_op_binary(self, binary, operator, **kw):
self.render_literal_value(flags, sqltypes.STRINGTYPE),
)

def visit_json_getitem_op_binary(
self, binary, operator, _cast_applied=False, **kw
):
def _render_json_extract_from_binary(self, binary, operator, _cast_applied=False, **kw):
if (
not _cast_applied
and binary.type._type_affinity is not sqltypes.JSON
):
kw["_cast_applied"] = True
return self.process(cast(binary, binary.type), **kw)

left = self.process(binary.left, **kw)
right = self.process(binary.right, **kw)
if binary.type._type_affinity is sqltypes.JSON:
expr = "cast (json.filter(%s, %s) as json)"
return "JSON.FILTER(%s, %s)" % (left, right)
else:
expr = "json.filter(%s, %s)"
return "CASE JSON.FILTER(%s, %s) WHEN 'null' THEN NULL ELSE JSON.TEXT(JSON.FILTER(%s, %s)) END" % (left, right, left, right)

if not _cast_applied:
kw['_cast_applied'] = True
return self.process(cast(cast(binary, sqltypes.STRINGTYPE), binary.type), **kw)
def visit_json_getitem_op_binary(self, binary, operator, _cast_applied=False, **kw):
return self._render_json_extract_from_binary(binary, operator, _cast_applied, **kw)

return expr % (
self.process(binary.left, **kw),
self.process(binary.right, **kw),
)

def visit_json_path_getitem_op_binary(
self, binary, operator, _cast_applied=False, **kw
):
if not _cast_applied:
kw['_cast_applied'] = True
return self.process(cast(cast(binary, sqltypes.STRINGTYPE), binary.type), **kw)

# pdb.set_trace()
return "json.filter(%s, %s)" % (
self.process(binary.left, **kw),
self.process(binary.right, **kw),
)
def visit_json_path_getitem_op_binary(self, binary, operator, _cast_applied=False, **kw):
return self._render_json_extract_from_binary(binary, operator, _cast_applied, **kw)
53 changes: 49 additions & 4 deletions sqlalchemy_monetdb/dialect.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import json
import re
import typing
from typing import Optional
from typing import Optional, List, Any
from collections import defaultdict

from sqlalchemy import text
Expand All @@ -11,14 +11,16 @@

from sqlalchemy import pool, exc
from sqlalchemy.engine import default, reflection, ObjectScope, ObjectKind
from sqlalchemy.engine.interfaces import ReflectedCheckConstraint
from sqlalchemy.sql import sqltypes

from sqlalchemy_monetdb.base import MonetExecutionContext, MonetIdentifierPreparer
from sqlalchemy_monetdb.compiler import (
MonetDDLCompiler,
MonetTypeCompiler,
MonetCompiler,
)
from sqlalchemy_monetdb.monetdb_types import MONETDB_TYPE_MAP
from sqlalchemy_monetdb.monetdb_types import MONETDB_TYPE_MAP, JSONPathType

import pymonetdb

Expand Down Expand Up @@ -67,6 +69,10 @@ class MonetDialect(default.DefaultDialect):
type_compiler = MonetTypeCompiler
default_paramstyle = "named"

colspecs = {
sqltypes.JSON.JSONPathType: JSONPathType,
}

def __init__(self, json_serializer=None, json_deserializer=None, **kwargs):
default.DefaultDialect.__init__(self, **kwargs)
self._json_serializer = json_serializer
Expand Down Expand Up @@ -892,8 +898,47 @@ def get_unique_constraints(
res = [{"column_names": c, "name": n} for n, c in col_dict.items()]
return res

def get_check_constraints(self, connection, table_name, schema=None, **kw):
return []

def get_check_constraints(self, connection: "Connection", table_name: str, schema: str | None = None, **kw:Any) -> List[ReflectedCheckConstraint]:
"""Return information about check constraints in `table_name`.
Given a string `table_name` and an optional string `schema`, return
check constraint information as a list of dicts with these keys:
name
name of check constraint
sqltext
the check constraint’s SQL expression
**kw
other options passed to the dialect's get_check_constraints() method.
.. versionadded:: 2.0.0
"""

q = """
SELECT k.name name, sys.check_constraint(:schema, k.name) sqltext
FROM
sys.tables t,
sys.keys k
WHERE
k.table_id = t.id AND
t.id = :table_id AND
k.type = 4
order by name
"""

if schema is None:
schema = connection.execute(text("SELECT current_schema")).scalar()

args = {"table_id": self._table_id(connection, table_name, schema), "schema": schema}
c = connection.execute(text(q), args)
table = c.fetchall()

res = [{"name": name, "sqltext": sqltext} for name, sqltext in table]
return res

def get_isolation_level_values(self, dbapi_conn):
return (
Expand Down
2 changes: 1 addition & 1 deletion sqlalchemy_monetdb/requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -716,7 +716,7 @@ def unique_constraints_reflect_as_index(self):
@property
def check_constraint_reflection(self):
"""target dialect supports reflection of check constraints"""
return exclusions.closed()
return exclusions.open()

@property
def duplicate_key_raises_integrity_error(self):
Expand Down
9 changes: 6 additions & 3 deletions test/test_suite.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ def test_limit_render_multiple_times(*args, **kwargs):
class CTETest(CTETest):
pass

# @pytest.mark.skip(reason="The dialect is not supporting JSON type")
# class JSONTest(JSONTest):
# pass
class JSONTest:
@pytest.mark.skip(reason="MonetDB normalizes json input "
"by removing whitespace. "
"This is unexpected in this test.")
def test_round_trip_custom_json(self):
pass
11 changes: 0 additions & 11 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,14 +1,3 @@
# [tox]
# envlist = py{3.7,3.8,3.9,3.10}-sqlalchemy{13,14}-{pymonetdb,monetdbe}

# [testenv]
# deps =
# pymonetdb: pymonetdb
# monetdbe: monetdbe
# pytest
# coverage
# commands=py.test

[tox]
minversion = 4.11.3
envlist = py{38, 39, 310, 311}, flake8
Expand Down

0 comments on commit 8631ee5

Please sign in to comment.