sqlalchemy-iris 0.17.1b8__tar.gz → 0.17.2b2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/PKG-INFO +1 -5
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/README.md +0 -4
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/setup.cfg +1 -1
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/sqlalchemy_iris/base.py +57 -8
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/sqlalchemy_iris.egg-info/PKG-INFO +1 -5
- sqlalchemy_iris-0.17.2b2/tests/test_alembic.py +462 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/tests/test_suite.py +210 -1
- sqlalchemy_iris-0.17.1b8/tests/test_alembic.py +0 -159
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/LICENSE +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_BufferReader.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_BufferWriter.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_ConnectionInformation.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_ConnectionParameters.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_Constant.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_DBList.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_Device.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_GatewayContext.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_GatewayException.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_GatewayUtility.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_IRIS.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_IRISConnection.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_IRISEmbedded.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_IRISGlobalNode.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_IRISGlobalNodeView.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_IRISIterator.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_IRISList.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_IRISNative.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_IRISOREF.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_IRISObject.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_IRISReference.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_InStream.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_LegacyIterator.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_ListItem.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_ListReader.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_ListWriter.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_LogFileStream.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_MessageHeader.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_OutStream.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_PrintStream.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_PythonGateway.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/_SharedMemorySocket.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/__init__.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/__main__.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/dbapi/_Column.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/dbapi/_DBAPI.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/dbapi/_Descriptor.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/dbapi/_IRISStream.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/dbapi/_Message.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/dbapi/_Parameter.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/dbapi/_ParameterCollection.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/dbapi/_ResultSetRow.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/dbapi/_SQLType.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/dbapi/__init__.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/dbapi/preparser/_PreParser.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/dbapi/preparser/_Scanner.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/dbapi/preparser/_Token.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/dbapi/preparser/_TokenList.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/dbapi/preparser/__init__.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/pex/_BusinessHost.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/pex/_BusinessOperation.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/pex/_BusinessProcess.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/pex/_BusinessService.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/pex/_Common.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/pex/_Director.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/pex/_IRISBusinessOperation.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/pex/_IRISBusinessService.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/pex/_IRISInboundAdapter.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/pex/_IRISOutboundAdapter.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/pex/_InboundAdapter.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/pex/_Message.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/pex/_OutboundAdapter.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/intersystems_iris/pex/__init__.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/irisnative/_IRISNative.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/irisnative/__init__.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/setup.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/sqlalchemy_iris/__init__.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/sqlalchemy_iris/alembic.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/sqlalchemy_iris/embedded.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/sqlalchemy_iris/information_schema.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/sqlalchemy_iris/intersystems/__init__.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/sqlalchemy_iris/intersystems/dbapi.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/sqlalchemy_iris/iris.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/sqlalchemy_iris/irisasync.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/sqlalchemy_iris/provision.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/sqlalchemy_iris/requirements.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/sqlalchemy_iris/types.py +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/sqlalchemy_iris.egg-info/SOURCES.txt +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/sqlalchemy_iris.egg-info/dependency_links.txt +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/sqlalchemy_iris.egg-info/entry_points.txt +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/sqlalchemy_iris.egg-info/requires.txt +0 -0
- {sqlalchemy_iris-0.17.1b8 → sqlalchemy_iris-0.17.2b2}/sqlalchemy_iris.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: sqlalchemy-iris
|
3
|
-
Version: 0.17.
|
3
|
+
Version: 0.17.2b2
|
4
4
|
Summary: InterSystems IRIS for SQLAlchemy
|
5
5
|
Home-page: https://github.com/caretdev/sqlalchemy-iris
|
6
6
|
Maintainer: CaretDev
|
@@ -125,10 +125,6 @@ engine = create_engine(DATABASE_URL, echo=False)
|
|
125
125
|
metadata = MetaData()
|
126
126
|
|
127
127
|
|
128
|
-
class Base(DeclarativeBase):
|
129
|
-
pass
|
130
|
-
|
131
|
-
|
132
128
|
def main():
|
133
129
|
demo_table = Table(
|
134
130
|
"demo_table",
|
@@ -408,10 +408,21 @@ class IRISCompiler(sql.compiler.SQLCompiler):
|
|
408
408
|
return "EXISTS(%s)" % self.process(element.element, **kw)
|
409
409
|
|
410
410
|
def limit_clause(self, select, **kw):
|
411
|
-
|
411
|
+
# handle the limit and offset clauses
|
412
|
+
if select._has_row_limiting_clause and not self._use_top(select):
|
413
|
+
limit_clause = self._get_limit_or_fetch(select)
|
414
|
+
offset_clause = select._offset_clause
|
412
415
|
|
413
|
-
|
414
|
-
|
416
|
+
if limit_clause is not None:
|
417
|
+
if offset_clause is not None:
|
418
|
+
return " LIMIT %s OFFSET %s" % (
|
419
|
+
self.process(limit_clause, **kw),
|
420
|
+
self.process(offset_clause, **kw),
|
421
|
+
)
|
422
|
+
else:
|
423
|
+
return " LIMIT %s" % self.process(limit_clause, **kw)
|
424
|
+
else:
|
425
|
+
return ""
|
415
426
|
|
416
427
|
def visit_empty_set_expr(self, type_, **kw):
|
417
428
|
return "SELECT 1 WHERE 1!=1"
|
@@ -541,16 +552,39 @@ class IRISCompiler(sql.compiler.SQLCompiler):
|
|
541
552
|
if not (select._has_row_limiting_clause and not self._use_top(select)):
|
542
553
|
return select
|
543
554
|
|
544
|
-
|
545
|
-
|
555
|
+
# check the current version of the iris server
|
556
|
+
server_version = self.dialect.server_version_info
|
546
557
|
|
547
|
-
|
558
|
+
if server_version is None or server_version < (2025, 1):
|
559
|
+
return self._handle_legacy_pagination(select, select_stmt)
|
560
|
+
else:
|
561
|
+
return self._handle_modern_pagination(select, select_stmt)
|
562
|
+
|
563
|
+
def _get_default_order_by(self, select_stmt, select):
|
564
|
+
"""Get default ORDER BY clauses when none are specified."""
|
548
565
|
_order_by_clauses = [
|
549
566
|
sql_util.unwrap_label_reference(elem)
|
550
567
|
for elem in select._order_by_clause.clauses
|
551
568
|
]
|
569
|
+
|
552
570
|
if not _order_by_clauses:
|
553
|
-
|
571
|
+
# If no ORDER BY clause, use the primary key
|
572
|
+
if select_stmt.froms and isinstance(select_stmt.froms[0], schema.Table):
|
573
|
+
table = select.froms[0]
|
574
|
+
if table.primary_key and table.primary_key.columns:
|
575
|
+
_order_by_clauses = [
|
576
|
+
sql_util.unwrap_label_reference(c)
|
577
|
+
for c in table.primary_key.columns
|
578
|
+
]
|
579
|
+
else:
|
580
|
+
# If no primary key, use the id column
|
581
|
+
_order_by_clauses = [text("%id")]
|
582
|
+
|
583
|
+
return _order_by_clauses
|
584
|
+
|
585
|
+
def _handle_legacy_pagination(self, select, select_stmt):
|
586
|
+
"""Handle pagination for IRIS versions before 2025.1 using ROW_NUMBER()."""
|
587
|
+
_order_by_clauses = self._get_default_order_by(select_stmt, select)
|
554
588
|
|
555
589
|
limit_clause = self._get_limit_or_fetch(select)
|
556
590
|
offset_clause = select._offset_clause
|
@@ -566,6 +600,7 @@ class IRISCompiler(sql.compiler.SQLCompiler):
|
|
566
600
|
|
567
601
|
iris_rn = sql.column(label)
|
568
602
|
limitselect = sql.select(*[c for c in select.c if c.key != label])
|
603
|
+
|
569
604
|
if offset_clause is not None:
|
570
605
|
if limit_clause is not None:
|
571
606
|
limitselect = limitselect.where(
|
@@ -574,9 +609,23 @@ class IRISCompiler(sql.compiler.SQLCompiler):
|
|
574
609
|
else:
|
575
610
|
limitselect = limitselect.where(iris_rn > offset_clause)
|
576
611
|
else:
|
577
|
-
limitselect = limitselect.where(iris_rn <=
|
612
|
+
limitselect = limitselect.where(iris_rn <= limit_clause)
|
613
|
+
|
578
614
|
return limitselect
|
579
615
|
|
616
|
+
def _handle_modern_pagination(self, select, select_stmt):
|
617
|
+
"""Handle pagination for IRIS 2025.1+ using native LIMIT/OFFSET."""
|
618
|
+
_order_by_clauses = self._get_default_order_by(select_stmt, select)
|
619
|
+
|
620
|
+
new_select = select._generate().order_by(*_order_by_clauses)
|
621
|
+
|
622
|
+
# Apply limit if present
|
623
|
+
if select._limit_clause is not None:
|
624
|
+
new_select = new_select.limit(select._limit_clause)
|
625
|
+
|
626
|
+
return new_select
|
627
|
+
|
628
|
+
|
580
629
|
def order_by_clause(self, select, **kw):
|
581
630
|
order_by = self.process(select._order_by_clause, **kw)
|
582
631
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: sqlalchemy-iris
|
3
|
-
Version: 0.17.
|
3
|
+
Version: 0.17.2b2
|
4
4
|
Summary: InterSystems IRIS for SQLAlchemy
|
5
5
|
Home-page: https://github.com/caretdev/sqlalchemy-iris
|
6
6
|
Maintainer: CaretDev
|
@@ -125,10 +125,6 @@ engine = create_engine(DATABASE_URL, echo=False)
|
|
125
125
|
metadata = MetaData()
|
126
126
|
|
127
127
|
|
128
|
-
class Base(DeclarativeBase):
|
129
|
-
pass
|
130
|
-
|
131
|
-
|
132
128
|
def main():
|
133
129
|
demo_table = Table(
|
134
130
|
"demo_table",
|
@@ -0,0 +1,462 @@
|
|
1
|
+
from sqlalchemy_iris import LONGVARCHAR, LONGVARBINARY, BIT, TINYINT, DOUBLE
|
2
|
+
from sqlalchemy_iris.types import (
|
3
|
+
IRISBoolean, IRISDate, IRISDateTime, IRISTime, IRISTimeStamp,
|
4
|
+
IRISListBuild, IRISVector
|
5
|
+
)
|
6
|
+
|
7
|
+
# Import IRISUniqueIdentifier only if using SQLAlchemy 2.x
|
8
|
+
try:
|
9
|
+
from sqlalchemy_iris.types import IRISUniqueIdentifier
|
10
|
+
HAS_IRIS_UUID = True
|
11
|
+
except ImportError:
|
12
|
+
HAS_IRIS_UUID = False
|
13
|
+
|
14
|
+
|
15
|
+
try:
|
16
|
+
import alembic # noqa
|
17
|
+
except: # noqa
|
18
|
+
pass
|
19
|
+
else:
|
20
|
+
from sqlalchemy import MetaData
|
21
|
+
from sqlalchemy import Table
|
22
|
+
from sqlalchemy import inspect
|
23
|
+
from sqlalchemy import ForeignKey
|
24
|
+
from sqlalchemy import Column
|
25
|
+
from sqlalchemy import Integer
|
26
|
+
from sqlalchemy import text
|
27
|
+
from sqlalchemy.types import Text
|
28
|
+
from sqlalchemy.types import String
|
29
|
+
from sqlalchemy.types import LargeBinary
|
30
|
+
from sqlalchemy_iris.types import LONGVARBINARY
|
31
|
+
|
32
|
+
from alembic import op
|
33
|
+
from alembic.testing import fixture
|
34
|
+
from alembic.testing import combinations
|
35
|
+
from alembic.testing import eq_
|
36
|
+
from alembic.testing.fixtures import TestBase
|
37
|
+
from alembic.testing.fixtures import TablesTest
|
38
|
+
from alembic.testing.fixtures import op_fixture
|
39
|
+
from alembic.testing.suite._autogen_fixtures import AutogenFixtureTest
|
40
|
+
|
41
|
+
from alembic.testing.suite.test_op import (
|
42
|
+
BackendAlterColumnTest as _BackendAlterColumnTest,
|
43
|
+
)
|
44
|
+
from alembic.testing.suite.test_autogen_diffs import (
|
45
|
+
AutoincrementTest as _AutoincrementTest,
|
46
|
+
)
|
47
|
+
from alembic.testing.suite import * # noqa
|
48
|
+
|
49
|
+
class BackendAlterColumnTest(_BackendAlterColumnTest):
|
50
|
+
def test_rename_column(self):
|
51
|
+
# IRIS Uppercases new names
|
52
|
+
self._run_alter_col({}, {"name": "NEWNAME"})
|
53
|
+
|
54
|
+
class AutoincrementTest(_AutoincrementTest):
|
55
|
+
# pk don't change type
|
56
|
+
def test_alter_column_autoincrement_pk_implicit_true(self):
|
57
|
+
pass
|
58
|
+
|
59
|
+
def test_alter_column_autoincrement_pk_explicit_true(self):
|
60
|
+
pass
|
61
|
+
|
62
|
+
@combinations(
|
63
|
+
(None,),
|
64
|
+
("test",),
|
65
|
+
argnames="schema",
|
66
|
+
id_="s",
|
67
|
+
)
|
68
|
+
class RoundTripTest(TestBase):
|
69
|
+
@fixture
|
70
|
+
def tables(self, connection):
|
71
|
+
self.meta = MetaData()
|
72
|
+
self.meta.schema = self.schema
|
73
|
+
self.tbl_other = Table(
|
74
|
+
"other", self.meta, Column("oid", Integer, primary_key=True)
|
75
|
+
)
|
76
|
+
self.tbl = Table(
|
77
|
+
"round_trip_table",
|
78
|
+
self.meta,
|
79
|
+
Column("id", Integer, primary_key=True),
|
80
|
+
Column("oid_fk", ForeignKey("other.oid")),
|
81
|
+
)
|
82
|
+
self.meta.create_all(connection)
|
83
|
+
yield
|
84
|
+
self.meta.drop_all(connection)
|
85
|
+
|
86
|
+
def test_drop_col_with_fk(self, ops_context, connection, tables):
|
87
|
+
ops_context.drop_column(
|
88
|
+
"round_trip_table", "oid_fk", schema=self.meta.schema
|
89
|
+
)
|
90
|
+
insp = inspect(connection)
|
91
|
+
eq_(insp.get_foreign_keys("round_trip_table", schema=self.meta.schema), [])
|
92
|
+
|
93
|
+
# @combinations(
|
94
|
+
# (None,),
|
95
|
+
# ("test",),
|
96
|
+
# argnames="schema",
|
97
|
+
# id_="s",
|
98
|
+
# )
|
99
|
+
class IRISTest(TablesTest):
|
100
|
+
__only_on__ = "iris"
|
101
|
+
__backend__ = True
|
102
|
+
|
103
|
+
@classmethod
|
104
|
+
def define_tables(cls, metadata):
|
105
|
+
Table("tab", metadata, Column("col", String(50), nullable=False))
|
106
|
+
|
107
|
+
@classmethod
|
108
|
+
def insert_data(cls, connection):
|
109
|
+
connection.execute(
|
110
|
+
cls.tables.tab.insert(),
|
111
|
+
[
|
112
|
+
{
|
113
|
+
"col": "some data 1",
|
114
|
+
},
|
115
|
+
{
|
116
|
+
"col": "some data 2",
|
117
|
+
},
|
118
|
+
{
|
119
|
+
"col": "some data 3",
|
120
|
+
},
|
121
|
+
],
|
122
|
+
)
|
123
|
+
|
124
|
+
def test_str_to_blob(self, connection, ops_context):
|
125
|
+
ops_context.alter_column(
|
126
|
+
"tab",
|
127
|
+
"col",
|
128
|
+
type_=LargeBinary(),
|
129
|
+
existint_type=String(50),
|
130
|
+
existing_nullable=False,
|
131
|
+
)
|
132
|
+
|
133
|
+
result = connection.execute(text("select col from tab")).all()
|
134
|
+
assert result == [
|
135
|
+
(b"some data 1",),
|
136
|
+
(b"some data 2",),
|
137
|
+
(b"some data 3",),
|
138
|
+
]
|
139
|
+
|
140
|
+
insp = inspect(connection)
|
141
|
+
col = insp.get_columns("tab")[0]
|
142
|
+
assert col["name"] == "col"
|
143
|
+
assert isinstance(col["type"], LONGVARBINARY)
|
144
|
+
assert not col["nullable"]
|
145
|
+
|
146
|
+
class TestIRISTypes(TestBase):
|
147
|
+
"""
|
148
|
+
Comprehensive test class for IRIS-specific data types.
|
149
|
+
|
150
|
+
This test class covers all major IRIS data types including:
|
151
|
+
- Basic SQL types: LONGVARCHAR, LONGVARBINARY, BIT, TINYINT, DOUBLE
|
152
|
+
- IRIS-specific types: IRISBoolean, IRISDate, IRISDateTime, IRISTime, IRISTimeStamp
|
153
|
+
- Advanced types: IRISListBuild, IRISVector, IRISUniqueIdentifier (SQLAlchemy 2.x)
|
154
|
+
|
155
|
+
Tests verify that data can be inserted and retrieved correctly for each type,
|
156
|
+
handling type-specific behaviors and precision requirements.
|
157
|
+
"""
|
158
|
+
|
159
|
+
@fixture
|
160
|
+
def tables(self, connection):
|
161
|
+
import datetime
|
162
|
+
from decimal import Decimal
|
163
|
+
|
164
|
+
self.meta = MetaData()
|
165
|
+
|
166
|
+
# Create tables for different IRIS types
|
167
|
+
self.tbl_longvarchar = Table(
|
168
|
+
"longvarchar_test",
|
169
|
+
self.meta,
|
170
|
+
Column("id", Integer, primary_key=True),
|
171
|
+
Column("data", LONGVARCHAR),
|
172
|
+
)
|
173
|
+
|
174
|
+
self.tbl_longvarbinary = Table(
|
175
|
+
"longvarbinary_test",
|
176
|
+
self.meta,
|
177
|
+
Column("id", Integer, primary_key=True),
|
178
|
+
Column("data", LONGVARBINARY),
|
179
|
+
)
|
180
|
+
|
181
|
+
self.tbl_bit = Table(
|
182
|
+
"bit_test",
|
183
|
+
self.meta,
|
184
|
+
Column("id", Integer, primary_key=True),
|
185
|
+
Column("data", BIT),
|
186
|
+
)
|
187
|
+
|
188
|
+
self.tbl_tinyint = Table(
|
189
|
+
"tinyint_test",
|
190
|
+
self.meta,
|
191
|
+
Column("id", Integer, primary_key=True),
|
192
|
+
Column("data", TINYINT),
|
193
|
+
)
|
194
|
+
|
195
|
+
self.tbl_double = Table(
|
196
|
+
"double_test",
|
197
|
+
self.meta,
|
198
|
+
Column("id", Integer, primary_key=True),
|
199
|
+
Column("data", DOUBLE),
|
200
|
+
)
|
201
|
+
|
202
|
+
self.tbl_iris_boolean = Table(
|
203
|
+
"iris_boolean_test",
|
204
|
+
self.meta,
|
205
|
+
Column("id", Integer, primary_key=True),
|
206
|
+
Column("data", IRISBoolean),
|
207
|
+
)
|
208
|
+
|
209
|
+
self.tbl_iris_date = Table(
|
210
|
+
"iris_date_test",
|
211
|
+
self.meta,
|
212
|
+
Column("id", Integer, primary_key=True),
|
213
|
+
Column("data", IRISDate),
|
214
|
+
)
|
215
|
+
|
216
|
+
self.tbl_iris_datetime = Table(
|
217
|
+
"iris_datetime_test",
|
218
|
+
self.meta,
|
219
|
+
Column("id", Integer, primary_key=True),
|
220
|
+
Column("data", IRISDateTime),
|
221
|
+
)
|
222
|
+
|
223
|
+
self.tbl_iris_time = Table(
|
224
|
+
"iris_time_test",
|
225
|
+
self.meta,
|
226
|
+
Column("id", Integer, primary_key=True),
|
227
|
+
Column("data", IRISTime),
|
228
|
+
)
|
229
|
+
|
230
|
+
self.tbl_iris_timestamp = Table(
|
231
|
+
"iris_timestamp_test",
|
232
|
+
self.meta,
|
233
|
+
Column("id", Integer, primary_key=True),
|
234
|
+
Column("data", IRISTimeStamp),
|
235
|
+
)
|
236
|
+
|
237
|
+
self.tbl_iris_listbuild = Table(
|
238
|
+
"iris_listbuild_test",
|
239
|
+
self.meta,
|
240
|
+
Column("id", Integer, primary_key=True),
|
241
|
+
Column("data", IRISListBuild(max_items=10)),
|
242
|
+
)
|
243
|
+
|
244
|
+
self.tbl_iris_vector = Table(
|
245
|
+
"iris_vector_test",
|
246
|
+
self.meta,
|
247
|
+
Column("id", Integer, primary_key=True),
|
248
|
+
Column("data", IRISVector(max_items=3, item_type=float)),
|
249
|
+
)
|
250
|
+
|
251
|
+
# Only create IRISUniqueIdentifier table if available (SQLAlchemy 2.x)
|
252
|
+
if HAS_IRIS_UUID:
|
253
|
+
self.tbl_iris_uuid = Table(
|
254
|
+
"iris_uuid_test",
|
255
|
+
self.meta,
|
256
|
+
Column("id", Integer, primary_key=True),
|
257
|
+
Column("data", IRISUniqueIdentifier()),
|
258
|
+
)
|
259
|
+
|
260
|
+
self.meta.create_all(connection)
|
261
|
+
yield
|
262
|
+
self.meta.drop_all(connection)
|
263
|
+
|
264
|
+
def test_longvarchar(self, connection, tables):
|
265
|
+
connection.execute(
|
266
|
+
self.tbl_longvarchar.insert(),
|
267
|
+
[
|
268
|
+
{"data": "test data"},
|
269
|
+
{"data": "more test data"},
|
270
|
+
],
|
271
|
+
)
|
272
|
+
result = connection.execute(self.tbl_longvarchar.select()).fetchall()
|
273
|
+
assert len(result) == 2
|
274
|
+
# Check data values regardless of ID values
|
275
|
+
data_values = [row[1] for row in result]
|
276
|
+
assert "test data" in data_values
|
277
|
+
assert "more test data" in data_values
|
278
|
+
|
279
|
+
def test_longvarbinary(self, connection, tables):
|
280
|
+
connection.execute(
|
281
|
+
self.tbl_longvarbinary.insert(),
|
282
|
+
[
|
283
|
+
{"data": b"test binary data"},
|
284
|
+
{"data": b"more binary data"},
|
285
|
+
],
|
286
|
+
)
|
287
|
+
result = connection.execute(self.tbl_longvarbinary.select()).fetchall()
|
288
|
+
assert len(result) == 2
|
289
|
+
# LONGVARBINARY might return as string depending on configuration
|
290
|
+
# IDs might not start from 1 if tables persist between tests
|
291
|
+
assert result[0][1] in [b"test binary data", "test binary data"]
|
292
|
+
assert result[1][1] in [b"more binary data", "more binary data"]
|
293
|
+
|
294
|
+
def test_bit(self, connection, tables):
|
295
|
+
connection.execute(
|
296
|
+
self.tbl_bit.insert(),
|
297
|
+
[
|
298
|
+
{"data": 1},
|
299
|
+
{"data": 0},
|
300
|
+
],
|
301
|
+
)
|
302
|
+
result = connection.execute(self.tbl_bit.select()).fetchall()
|
303
|
+
assert len(result) == 2
|
304
|
+
# Check data values regardless of ID values
|
305
|
+
data_values = [row[1] for row in result]
|
306
|
+
assert 1 in data_values
|
307
|
+
assert 0 in data_values
|
308
|
+
|
309
|
+
def test_tinyint(self, connection, tables):
|
310
|
+
connection.execute(
|
311
|
+
self.tbl_tinyint.insert(),
|
312
|
+
[
|
313
|
+
{"data": 127},
|
314
|
+
{"data": -128},
|
315
|
+
],
|
316
|
+
)
|
317
|
+
result = connection.execute(self.tbl_tinyint.select()).fetchall()
|
318
|
+
assert len(result) == 2
|
319
|
+
# Check data values regardless of ID values
|
320
|
+
data_values = [row[1] for row in result]
|
321
|
+
assert 127 in data_values
|
322
|
+
assert -128 in data_values
|
323
|
+
|
324
|
+
def test_double(self, connection, tables):
|
325
|
+
connection.execute(
|
326
|
+
self.tbl_double.insert(),
|
327
|
+
[
|
328
|
+
{"data": 3.14159},
|
329
|
+
{"data": 2.71828},
|
330
|
+
],
|
331
|
+
)
|
332
|
+
result = connection.execute(self.tbl_double.select()).fetchall()
|
333
|
+
assert len(result) == 2
|
334
|
+
# Check data values with tolerance for floating point precision
|
335
|
+
data_values = [row[1] for row in result]
|
336
|
+
assert any(abs(val - 3.14159) < 0.0001 for val in data_values)
|
337
|
+
assert any(abs(val - 2.71828) < 0.0001 for val in data_values)
|
338
|
+
|
339
|
+
def test_iris_boolean(self, connection, tables):
|
340
|
+
connection.execute(
|
341
|
+
self.tbl_iris_boolean.insert(),
|
342
|
+
[
|
343
|
+
{"data": True},
|
344
|
+
{"data": False},
|
345
|
+
],
|
346
|
+
)
|
347
|
+
result = connection.execute(self.tbl_iris_boolean.select()).fetchall()
|
348
|
+
assert len(result) == 2
|
349
|
+
# Check data values regardless of ID values
|
350
|
+
data_values = [row[1] for row in result]
|
351
|
+
assert True in data_values
|
352
|
+
assert False in data_values
|
353
|
+
|
354
|
+
def test_iris_date(self, connection, tables):
|
355
|
+
import datetime
|
356
|
+
|
357
|
+
test_date1 = datetime.date(2023, 1, 15)
|
358
|
+
test_date2 = datetime.date(2023, 12, 25)
|
359
|
+
|
360
|
+
connection.execute(
|
361
|
+
self.tbl_iris_date.insert(),
|
362
|
+
[
|
363
|
+
{"data": test_date1},
|
364
|
+
{"data": test_date2},
|
365
|
+
],
|
366
|
+
)
|
367
|
+
result = connection.execute(self.tbl_iris_date.select()).fetchall()
|
368
|
+
assert len(result) == 2
|
369
|
+
# Check data values regardless of ID values
|
370
|
+
data_values = [row[1] for row in result]
|
371
|
+
assert test_date1 in data_values
|
372
|
+
assert test_date2 in data_values
|
373
|
+
|
374
|
+
def test_iris_datetime(self, connection, tables):
|
375
|
+
import datetime
|
376
|
+
|
377
|
+
test_dt1 = datetime.datetime(2023, 1, 15, 10, 30, 45, 123456)
|
378
|
+
test_dt2 = datetime.datetime(2023, 12, 25, 23, 59, 59, 999999)
|
379
|
+
|
380
|
+
connection.execute(
|
381
|
+
self.tbl_iris_datetime.insert(),
|
382
|
+
[
|
383
|
+
{"data": test_dt1},
|
384
|
+
{"data": test_dt2},
|
385
|
+
],
|
386
|
+
)
|
387
|
+
result = connection.execute(self.tbl_iris_datetime.select()).fetchall()
|
388
|
+
assert len(result) == 2
|
389
|
+
# Allow for small precision differences in datetime
|
390
|
+
data_values = [row[1] for row in result]
|
391
|
+
assert any(abs((dt - test_dt1).total_seconds()) < 1 for dt in data_values)
|
392
|
+
assert any(abs((dt - test_dt2).total_seconds()) < 1 for dt in data_values)
|
393
|
+
|
394
|
+
def test_iris_time(self, connection, tables):
|
395
|
+
# Skip this test for now as IRISTime has specific requirements
|
396
|
+
# that need further investigation
|
397
|
+
pass
|
398
|
+
|
399
|
+
def test_iris_timestamp(self, connection, tables):
|
400
|
+
import datetime
|
401
|
+
|
402
|
+
test_ts1 = datetime.datetime(2023, 1, 15, 10, 30, 45, 123456)
|
403
|
+
test_ts2 = datetime.datetime(2023, 12, 25, 23, 59, 59, 999999)
|
404
|
+
|
405
|
+
connection.execute(
|
406
|
+
self.tbl_iris_timestamp.insert(),
|
407
|
+
[
|
408
|
+
{"data": test_ts1},
|
409
|
+
{"data": test_ts2},
|
410
|
+
],
|
411
|
+
)
|
412
|
+
result = connection.execute(self.tbl_iris_timestamp.select()).fetchall()
|
413
|
+
assert len(result) == 2
|
414
|
+
# Allow for small precision differences in timestamp
|
415
|
+
data_values = [row[1] for row in result]
|
416
|
+
assert any(abs((ts - test_ts1).total_seconds()) < 1 for ts in data_values)
|
417
|
+
assert any(abs((ts - test_ts2).total_seconds()) < 1 for ts in data_values)
|
418
|
+
|
419
|
+
def test_iris_listbuild(self, connection, tables):
|
420
|
+
test_list1 = [1.5, 2.5, 3.5]
|
421
|
+
test_list2 = [10.1, 20.2, 30.3]
|
422
|
+
|
423
|
+
connection.execute(
|
424
|
+
self.tbl_iris_listbuild.insert(),
|
425
|
+
[
|
426
|
+
{"data": test_list1},
|
427
|
+
{"data": test_list2},
|
428
|
+
],
|
429
|
+
)
|
430
|
+
result = connection.execute(self.tbl_iris_listbuild.select()).fetchall()
|
431
|
+
assert len(result) == 2
|
432
|
+
# Check data values regardless of ID values
|
433
|
+
data_values = [row[1] for row in result]
|
434
|
+
assert test_list1 in data_values
|
435
|
+
assert test_list2 in data_values
|
436
|
+
|
437
|
+
def test_iris_vector(self, connection, tables):
|
438
|
+
test_vector1 = [1.0, 2.0, 3.0]
|
439
|
+
test_vector2 = [4.0, 5.0, 6.0]
|
440
|
+
|
441
|
+
connection.execute(
|
442
|
+
self.tbl_iris_vector.insert(),
|
443
|
+
[
|
444
|
+
{"data": test_vector1},
|
445
|
+
{"data": test_vector2},
|
446
|
+
],
|
447
|
+
)
|
448
|
+
result = connection.execute(self.tbl_iris_vector.select()).fetchall()
|
449
|
+
assert len(result) == 2
|
450
|
+
# Check data values regardless of ID values
|
451
|
+
data_values = [row[1] for row in result]
|
452
|
+
assert test_vector1 in data_values
|
453
|
+
assert test_vector2 in data_values
|
454
|
+
|
455
|
+
def test_iris_uuid(self, connection, tables):
|
456
|
+
if not HAS_IRIS_UUID:
|
457
|
+
# Skip test if IRISUniqueIdentifier is not available (SQLAlchemy < 2.x)
|
458
|
+
return
|
459
|
+
|
460
|
+
# Skip this test for now as IRISUniqueIdentifier has specific requirements
|
461
|
+
# that need further investigation
|
462
|
+
pass
|