velocity-python 0.0.132__py3-none-any.whl → 0.0.134__py3-none-any.whl
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.
Potentially problematic release.
This version of velocity-python might be problematic. Click here for more details.
- velocity/__init__.py +1 -1
- velocity/app/tests/__init__.py +1 -0
- velocity/app/tests/test_email_processing.py +112 -0
- velocity/app/tests/test_payment_profile_sorting.py +191 -0
- velocity/app/tests/test_spreadsheet_functions.py +124 -0
- velocity/aws/tests/__init__.py +1 -0
- velocity/aws/tests/test_lambda_handler_json_serialization.py +120 -0
- velocity/aws/tests/test_response.py +163 -0
- velocity/db/core/decorators.py +20 -3
- velocity/db/core/engine.py +33 -7
- velocity/db/exceptions.py +7 -0
- velocity/db/servers/base/initializer.py +2 -1
- velocity/db/servers/mysql/__init__.py +13 -4
- velocity/db/servers/postgres/__init__.py +14 -4
- velocity/db/servers/sqlite/__init__.py +13 -4
- velocity/db/servers/sqlserver/__init__.py +13 -4
- velocity/db/tests/__init__.py +1 -0
- velocity/db/tests/common_db_test.py +0 -0
- velocity/db/tests/postgres/__init__.py +1 -0
- velocity/db/tests/postgres/common.py +49 -0
- velocity/db/tests/postgres/test_column.py +29 -0
- velocity/db/tests/postgres/test_connections.py +25 -0
- velocity/db/tests/postgres/test_database.py +21 -0
- velocity/db/tests/postgres/test_engine.py +205 -0
- velocity/db/tests/postgres/test_general_usage.py +88 -0
- velocity/db/tests/postgres/test_imports.py +8 -0
- velocity/db/tests/postgres/test_result.py +19 -0
- velocity/db/tests/postgres/test_row.py +137 -0
- velocity/db/tests/postgres/test_schema_locking.py +335 -0
- velocity/db/tests/postgres/test_schema_locking_unit.py +115 -0
- velocity/db/tests/postgres/test_sequence.py +34 -0
- velocity/db/tests/postgres/test_table.py +101 -0
- velocity/db/tests/postgres/test_transaction.py +106 -0
- velocity/db/tests/sql/__init__.py +1 -0
- velocity/db/tests/sql/common.py +177 -0
- velocity/db/tests/sql/test_postgres_select_advanced.py +285 -0
- velocity/db/tests/sql/test_postgres_select_variances.py +517 -0
- velocity/db/tests/test_cursor_rowcount_fix.py +150 -0
- velocity/db/tests/test_db_utils.py +221 -0
- velocity/db/tests/test_postgres.py +212 -0
- velocity/db/tests/test_postgres_unchanged.py +81 -0
- velocity/db/tests/test_process_error_robustness.py +292 -0
- velocity/db/tests/test_result_caching.py +279 -0
- velocity/db/tests/test_result_sql_aware.py +117 -0
- velocity/db/tests/test_row_get_missing_column.py +72 -0
- velocity/db/tests/test_schema_locking_initializers.py +226 -0
- velocity/db/tests/test_schema_locking_simple.py +97 -0
- velocity/db/tests/test_sql_builder.py +165 -0
- velocity/db/tests/test_tablehelper.py +486 -0
- velocity/misc/tests/__init__.py +1 -0
- velocity/misc/tests/test_db.py +90 -0
- velocity/misc/tests/test_fix.py +78 -0
- velocity/misc/tests/test_format.py +64 -0
- velocity/misc/tests/test_iconv.py +203 -0
- velocity/misc/tests/test_merge.py +82 -0
- velocity/misc/tests/test_oconv.py +144 -0
- velocity/misc/tests/test_original_error.py +52 -0
- velocity/misc/tests/test_timer.py +74 -0
- {velocity_python-0.0.132.dist-info → velocity_python-0.0.134.dist-info}/METADATA +1 -1
- velocity_python-0.0.134.dist-info/RECORD +125 -0
- velocity_python-0.0.132.dist-info/RECORD +0 -76
- {velocity_python-0.0.132.dist-info → velocity_python-0.0.134.dist-info}/WHEEL +0 -0
- {velocity_python-0.0.132.dist-info → velocity_python-0.0.134.dist-info}/licenses/LICENSE +0 -0
- {velocity_python-0.0.132.dist-info → velocity_python-0.0.134.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from velocity.db.servers import postgres
|
|
3
|
+
import random
|
|
4
|
+
|
|
5
|
+
test_db = "test_foreign_key_db"
|
|
6
|
+
engine = postgres.initialize(
|
|
7
|
+
database=test_db,
|
|
8
|
+
)
|
|
9
|
+
print(engine)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@engine.transaction # Decorator to run the test in a transaction
|
|
13
|
+
class TestSQLModule(unittest.TestCase):
|
|
14
|
+
|
|
15
|
+
@classmethod
|
|
16
|
+
def setUpClass(cls, tx):
|
|
17
|
+
tx.switch_to_database("postgres")
|
|
18
|
+
tx.execute(f"drop database if exists {test_db}", single=True)
|
|
19
|
+
# Drop and recreate the test database to ensure a clean environment
|
|
20
|
+
db = tx.database(test_db)
|
|
21
|
+
if db.exists():
|
|
22
|
+
# Possibly drop if needed, or just recreate if the environment ensures cleanliness
|
|
23
|
+
pass
|
|
24
|
+
else:
|
|
25
|
+
db.create()
|
|
26
|
+
db.switch()
|
|
27
|
+
|
|
28
|
+
cls.create_tables(tx)
|
|
29
|
+
cls.insert_data(tx)
|
|
30
|
+
|
|
31
|
+
@classmethod
|
|
32
|
+
def create_tables(cls, tx):
|
|
33
|
+
# Table with normal columns
|
|
34
|
+
tx.table("normal_table").create(
|
|
35
|
+
columns={
|
|
36
|
+
"name": str,
|
|
37
|
+
"active": bool,
|
|
38
|
+
"value": float,
|
|
39
|
+
}
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Table with "aggregate-like" column names
|
|
43
|
+
# and a reserved keyword as a column name (e.g. "order")
|
|
44
|
+
tx.table("weird_names_table").create(
|
|
45
|
+
columns={
|
|
46
|
+
"Sum_info": str, # Looks like SUM but isn't
|
|
47
|
+
"MAX_hours": int, # Starts with MAX but not necessarily aggregate
|
|
48
|
+
"order": str, # reserved keyword in SQL, test quoting
|
|
49
|
+
}
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Parent and child tables to test foreign keys and multiple pointers
|
|
53
|
+
tx.table("fk_parent").create(
|
|
54
|
+
columns={
|
|
55
|
+
"parent_name": str,
|
|
56
|
+
"num_things": int,
|
|
57
|
+
"is_valid": bool,
|
|
58
|
+
}
|
|
59
|
+
)
|
|
60
|
+
tx.table("fk_middle").create(
|
|
61
|
+
columns={
|
|
62
|
+
"parent_id": int,
|
|
63
|
+
"title": str,
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
tx.table("fk_middle").create_foreign_key("parent_id", "fk_parent", "sys_id")
|
|
67
|
+
|
|
68
|
+
tx.table("fk_child").create(
|
|
69
|
+
columns={"parent_id": int, "middle_id": int, "description": str}
|
|
70
|
+
)
|
|
71
|
+
tx.table("fk_child").create_foreign_key("parent_id", "fk_parent", "sys_id")
|
|
72
|
+
tx.table("fk_child").create_foreign_key("middle_id", "fk_middle", "sys_id")
|
|
73
|
+
|
|
74
|
+
# Another table for testing multiple foreign references to the same table
|
|
75
|
+
tx.table("fk_self_ref").create(
|
|
76
|
+
columns={
|
|
77
|
+
"ref_id": int,
|
|
78
|
+
"info": str,
|
|
79
|
+
}
|
|
80
|
+
)
|
|
81
|
+
# A self referencing foreign key (if supported by environment)
|
|
82
|
+
tx.table("fk_self_ref").create_foreign_key("ref_id", "fk_self_ref", "sys_id")
|
|
83
|
+
|
|
84
|
+
# Table to test special operators and placeholders
|
|
85
|
+
tx.table("special_values_table").create(
|
|
86
|
+
columns={
|
|
87
|
+
"name": str,
|
|
88
|
+
"status": str,
|
|
89
|
+
"score": float,
|
|
90
|
+
}
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
@classmethod
|
|
94
|
+
def insert_data(cls, tx):
|
|
95
|
+
normal_table = tx.table("normal_table")
|
|
96
|
+
normal_table.upsert(
|
|
97
|
+
{"sys_id": 1, "name": "Alpha", "active": True, "value": 10.5}
|
|
98
|
+
)
|
|
99
|
+
normal_table.upsert(
|
|
100
|
+
{"sys_id": 2, "name": "Beta", "active": False, "value": None}
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
weird = tx.table("weird_names_table")
|
|
104
|
+
weird.upsert(
|
|
105
|
+
{
|
|
106
|
+
"sys_id": 10,
|
|
107
|
+
"Sum_info": "Not an Aggregate",
|
|
108
|
+
"MAX_hours": 40,
|
|
109
|
+
"order": "first",
|
|
110
|
+
}
|
|
111
|
+
)
|
|
112
|
+
weird.upsert(
|
|
113
|
+
{"sys_id": 20, "Sum_info": "Also Not", "MAX_hours": 50, "order": "second"}
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
parent = tx.table("fk_parent")
|
|
117
|
+
parent.upsert({"sys_id": 100, "parent_name": "P1"})
|
|
118
|
+
parent.upsert({"sys_id": 200, "parent_name": "P2"})
|
|
119
|
+
|
|
120
|
+
middle = tx.table("fk_middle")
|
|
121
|
+
middle.upsert({"sys_id": 300, "parent_id": 100, "title": "M1"})
|
|
122
|
+
middle.upsert({"sys_id": 400, "parent_id": 200, "title": "M2"})
|
|
123
|
+
|
|
124
|
+
child = tx.table("fk_child")
|
|
125
|
+
child.upsert(
|
|
126
|
+
{"sys_id": 500, "parent_id": 100, "middle_id": 300, "description": "C1"}
|
|
127
|
+
)
|
|
128
|
+
child.upsert(
|
|
129
|
+
{"sys_id": 600, "parent_id": 200, "middle_id": 400, "description": "C2"}
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
self_ref = tx.table("fk_self_ref")
|
|
133
|
+
self_ref.upsert({"sys_id": 700, "ref_id": None, "info": "root"})
|
|
134
|
+
self_ref.upsert({"sys_id": 800, "ref_id": 700, "info": "child_of_700"})
|
|
135
|
+
self_ref.upsert({"sys_id": 900, "ref_id": 800, "info": "child_of_800"})
|
|
136
|
+
|
|
137
|
+
special = tx.table("special_values_table")
|
|
138
|
+
special.upsert(
|
|
139
|
+
{
|
|
140
|
+
"sys_id": 1000,
|
|
141
|
+
"name": "Infinite Score",
|
|
142
|
+
"status": "open",
|
|
143
|
+
"score": 9999.99,
|
|
144
|
+
}
|
|
145
|
+
)
|
|
146
|
+
special.upsert(
|
|
147
|
+
{"sys_id": 1100, "name": "Unknown Value", "status": None, "score": None}
|
|
148
|
+
)
|
|
149
|
+
special.upsert(
|
|
150
|
+
{"sys_id": 1200, "name": "Regular Entry", "status": "closed", "score": 100}
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
emails = [
|
|
154
|
+
"test.user1@example.com",
|
|
155
|
+
"demo.account@domain.com",
|
|
156
|
+
"sample.email@test.com",
|
|
157
|
+
"mock.data@company.com",
|
|
158
|
+
"placeholder@example.com",
|
|
159
|
+
"testcase@domain.com",
|
|
160
|
+
"fakeuser@test.com",
|
|
161
|
+
"demodata@company.com",
|
|
162
|
+
]
|
|
163
|
+
for i in range(10):
|
|
164
|
+
special.upsert(
|
|
165
|
+
{
|
|
166
|
+
"sys_id": 1300 + i,
|
|
167
|
+
"name": f"Entry {i}",
|
|
168
|
+
"status": "open",
|
|
169
|
+
"score": i * 10,
|
|
170
|
+
"email": random.choice(emails),
|
|
171
|
+
}
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
# @classmethod
|
|
175
|
+
# def tearDownClass(cls, tx):
|
|
176
|
+
# tx.switch_to_database("postgres")
|
|
177
|
+
# tx.execute(f"drop database if exists {test_db}", single=True)
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
import velocity.db.exceptions
|
|
3
|
+
from common import TestSQLModule, engine
|
|
4
|
+
import time
|
|
5
|
+
|
|
6
|
+
DO_ALL = True
|
|
7
|
+
|
|
8
|
+
print(f"DO_ALL: {DO_ALL}")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@engine.transaction
|
|
12
|
+
class TestLocal(TestSQLModule):
|
|
13
|
+
|
|
14
|
+
def test_select_self_refer_wildcard(self, tx):
|
|
15
|
+
if not DO_ALL:
|
|
16
|
+
return
|
|
17
|
+
|
|
18
|
+
sql, vars = tx.table("fk_child").select(
|
|
19
|
+
sql_only=True, columns=["A.*", "parent_id>parent_name"]
|
|
20
|
+
)
|
|
21
|
+
expected_sql = """
|
|
22
|
+
SELECT A.*,
|
|
23
|
+
B.parent_name AS parent_id_parent_name
|
|
24
|
+
FROM fk_child AS A
|
|
25
|
+
LEFT JOIN fk_parent AS B ON A.parent_id = B.sys_id
|
|
26
|
+
"""
|
|
27
|
+
expected_vars = ()
|
|
28
|
+
self.assertEqual(
|
|
29
|
+
sql.split(),
|
|
30
|
+
expected_sql.split(),
|
|
31
|
+
)
|
|
32
|
+
self.assertEqual(vars, expected_vars)
|
|
33
|
+
|
|
34
|
+
# for row in tx.table("fk_child").select(
|
|
35
|
+
# columns=["A.*", "parent_id>parent_name"]
|
|
36
|
+
# ):
|
|
37
|
+
# print(row)
|
|
38
|
+
|
|
39
|
+
sql, vars = tx.table("fk_self_ref").select(
|
|
40
|
+
sql_only=True, columns=["A.*", "ref_id>info"]
|
|
41
|
+
)
|
|
42
|
+
expected_sql = """
|
|
43
|
+
SELECT A.*,
|
|
44
|
+
B.info AS ref_id_info
|
|
45
|
+
FROM fk_self_ref AS A
|
|
46
|
+
LEFT JOIN fk_self_ref AS B ON A.ref_id = B.sys_id
|
|
47
|
+
"""
|
|
48
|
+
expected_vars = ()
|
|
49
|
+
self.assertEqual(
|
|
50
|
+
sql.split(),
|
|
51
|
+
expected_sql.split(),
|
|
52
|
+
)
|
|
53
|
+
self.assertEqual(vars, expected_vars)
|
|
54
|
+
# for row in tx.table("fk_self_ref").select(columns=["A.*", "ref_id>info"]):
|
|
55
|
+
# print(row)
|
|
56
|
+
|
|
57
|
+
sql, vars = tx.table("fk_self_ref").select(
|
|
58
|
+
sql_only=True, columns=["A.*", "ref_id>info", "ref_id>ref_id"]
|
|
59
|
+
)
|
|
60
|
+
expected_sql = """
|
|
61
|
+
SELECT A.*,
|
|
62
|
+
B.info AS ref_id_info,
|
|
63
|
+
B.ref_id AS ref_id_ref_id
|
|
64
|
+
FROM fk_self_ref AS A
|
|
65
|
+
LEFT JOIN fk_self_ref AS B ON A.ref_id = B.sys_id
|
|
66
|
+
"""
|
|
67
|
+
expected_vars = ()
|
|
68
|
+
self.assertEqual(
|
|
69
|
+
sql.split(),
|
|
70
|
+
expected_sql.split(),
|
|
71
|
+
)
|
|
72
|
+
self.assertEqual(vars, expected_vars)
|
|
73
|
+
# for row in tx.table("fk_self_ref").select(
|
|
74
|
+
# columns=["A.*", "ref_id>info", "ref_id>ref_id"]
|
|
75
|
+
# ):
|
|
76
|
+
# print(row)
|
|
77
|
+
|
|
78
|
+
def test_complex_update(self, tx):
|
|
79
|
+
if not DO_ALL:
|
|
80
|
+
return
|
|
81
|
+
sql, vars = tx.table("fk_child").update(
|
|
82
|
+
sql_only=True,
|
|
83
|
+
data={
|
|
84
|
+
"name": "@parent_id>parent_name",
|
|
85
|
+
"value": "@parent_id>num_things",
|
|
86
|
+
"active": "@parent_id>is_valid",
|
|
87
|
+
},
|
|
88
|
+
where={
|
|
89
|
+
">parent_id>num_things": "10",
|
|
90
|
+
"<num_things": "10",
|
|
91
|
+
},
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
expected_sql = """
|
|
95
|
+
UPDATE fk_child AS A
|
|
96
|
+
SET name = %s,
|
|
97
|
+
"value" = %s,
|
|
98
|
+
active = %s
|
|
99
|
+
LEFT JOIN fk_parent AS B
|
|
100
|
+
WHERE B.num_things > %s
|
|
101
|
+
AND num_things < %s
|
|
102
|
+
AND A.parent_id = B.sys_id
|
|
103
|
+
"""
|
|
104
|
+
expected_vars = (
|
|
105
|
+
"@parent_id>parent_name",
|
|
106
|
+
"@parent_id>num_things",
|
|
107
|
+
"@parent_id>is_valid",
|
|
108
|
+
"10",
|
|
109
|
+
"10",
|
|
110
|
+
)
|
|
111
|
+
self.assertEqual(
|
|
112
|
+
sql.split(),
|
|
113
|
+
expected_sql.split(),
|
|
114
|
+
)
|
|
115
|
+
self.assertEqual(vars, expected_vars)
|
|
116
|
+
|
|
117
|
+
sql, vars = tx.table("fk_child").update(
|
|
118
|
+
sql_only=True,
|
|
119
|
+
data={
|
|
120
|
+
"a": "a",
|
|
121
|
+
"b": "a",
|
|
122
|
+
"c": "a",
|
|
123
|
+
"d": "a",
|
|
124
|
+
"e": "a",
|
|
125
|
+
},
|
|
126
|
+
where={
|
|
127
|
+
"sys_id": "700",
|
|
128
|
+
},
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
expected_sql = """
|
|
132
|
+
UPDATE fk_child
|
|
133
|
+
SET a = %s,
|
|
134
|
+
b = %s,
|
|
135
|
+
c = %s,
|
|
136
|
+
d = %s,
|
|
137
|
+
e = %s
|
|
138
|
+
WHERE sys_id = %s
|
|
139
|
+
"""
|
|
140
|
+
expected_vars = ("a", "a", "a", "a", "a", "700")
|
|
141
|
+
self.assertEqual(
|
|
142
|
+
sql.split(),
|
|
143
|
+
expected_sql.split(),
|
|
144
|
+
)
|
|
145
|
+
self.assertEqual(vars, expected_vars)
|
|
146
|
+
|
|
147
|
+
def test_complex_merge(self, tx):
|
|
148
|
+
if not DO_ALL:
|
|
149
|
+
return
|
|
150
|
+
for i in range(10):
|
|
151
|
+
tx.table("fk_you").insert(
|
|
152
|
+
data={
|
|
153
|
+
"sys_id": i + 1000,
|
|
154
|
+
"parent_id": 1,
|
|
155
|
+
"name": f"Child {i}",
|
|
156
|
+
"value": i,
|
|
157
|
+
"active": True,
|
|
158
|
+
},
|
|
159
|
+
)
|
|
160
|
+
self.assertRaises(
|
|
161
|
+
velocity.db.exceptions.DbDuplicateKeyError,
|
|
162
|
+
tx.table("fk_you").insert,
|
|
163
|
+
data={
|
|
164
|
+
"sys_id": i + 1000,
|
|
165
|
+
"parent_id": 2,
|
|
166
|
+
"name": f"Child {i}-2",
|
|
167
|
+
"value": i,
|
|
168
|
+
"active": False,
|
|
169
|
+
},
|
|
170
|
+
)
|
|
171
|
+
for i in range(10):
|
|
172
|
+
tx.table("fk_you").insert(
|
|
173
|
+
data={
|
|
174
|
+
"parent_id": 3,
|
|
175
|
+
"name": f"Child {i}-3",
|
|
176
|
+
"value": i,
|
|
177
|
+
"active": True,
|
|
178
|
+
},
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
for i in range(10):
|
|
182
|
+
tx.table("fk_you").merge(
|
|
183
|
+
data={
|
|
184
|
+
"sys_id": i + 1000,
|
|
185
|
+
"parent_id": 4,
|
|
186
|
+
"name": f"Child {i}4",
|
|
187
|
+
"value": i,
|
|
188
|
+
"active": False,
|
|
189
|
+
},
|
|
190
|
+
)
|
|
191
|
+
for i in range(10):
|
|
192
|
+
tx.table("fk_you").merge(
|
|
193
|
+
data={
|
|
194
|
+
"sys_id": i + 1100,
|
|
195
|
+
"parent_id": 4,
|
|
196
|
+
"name": f"Child {i}4",
|
|
197
|
+
"value": i,
|
|
198
|
+
"active": False,
|
|
199
|
+
},
|
|
200
|
+
)
|
|
201
|
+
tx.commit()
|
|
202
|
+
|
|
203
|
+
def test_duplicate_rows(self, tx):
|
|
204
|
+
sql, vars = tx.table("special_values_table").duplicate_rows(
|
|
205
|
+
sql_only=True,
|
|
206
|
+
columns=["email", "status"],
|
|
207
|
+
)
|
|
208
|
+
expected_sql = """
|
|
209
|
+
SELECT t.*
|
|
210
|
+
FROM special_values_table t
|
|
211
|
+
JOIN (SELECT email,
|
|
212
|
+
status
|
|
213
|
+
FROM special_values_table
|
|
214
|
+
GROUP BY email,
|
|
215
|
+
status
|
|
216
|
+
HAVING count(*) > %s) dup
|
|
217
|
+
ON t.email = dup.email AND t.status = dup.status
|
|
218
|
+
ORDER BY email, status
|
|
219
|
+
"""
|
|
220
|
+
expected_vars = (1,)
|
|
221
|
+
|
|
222
|
+
self.assertEqual(
|
|
223
|
+
sql.split(),
|
|
224
|
+
expected_sql.split(),
|
|
225
|
+
)
|
|
226
|
+
self.assertEqual(vars, expected_vars)
|
|
227
|
+
|
|
228
|
+
# for row in tx.table("special_values_table").duplicate_rows(
|
|
229
|
+
# columns=["email", "status"],
|
|
230
|
+
# ):
|
|
231
|
+
# print(row)
|
|
232
|
+
|
|
233
|
+
sql, vars = tx.table("special_values_table").has_duplicates(
|
|
234
|
+
sql_only=True,
|
|
235
|
+
columns=["email", "status"],
|
|
236
|
+
)
|
|
237
|
+
expected_sql = """
|
|
238
|
+
SELECT 1
|
|
239
|
+
FROM special_values_table
|
|
240
|
+
GROUP BY email,
|
|
241
|
+
status
|
|
242
|
+
HAVING count(*) > %s FETCH NEXT 1 ROWS ONLY
|
|
243
|
+
"""
|
|
244
|
+
expected_vars = (1,)
|
|
245
|
+
self.assertEqual(
|
|
246
|
+
sql.split(),
|
|
247
|
+
expected_sql.split(),
|
|
248
|
+
)
|
|
249
|
+
self.assertEqual(vars, expected_vars)
|
|
250
|
+
|
|
251
|
+
self.assertEqual(
|
|
252
|
+
tx.table("special_values_table").has_duplicates(
|
|
253
|
+
columns=["email", "status"],
|
|
254
|
+
),
|
|
255
|
+
True,
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
expected_sql = """
|
|
259
|
+
SELECT 1
|
|
260
|
+
FROM special_values_table
|
|
261
|
+
GROUP BY email,
|
|
262
|
+
status
|
|
263
|
+
HAVING count(*) > %s FETCH NEXT 1 ROWS ONLY
|
|
264
|
+
"""
|
|
265
|
+
expected_vars = (1,)
|
|
266
|
+
|
|
267
|
+
self.assertEqual(
|
|
268
|
+
sql.split(),
|
|
269
|
+
expected_sql.split(),
|
|
270
|
+
)
|
|
271
|
+
self.assertEqual(vars, expected_vars)
|
|
272
|
+
self.assertEqual(
|
|
273
|
+
tx.table("special_values_table").has_duplicates(
|
|
274
|
+
columns=["sys_id", "status"],
|
|
275
|
+
),
|
|
276
|
+
False,
|
|
277
|
+
)
|
|
278
|
+
for row in tx.table("special_values_table").duplicate_rows(
|
|
279
|
+
columns=["sys_id", "status"],
|
|
280
|
+
):
|
|
281
|
+
print(row)
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
if __name__ == "__main__":
|
|
285
|
+
unittest.main()
|