Skip to content

Commit 56dfa4d

Browse files
iproto: fix schema with constraints fetch
Before this patch, only schemas with 3-level nesting were expected. Simple foreign keys schema has 4-level nesting. After this patch, nesting depth up to 32 is supported. (There are no known schemas with such nesting, but this should be enough for any future extensions.) Closes #282
1 parent 0622297 commit 56dfa4d

File tree

4 files changed

+77
-2
lines changed

4 files changed

+77
-2
lines changed

Diff for: CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99
### Changed
1010
- Discovery iproto features only for Tarantools since version 2.10.0 (#283).
1111

12+
### Fixed
13+
- Schema fetch for spaces with foreign keys (#282).
14+
1215
## 0.12.0 - 2023-02-13
1316

1417
### Added

Diff for: tarantool/schema.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@
1212
import tarantool.const as const
1313

1414

15+
"""
16+
Max possible known schema depth is 4 if foreign keys are used (since
17+
Tarantool 2.10), but there are no restrictions in protocol.
18+
"""
19+
MAX_RECURSION_DEPTH = 32
20+
21+
1522
class RecursionError(Error):
1623
"""
1724
Report the situation when max recursion depth is reached.
@@ -102,7 +109,7 @@ def __init__(self, index_row, space):
102109
self.unique = index_row[4]
103110
self.parts = []
104111
try:
105-
parts_raw = to_unicode_recursive(index_row[5], 3)
112+
parts_raw = to_unicode_recursive(index_row[5], MAX_RECURSION_DEPTH)
106113
except RecursionError as e:
107114
errmsg = 'Unexpected index parts structure: ' + str(e)
108115
raise SchemaError(errmsg)
@@ -159,7 +166,7 @@ def __init__(self, space_row, schema):
159166
self.schema[self.name] = self
160167
self.format = dict()
161168
try:
162-
format_raw = to_unicode_recursive(space_row[6], 3)
169+
format_raw = to_unicode_recursive(space_row[6], MAX_RECURSION_DEPTH)
163170
except RecursionError as e:
164171
errmsg = 'Unexpected space format structure: ' + str(e)
165172
raise SchemaError(errmsg)

Diff for: test/suites/lib/skip.py

+11
Original file line numberDiff line numberDiff line change
@@ -236,3 +236,14 @@ def skip_or_run_auth_type_test_call(self):
236236

237237
return skip_or_run_test_tarantool_call(self, '2.11.0',
238238
'does not support auth type')
239+
240+
def skip_or_run_constraints_test(func):
241+
"""Decorator to skip or run tests related to spaces with
242+
schema constraints.
243+
244+
Tarantool supports schema constraints only since 2.10.0 version.
245+
See https://github.com/tarantool/tarantool/issues/6436
246+
"""
247+
248+
return skip_or_run_test_tarantool(func, '2.10.0',
249+
'does not support schema constraints')

Diff for: test/suites/test_schema.py

+54
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import sys
22
import unittest
33
import tarantool
4+
import pkg_resources
5+
46
from .lib.tarantool_server import TarantoolServer
7+
from .lib.skip import skip_or_run_constraints_test
58
from tarantool.error import NotSupportedError
69

710

@@ -102,6 +105,33 @@ def setUpClass(self):
102105
end
103106
""")
104107

108+
if self.srv.admin.tnt_version >= pkg_resources.parse_version('2.10.0'):
109+
self.srv.admin("""
110+
box.schema.create_space(
111+
'constr_tester_1', {
112+
format = {
113+
{ name = 'id', type = 'unsigned' },
114+
{ name = 'payload', type = 'number' },
115+
}
116+
})
117+
box.space.constr_tester_1:create_index('I1', { parts = {'id'} })
118+
119+
box.space.constr_tester_1:replace({1, 999})
120+
121+
box.schema.create_space(
122+
'constr_tester_2', {
123+
format = {
124+
{ name = 'id', type = 'unsigned' },
125+
{ name = 'table1_id', type = 'unsigned',
126+
foreign_key = { fk_video = { space = 'constr_tester_1', field = 'id' } },
127+
},
128+
{ name = 'payload', type = 'number' },
129+
}
130+
})
131+
box.space.constr_tester_2:create_index('I1', { parts = {'id'} })
132+
box.space.constr_tester_2:create_index('I2', { parts = {'table1_id'} })
133+
""")
134+
105135
def setUp(self):
106136
# prevent a remote tarantool from clean our session
107137
if self.srv.is_started():
@@ -541,8 +571,32 @@ def test_08_schema_fetch_disable_via_connection_pool(self):
541571
self._run_test_schema_fetch_disable(self.pool_con_schema_disable,
542572
mode=tarantool.Mode.ANY)
543573

574+
@skip_or_run_constraints_test
575+
def test_09_foreign_key_info_fetched_to_schema(self):
576+
self.assertIn('foreign_key', self.sch.get_space('constr_tester_2').format['table1_id'])
577+
578+
@skip_or_run_constraints_test
579+
def test_10_foreign_key_valid_replace(self):
580+
self.assertSequenceEqual(
581+
self.con.replace('constr_tester_2', [1, 1, 623]),
582+
[[1, 1, 623]])
583+
584+
@skip_or_run_constraints_test
585+
def test_11_foreign_key_invalid_replace(self):
586+
with self.assertRaisesRegex(tarantool.DatabaseError,
587+
'foreign tuple was not found'):
588+
self.con.replace('constr_tester_2', [2, 999, 623])
589+
544590
@classmethod
545591
def tearDownClass(self):
592+
# We need to drop spaces with foreign keys with predetermined order,
593+
# otherwise remote server clean() will fail to clean up resources.
594+
if self.srv.admin.tnt_version >= pkg_resources.parse_version('2.10.0'):
595+
self.srv.admin("""
596+
box.space.constr_tester_2:drop()
597+
box.space.constr_tester_1:drop()
598+
""")
599+
546600
self.con.close()
547601
self.con_schema_disable.close()
548602
if not sys.platform.startswith("win"):

0 commit comments

Comments
 (0)