summaryrefslogtreecommitdiff
path: root/test/sql
diff options
context:
space:
mode:
Diffstat (limited to 'test/sql')
-rw-r--r--test/sql/test_functions.py8
-rw-r--r--test/sql/test_operators.py149
-rw-r--r--test/sql/test_types.py109
3 files changed, 222 insertions, 44 deletions
diff --git a/test/sql/test_functions.py b/test/sql/test_functions.py
index 51cfcb919..0074d789b 100644
--- a/test/sql/test_functions.py
+++ b/test/sql/test_functions.py
@@ -2,7 +2,7 @@ from sqlalchemy.testing import eq_, is_
import datetime
from sqlalchemy import func, select, Integer, literal, DateTime, Table, \
Column, Sequence, MetaData, extract, Date, String, bindparam, \
- literal_column, Array, Numeric
+ literal_column, ARRAY, Numeric
from sqlalchemy.sql import table, column
from sqlalchemy import sql, util
from sqlalchemy.sql.compiler import BIND_TEMPLATES
@@ -558,7 +558,7 @@ class ReturnTypeTest(fixtures.TestBase):
def test_array_agg(self):
expr = func.array_agg(column('data', Integer))
- is_(expr.type._type_affinity, Array)
+ is_(expr.type._type_affinity, ARRAY)
is_(expr.type.item_type._type_affinity, Integer)
def test_mode(self):
@@ -573,13 +573,13 @@ class ReturnTypeTest(fixtures.TestBase):
def test_percentile_cont_array(self):
expr = func.percentile_cont(0.5, 0.7).within_group(
column('data', Integer))
- is_(expr.type._type_affinity, Array)
+ is_(expr.type._type_affinity, ARRAY)
is_(expr.type.item_type._type_affinity, Integer)
def test_percentile_cont_array_desc(self):
expr = func.percentile_cont(0.5, 0.7).within_group(
column('data', Integer).desc())
- is_(expr.type._type_affinity, Array)
+ is_(expr.type._type_affinity, ARRAY)
is_(expr.type.item_type._type_affinity, Integer)
def test_cume_dist(self):
diff --git a/test/sql/test_operators.py b/test/sql/test_operators.py
index 03c0f89be..6a6c749a4 100644
--- a/test/sql/test_operators.py
+++ b/test/sql/test_operators.py
@@ -15,7 +15,7 @@ from sqlalchemy.sql.elements import _literal_as_text
from sqlalchemy.schema import Column, Table, MetaData
from sqlalchemy.sql import compiler
from sqlalchemy.types import TypeEngine, TypeDecorator, UserDefinedType, \
- Boolean, NullType, MatchType, Indexable, Concatenable, Array
+ Boolean, NullType, MatchType, Indexable, Concatenable, ARRAY, JSON
from sqlalchemy.dialects import mysql, firebird, postgresql, oracle, \
sqlite, mssql
from sqlalchemy import util
@@ -632,7 +632,125 @@ class ExtensionOperatorTest(fixtures.TestBase, testing.AssertsCompiledSQL):
)
-class IndexableTest(fixtures.TestBase, testing.AssertsCompiledSQL):
+class JSONIndexOpTest(fixtures.TestBase, testing.AssertsCompiledSQL):
+ def setUp(self):
+ class MyTypeCompiler(compiler.GenericTypeCompiler):
+ def visit_mytype(self, type, **kw):
+ return "MYTYPE"
+
+ def visit_myothertype(self, type, **kw):
+ return "MYOTHERTYPE"
+
+ class MyCompiler(compiler.SQLCompiler):
+
+ def visit_json_getitem_op_binary(self, binary, operator, **kw):
+ return self._generate_generic_binary(
+ binary, " -> ", **kw
+ )
+
+ def visit_json_path_getitem_op_binary(
+ self, binary, operator, **kw):
+ return self._generate_generic_binary(
+ binary, " #> ", **kw
+ )
+
+ def visit_getitem_binary(self, binary, operator, **kw):
+ raise NotImplementedError()
+
+ class MyDialect(default.DefaultDialect):
+ statement_compiler = MyCompiler
+ type_compiler = MyTypeCompiler
+
+ class MyType(JSON):
+ __visit_name__ = 'mytype'
+
+ pass
+
+ self.MyType = MyType
+ self.__dialect__ = MyDialect()
+
+ def test_setup_getitem(self):
+ col = Column('x', self.MyType())
+
+ is_(
+ col[5].type._type_affinity, JSON
+ )
+ is_(
+ col[5]['foo'].type._type_affinity, JSON
+ )
+ is_(
+ col[('a', 'b', 'c')].type._type_affinity, JSON
+ )
+
+ def test_getindex_literal_integer(self):
+
+ col = Column('x', self.MyType())
+
+ self.assert_compile(
+ col[5],
+ "x -> :x_1",
+ checkparams={'x_1': 5}
+ )
+
+ def test_getindex_literal_string(self):
+
+ col = Column('x', self.MyType())
+
+ self.assert_compile(
+ col['foo'],
+ "x -> :x_1",
+ checkparams={'x_1': 'foo'}
+ )
+
+ def test_path_getindex_literal(self):
+
+ col = Column('x', self.MyType())
+
+ self.assert_compile(
+ col[('a', 'b', 3, 4, 'd')],
+ "x #> :x_1",
+ checkparams={'x_1': ('a', 'b', 3, 4, 'd')}
+ )
+
+ def test_getindex_sqlexpr(self):
+
+ col = Column('x', self.MyType())
+ col2 = Column('y', Integer())
+
+ self.assert_compile(
+ col[col2],
+ "x -> y",
+ checkparams={}
+ )
+
+ self.assert_compile(
+ col[col2 + 8],
+ "x -> (y + :y_1)",
+ checkparams={'y_1': 8}
+ )
+
+ def test_override_operators(self):
+ special_index_op = operators.custom_op('$$>')
+
+ class MyOtherType(JSON, TypeEngine):
+ __visit_name__ = 'myothertype'
+
+ class Comparator(TypeEngine.Comparator):
+
+ def _adapt_expression(self, op, other_comparator):
+ return special_index_op, MyOtherType()
+
+ comparator_factory = Comparator
+
+ col = Column('x', MyOtherType())
+ self.assert_compile(
+ col[5],
+ "x $$> :x_1",
+ checkparams={'x_1': 5}
+ )
+
+
+class ArrayIndexOpTest(fixtures.TestBase, testing.AssertsCompiledSQL):
def setUp(self):
class MyTypeCompiler(compiler.GenericTypeCompiler):
def visit_mytype(self, type, **kw):
@@ -658,31 +776,14 @@ class IndexableTest(fixtures.TestBase, testing.AssertsCompiledSQL):
statement_compiler = MyCompiler
type_compiler = MyTypeCompiler
- class MyType(Indexable, TypeEngine):
+ class MyType(ARRAY):
__visit_name__ = 'mytype'
def __init__(self, zero_indexes=False, dimensions=1):
if zero_indexes:
self.zero_indexes = zero_indexes
self.dimensions = dimensions
-
- class Comparator(Indexable.Comparator):
- def _setup_getitem(self, index):
- if isinstance(index, slice):
- return_type = self.type
- elif self.type.dimensions is None or \
- self.type.dimensions == 1:
- return_type = Integer()
- else:
- adapt_kw = {'dimensions': self.type.dimensions - 1}
- # this is also testing the behavior of adapt()
- # that we can pass kw that override constructor kws.
- # required a small change to util.constructor_copy().
- return_type = self.type.adapt(
- self.type.__class__, **adapt_kw)
-
- return operators.getitem, index, return_type
- comparator_factory = Comparator
+ self.item_type = Integer()
self.MyType = MyType
self.__dialect__ = MyDialect()
@@ -694,13 +795,13 @@ class IndexableTest(fixtures.TestBase, testing.AssertsCompiledSQL):
col = Column('x', self.MyType(dimensions=3))
is_(
- col[5].type._type_affinity, self.MyType
+ col[5].type._type_affinity, ARRAY
)
eq_(
col[5].type.dimensions, 2
)
is_(
- col[5][6].type._type_affinity, self.MyType
+ col[5][6].type._type_affinity, ARRAY
)
eq_(
col[5][6].type.dimensions, 1
@@ -2273,7 +2374,7 @@ class AnyAllTest(fixtures.TestBase, testing.AssertsCompiledSQL):
t = Table(
'tab1', m,
- Column('arrval', Array(Integer)),
+ Column('arrval', ARRAY(Integer)),
Column('data', Integer)
)
return t
diff --git a/test/sql/test_types.py b/test/sql/test_types.py
index f1fb611fb..bb227bc5d 100644
--- a/test/sql/test_types.py
+++ b/test/sql/test_types.py
@@ -10,14 +10,14 @@ from sqlalchemy import (
and_, func, Date, LargeBinary, literal, cast, text, Enum,
type_coerce, VARCHAR, Time, DateTime, BigInteger, SmallInteger, BOOLEAN,
BLOB, NCHAR, NVARCHAR, CLOB, TIME, DATE, DATETIME, TIMESTAMP, SMALLINT,
- INTEGER, DECIMAL, NUMERIC, FLOAT, REAL, Array)
+ INTEGER, DECIMAL, NUMERIC, FLOAT, REAL, ARRAY, JSON)
from sqlalchemy.sql import ddl
from sqlalchemy.sql import visitors
from sqlalchemy import inspection
from sqlalchemy import exc, types, util, dialects
for name in dialects.__all__:
__import__("sqlalchemy.dialects.%s" % name)
-from sqlalchemy.sql import operators, column, table
+from sqlalchemy.sql import operators, column, table, null
from sqlalchemy.schema import CheckConstraint, AddConstraint
from sqlalchemy.engine import default
from sqlalchemy.testing.schema import Table, Column
@@ -140,7 +140,7 @@ class AdaptTest(fixtures.TestBase):
for is_down_adaption, typ, target_adaptions in adaptions():
if typ in (types.TypeDecorator, types.TypeEngine, types.Variant):
continue
- elif issubclass(typ, Array):
+ elif issubclass(typ, ARRAY):
t1 = typ(String)
else:
t1 = typ()
@@ -148,6 +148,8 @@ class AdaptTest(fixtures.TestBase):
if not issubclass(typ, types.Enum) and \
issubclass(cls, types.Enum):
continue
+ if cls.__module__.startswith("test"):
+ continue
# print("ADAPT %s -> %s" % (t1.__class__, cls))
t2 = t1.adapt(cls)
@@ -190,7 +192,7 @@ class AdaptTest(fixtures.TestBase):
for typ in self._all_types():
if typ in (types.TypeDecorator, types.TypeEngine, types.Variant):
continue
- elif issubclass(typ, Array):
+ elif issubclass(typ, ARRAY):
t1 = typ(String)
else:
t1 = typ()
@@ -1406,23 +1408,98 @@ class BinaryTest(fixtures.TestBase, AssertsExecutionResults):
return o.read()
+class JSONTest(fixtures.TestBase):
+
+ def setup(self):
+ metadata = MetaData()
+ self.test_table = Table('test_table', metadata,
+ Column('id', Integer, primary_key=True),
+ Column('test_column', JSON),
+ )
+ self.jsoncol = self.test_table.c.test_column
+
+ self.dialect = default.DefaultDialect()
+ self.dialect._json_serializer = None
+ self.dialect._json_deserializer = None
+
+ def test_bind_serialize_default(self):
+ proc = self.test_table.c.test_column.type._cached_bind_processor(
+ self.dialect)
+ eq_(
+ proc({"A": [1, 2, 3, True, False]}),
+ '{"A": [1, 2, 3, true, false]}'
+ )
+
+ def test_bind_serialize_None(self):
+ proc = self.test_table.c.test_column.type._cached_bind_processor(
+ self.dialect)
+ eq_(
+ proc(None),
+ 'null'
+ )
+
+ def test_bind_serialize_none_as_null(self):
+ proc = JSON(none_as_null=True)._cached_bind_processor(
+ self.dialect)
+ eq_(
+ proc(None),
+ None
+ )
+ eq_(
+ proc(null()),
+ None
+ )
+
+ def test_bind_serialize_null(self):
+ proc = self.test_table.c.test_column.type._cached_bind_processor(
+ self.dialect)
+ eq_(
+ proc(null()),
+ None
+ )
+
+ def test_result_deserialize_default(self):
+ proc = self.test_table.c.test_column.type._cached_result_processor(
+ self.dialect, None)
+ eq_(
+ proc('{"A": [1, 2, 3, true, false]}'),
+ {"A": [1, 2, 3, True, False]}
+ )
+
+ def test_result_deserialize_null(self):
+ proc = self.test_table.c.test_column.type._cached_result_processor(
+ self.dialect, None)
+ eq_(
+ proc('null'),
+ None
+ )
+
+ def test_result_deserialize_None(self):
+ proc = self.test_table.c.test_column.type._cached_result_processor(
+ self.dialect, None)
+ eq_(
+ proc(None),
+ None
+ )
+
+
class ArrayTest(fixtures.TestBase):
def _myarray_fixture(self):
- class MyArray(Array):
+ class MyArray(ARRAY):
pass
return MyArray
def test_array_index_map_dimensions(self):
- col = column('x', Array(Integer, dimensions=3))
+ col = column('x', ARRAY(Integer, dimensions=3))
is_(
- col[5].type._type_affinity, Array
+ col[5].type._type_affinity, ARRAY
)
eq_(
col[5].type.dimensions, 2
)
is_(
- col[5][6].type._type_affinity, Array
+ col[5][6].type._type_affinity, ARRAY
)
eq_(
col[5][6].type.dimensions, 1
@@ -1435,8 +1512,8 @@ class ArrayTest(fixtures.TestBase):
m = MetaData()
arrtable = Table(
'arrtable', m,
- Column('intarr', Array(Integer)),
- Column('strarr', Array(String)),
+ Column('intarr', ARRAY(Integer)),
+ Column('strarr', ARRAY(String)),
)
is_(arrtable.c.intarr[1].type._type_affinity, Integer)
is_(arrtable.c.strarr[1].type._type_affinity, String)
@@ -1445,11 +1522,11 @@ class ArrayTest(fixtures.TestBase):
m = MetaData()
arrtable = Table(
'arrtable', m,
- Column('intarr', Array(Integer)),
- Column('strarr', Array(String)),
+ Column('intarr', ARRAY(Integer)),
+ Column('strarr', ARRAY(String)),
)
- is_(arrtable.c.intarr[1:3].type._type_affinity, Array)
- is_(arrtable.c.strarr[1:3].type._type_affinity, Array)
+ is_(arrtable.c.intarr[1:3].type._type_affinity, ARRAY)
+ is_(arrtable.c.strarr[1:3].type._type_affinity, ARRAY)
def test_array_getitem_slice_type_dialect_level(self):
MyArray = self._myarray_fixture()
@@ -1459,8 +1536,8 @@ class ArrayTest(fixtures.TestBase):
Column('intarr', MyArray(Integer)),
Column('strarr', MyArray(String)),
)
- is_(arrtable.c.intarr[1:3].type._type_affinity, Array)
- is_(arrtable.c.strarr[1:3].type._type_affinity, Array)
+ is_(arrtable.c.intarr[1:3].type._type_affinity, ARRAY)
+ is_(arrtable.c.strarr[1:3].type._type_affinity, ARRAY)
# but the slice returns the actual type
assert isinstance(arrtable.c.intarr[1:3].type, MyArray)