0
# Schema Operations
1
2
Database schema management including directory operations, table schema inspection, permissions management, and metadata operations.
3
4
## Capabilities
5
6
### Schema Client
7
8
The schema client provides operations for managing database structure, directories, and metadata.
9
10
```python { .api }
11
class SchemeClient:
12
def __init__(self, driver: Driver):
13
"""
14
Create schema operations client.
15
16
Args:
17
driver (Driver): YDB driver instance
18
"""
19
20
def make_directory(
21
self,
22
path: str,
23
settings: MakeDirectorySettings = None
24
):
25
"""
26
Create directory in the database.
27
28
Args:
29
path (str): Directory path to create
30
settings (MakeDirectorySettings, optional): Directory creation settings
31
"""
32
33
def remove_directory(
34
self,
35
path: str,
36
settings: RemoveDirectorySettings = None
37
):
38
"""
39
Remove directory from the database.
40
41
Args:
42
path (str): Directory path to remove
43
settings (RemoveDirectorySettings, optional): Directory removal settings
44
"""
45
46
def list_directory(
47
self,
48
path: str,
49
settings: ListDirectorySettings = None
50
) -> Directory:
51
"""
52
List contents of a directory.
53
54
Args:
55
path (str): Directory path to list
56
settings (ListDirectorySettings, optional): Listing settings
57
58
Returns:
59
Directory: Directory information with child entries
60
"""
61
62
def describe_path(
63
self,
64
path: str,
65
settings: DescribePathSettings = None
66
) -> SchemeEntry:
67
"""
68
Get detailed information about a path entry.
69
70
Args:
71
path (str): Path to describe
72
settings (DescribePathSettings, optional): Description settings
73
74
Returns:
75
SchemeEntry: Path entry information
76
"""
77
78
def modify_permissions(
79
self,
80
path: str,
81
permissions: Permissions,
82
settings: ModifyPermissionsSettings = None
83
):
84
"""
85
Modify access permissions for a path.
86
87
Args:
88
path (str): Path to modify permissions for
89
permissions (Permissions): Permission modifications
90
settings (ModifyPermissionsSettings, optional): Permission settings
91
"""
92
93
class MakeDirectorySettings(BaseRequestSettings):
94
def __init__(
95
self,
96
request_type: str = None,
97
trace_id: str = None,
98
timeout: float = None
99
):
100
"""
101
Settings for directory creation operations.
102
103
Args:
104
request_type (str, optional): Request type for logging
105
trace_id (str, optional): Request tracing identifier
106
timeout (float, optional): Operation timeout
107
"""
108
109
class RemoveDirectorySettings(BaseRequestSettings):
110
def __init__(
111
self,
112
request_type: str = None,
113
trace_id: str = None,
114
timeout: float = None
115
):
116
"""
117
Settings for directory removal operations.
118
119
Args:
120
request_type (str, optional): Request type for logging
121
trace_id (str, optional): Request tracing identifier
122
timeout (float, optional): Operation timeout
123
"""
124
125
class ListDirectorySettings(BaseRequestSettings):
126
def __init__(
127
self,
128
request_type: str = None,
129
trace_id: str = None,
130
timeout: float = None
131
):
132
"""
133
Settings for directory listing operations.
134
135
Args:
136
request_type (str, optional): Request type for logging
137
trace_id (str, optional): Request tracing identifier
138
timeout (float, optional): Operation timeout
139
"""
140
141
class DescribePathSettings(BaseRequestSettings):
142
def __init__(
143
self,
144
request_type: str = None,
145
trace_id: str = None,
146
timeout: float = None
147
):
148
"""
149
Settings for path description operations.
150
151
Args:
152
request_type (str, optional): Request type for logging
153
trace_id (str, optional): Request tracing identifier
154
timeout (float, optional): Operation timeout
155
"""
156
```
157
158
### Schema Entry Types
159
160
Enumeration of different entry types in the database schema.
161
162
```python { .api }
163
class SchemeEntryType(enum.IntEnum):
164
"""
165
Database entry types enumeration.
166
"""
167
TYPE_UNSPECIFIED = 0
168
DIRECTORY = 1
169
TABLE = 2
170
PERS_QUEUE_GROUP = 3
171
DATABASE = 4
172
RTMR_VOLUME = 5
173
BLOCK_STORE_VOLUME = 6
174
COORDINATION_NODE = 7
175
COLUMN_STORE = 12
176
COLUMN_TABLE = 13
177
SEQUENCE = 15
178
REPLICATION = 16
179
TOPIC = 17
180
EXTERNAL_TABLE = 18
181
EXTERNAL_DATA_SOURCE = 19
182
VIEW = 20
183
RESOURCE_POOL = 21
184
185
@staticmethod
186
def is_table(entry: 'SchemeEntryType') -> bool:
187
"""
188
Check if entry is a row table.
189
190
Args:
191
entry (SchemeEntryType): Entry type to check
192
193
Returns:
194
bool: True if entry is a row table
195
"""
196
197
@staticmethod
198
def is_any_table(entry: 'SchemeEntryType') -> bool:
199
"""
200
Check if entry is any type of table.
201
202
Args:
203
entry (SchemeEntryType): Entry type to check
204
205
Returns:
206
bool: True if entry is any table type
207
"""
208
209
@staticmethod
210
def is_column_table(entry: 'SchemeEntryType') -> bool:
211
"""
212
Check if entry is a column table.
213
214
Args:
215
entry (SchemeEntryType): Entry type to check
216
217
Returns:
218
bool: True if entry is a column table
219
"""
220
221
@staticmethod
222
def is_directory(entry: 'SchemeEntryType') -> bool:
223
"""
224
Check if entry is a directory.
225
226
Args:
227
entry (SchemeEntryType): Entry type to check
228
229
Returns:
230
bool: True if entry is a directory
231
"""
232
233
@staticmethod
234
def is_topic(entry: 'SchemeEntryType') -> bool:
235
"""
236
Check if entry is a topic.
237
238
Args:
239
entry (SchemeEntryType): Entry type to check
240
241
Returns:
242
bool: True if entry is a topic
243
"""
244
```
245
246
### Schema Entries
247
248
Data structures representing database schema entries and their metadata.
249
250
```python { .api }
251
class SchemeEntry:
252
def __init__(
253
self,
254
name: str,
255
owner: str,
256
type: SchemeEntryType,
257
effective_permissions: List[str] = None,
258
permissions: List[Permissions] = None,
259
size_bytes: int = None
260
):
261
"""
262
Schema entry information.
263
264
Args:
265
name (str): Entry name
266
owner (str): Entry owner
267
type (SchemeEntryType): Entry type
268
effective_permissions (List[str], optional): Effective permissions
269
permissions (List[Permissions], optional): Permission settings
270
size_bytes (int, optional): Entry size in bytes
271
"""
272
273
@property
274
def name(self) -> str:
275
"""Entry name."""
276
277
@property
278
def owner(self) -> str:
279
"""Entry owner."""
280
281
@property
282
def type(self) -> SchemeEntryType:
283
"""Entry type."""
284
285
@property
286
def effective_permissions(self) -> List[str]:
287
"""Effective permissions for the entry."""
288
289
@property
290
def permissions(self) -> List[Permissions]:
291
"""Permission settings for the entry."""
292
293
@property
294
def size_bytes(self) -> int:
295
"""Entry size in bytes."""
296
297
@property
298
def is_directory(self) -> bool:
299
"""True if entry is a directory."""
300
301
@property
302
def is_table(self) -> bool:
303
"""True if entry is a table."""
304
305
@property
306
def is_topic(self) -> bool:
307
"""True if entry is a topic."""
308
309
class Directory:
310
def __init__(
311
self,
312
path: str,
313
children: List[SchemeEntry] = None,
314
self_entry: SchemeEntry = None
315
):
316
"""
317
Directory information with child entries.
318
319
Args:
320
path (str): Directory path
321
children (List[SchemeEntry], optional): Child entries
322
self_entry (SchemeEntry, optional): Directory's own entry info
323
"""
324
325
@property
326
def path(self) -> str:
327
"""Directory path."""
328
329
@property
330
def children(self) -> List[SchemeEntry]:
331
"""Child entries in the directory."""
332
333
@property
334
def self_entry(self) -> SchemeEntry:
335
"""Directory's own schema entry."""
336
337
def get_child(self, name: str) -> Optional[SchemeEntry]:
338
"""
339
Get child entry by name.
340
341
Args:
342
name (str): Child entry name
343
344
Returns:
345
Optional[SchemeEntry]: Child entry or None if not found
346
"""
347
348
def has_child(self, name: str) -> bool:
349
"""
350
Check if directory has a child with given name.
351
352
Args:
353
name (str): Child entry name
354
355
Returns:
356
bool: True if child exists
357
"""
358
359
def list_tables(self) -> List[SchemeEntry]:
360
"""
361
Get all table entries in the directory.
362
363
Returns:
364
List[SchemeEntry]: Table entries
365
"""
366
367
def list_directories(self) -> List[SchemeEntry]:
368
"""
369
Get all subdirectory entries.
370
371
Returns:
372
List[SchemeEntry]: Directory entries
373
"""
374
375
def list_topics(self) -> List[SchemeEntry]:
376
"""
377
Get all topic entries in the directory.
378
379
Returns:
380
List[SchemeEntry]: Topic entries
381
"""
382
```
383
384
### Permissions Management
385
386
Access control and permission management for database objects.
387
388
```python { .api }
389
class Permissions:
390
def __init__(
391
self,
392
subject: str = None,
393
permission_names: List[str] = None
394
):
395
"""
396
Permission settings for database objects.
397
398
Args:
399
subject (str, optional): Permission subject (user or group)
400
permission_names (List[str], optional): List of permission names
401
"""
402
403
@property
404
def subject(self) -> str:
405
"""Permission subject."""
406
407
@property
408
def permission_names(self) -> List[str]:
409
"""List of permission names."""
410
411
def add_permission(self, permission_name: str) -> 'Permissions':
412
"""
413
Add permission to the list.
414
415
Args:
416
permission_name (str): Permission name to add
417
418
Returns:
419
Permissions: Self for method chaining
420
"""
421
422
def remove_permission(self, permission_name: str) -> 'Permissions':
423
"""
424
Remove permission from the list.
425
426
Args:
427
permission_name (str): Permission name to remove
428
429
Returns:
430
Permissions: Self for method chaining
431
"""
432
433
def has_permission(self, permission_name: str) -> bool:
434
"""
435
Check if permission is granted.
436
437
Args:
438
permission_name (str): Permission name to check
439
440
Returns:
441
bool: True if permission is granted
442
"""
443
444
class ModifyPermissionsSettings(BaseRequestSettings):
445
def __init__(
446
self,
447
actions: List[PermissionAction] = None,
448
request_type: str = None,
449
trace_id: str = None,
450
timeout: float = None
451
):
452
"""
453
Settings for permission modification operations.
454
455
Args:
456
actions (List[PermissionAction], optional): Permission actions to perform
457
request_type (str, optional): Request type for logging
458
trace_id (str, optional): Request tracing identifier
459
timeout (float, optional): Operation timeout
460
"""
461
462
class PermissionAction:
463
def __init__(
464
self,
465
action_type: PermissionActionType,
466
subject: str,
467
permission_names: List[str]
468
):
469
"""
470
Permission modification action.
471
472
Args:
473
action_type (PermissionActionType): Type of action (grant/revoke)
474
subject (str): Permission subject
475
permission_names (List[str]): Permissions to modify
476
"""
477
478
@property
479
def action_type(self) -> PermissionActionType:
480
"""Type of permission action."""
481
482
@property
483
def subject(self) -> str:
484
"""Permission subject."""
485
486
@property
487
def permission_names(self) -> List[str]:
488
"""Permissions being modified."""
489
490
class PermissionActionType(enum.Enum):
491
"""Permission action types."""
492
GRANT = "grant"
493
REVOKE = "revoke"
494
SET = "set"
495
```
496
497
### Table Schema Information
498
499
Detailed table structure information retrieved through schema operations.
500
501
```python { .api }
502
class TableDescription:
503
def __init__(
504
self,
505
name: str = None,
506
columns: List[TableColumn] = None,
507
primary_key: List[str] = None,
508
indexes: List[TableIndex] = None,
509
ttl_settings: TtlSettings = None,
510
storage_settings: StorageSettings = None,
511
column_families: List[ColumnFamily] = None,
512
attributes: Dict[str, str] = None,
513
compaction_policy: CompactionPolicy = None,
514
partitioning_settings: PartitioningSettings = None,
515
key_bloom_filter: bool = None,
516
read_replicas_settings: ReadReplicasSettings = None
517
):
518
"""
519
Complete table description with structure and settings.
520
521
Args:
522
name (str, optional): Table name
523
columns (List[TableColumn], optional): Table columns
524
primary_key (List[str], optional): Primary key column names
525
indexes (List[TableIndex], optional): Secondary indexes
526
ttl_settings (TtlSettings, optional): Time-to-live settings
527
storage_settings (StorageSettings, optional): Storage configuration
528
column_families (List[ColumnFamily], optional): Column families
529
attributes (Dict[str, str], optional): Table attributes
530
compaction_policy (CompactionPolicy, optional): Compaction settings
531
partitioning_settings (PartitioningSettings, optional): Partitioning configuration
532
key_bloom_filter (bool, optional): Enable key bloom filter
533
read_replicas_settings (ReadReplicasSettings, optional): Read replica settings
534
"""
535
536
def with_column(self, column: TableColumn) -> 'TableDescription':
537
"""
538
Add column to table description.
539
540
Args:
541
column (TableColumn): Column to add
542
543
Returns:
544
TableDescription: Self for method chaining
545
"""
546
547
def with_primary_key(self, *key_names: str) -> 'TableDescription':
548
"""
549
Set primary key columns.
550
551
Args:
552
*key_names (str): Primary key column names
553
554
Returns:
555
TableDescription: Self for method chaining
556
"""
557
558
def with_index(self, index: TableIndex) -> 'TableDescription':
559
"""
560
Add secondary index to table description.
561
562
Args:
563
index (TableIndex): Index to add
564
565
Returns:
566
TableDescription: Self for method chaining
567
"""
568
569
def with_ttl(self, ttl_settings: TtlSettings) -> 'TableDescription':
570
"""
571
Set time-to-live settings.
572
573
Args:
574
ttl_settings (TtlSettings): TTL configuration
575
576
Returns:
577
TableDescription: Self for method chaining
578
"""
579
580
@property
581
def columns(self) -> List[TableColumn]:
582
"""Table columns."""
583
584
@property
585
def primary_key(self) -> List[str]:
586
"""Primary key column names."""
587
588
@property
589
def indexes(self) -> List[TableIndex]:
590
"""Secondary indexes."""
591
592
def get_column(self, name: str) -> Optional[TableColumn]:
593
"""
594
Get column by name.
595
596
Args:
597
name (str): Column name
598
599
Returns:
600
Optional[TableColumn]: Column or None if not found
601
"""
602
603
def has_column(self, name: str) -> bool:
604
"""
605
Check if table has column with given name.
606
607
Args:
608
name (str): Column name
609
610
Returns:
611
bool: True if column exists
612
"""
613
614
class TableColumn:
615
def __init__(
616
self,
617
name: str,
618
type: Type,
619
family: str = None,
620
not_null: bool = False
621
):
622
"""
623
Table column description.
624
625
Args:
626
name (str): Column name
627
type (Type): Column YDB type
628
family (str, optional): Column family name
629
not_null (bool): Whether column is not nullable
630
"""
631
632
@property
633
def name(self) -> str:
634
"""Column name."""
635
636
@property
637
def type(self) -> Type:
638
"""Column YDB type."""
639
640
@property
641
def family(self) -> str:
642
"""Column family name."""
643
644
@property
645
def not_null(self) -> bool:
646
"""Whether column is not nullable."""
647
648
class TableIndex:
649
def __init__(
650
self,
651
name: str,
652
index_columns: List[str],
653
data_columns: List[str] = None,
654
global_index: bool = True,
655
unique: bool = False
656
):
657
"""
658
Table index description.
659
660
Args:
661
name (str): Index name
662
index_columns (List[str]): Indexed column names
663
data_columns (List[str], optional): Additional data columns
664
global_index (bool): Whether index is global
665
unique (bool): Whether index enforces uniqueness
666
"""
667
668
@property
669
def name(self) -> str:
670
"""Index name."""
671
672
@property
673
def index_columns(self) -> List[str]:
674
"""Indexed column names."""
675
676
@property
677
def data_columns(self) -> List[str]:
678
"""Additional data columns."""
679
680
@property
681
def global_index(self) -> bool:
682
"""Whether index is global."""
683
684
@property
685
def unique(self) -> bool:
686
"""Whether index enforces uniqueness."""
687
```
688
689
### Schema Utility Functions
690
691
Convenience functions for common schema operations.
692
693
```python { .api }
694
def make_directory(
695
driver: Driver,
696
path: str,
697
settings: MakeDirectorySettings = None
698
):
699
"""
700
Create directory using driver.
701
702
Args:
703
driver (Driver): YDB driver instance
704
path (str): Directory path to create
705
settings (MakeDirectorySettings, optional): Creation settings
706
"""
707
708
def remove_directory(
709
driver: Driver,
710
path: str,
711
settings: RemoveDirectorySettings = None
712
):
713
"""
714
Remove directory using driver.
715
716
Args:
717
driver (Driver): YDB driver instance
718
path (str): Directory path to remove
719
settings (RemoveDirectorySettings, optional): Removal settings
720
"""
721
722
def list_directory(
723
driver: Driver,
724
path: str,
725
settings: ListDirectorySettings = None
726
) -> Directory:
727
"""
728
List directory contents using driver.
729
730
Args:
731
driver (Driver): YDB driver instance
732
path (str): Directory path to list
733
settings (ListDirectorySettings, optional): Listing settings
734
735
Returns:
736
Directory: Directory information
737
"""
738
739
def describe_path(
740
driver: Driver,
741
path: str,
742
settings: DescribePathSettings = None
743
) -> SchemeEntry:
744
"""
745
Describe path entry using driver.
746
747
Args:
748
driver (Driver): YDB driver instance
749
path (str): Path to describe
750
settings (DescribePathSettings, optional): Description settings
751
752
Returns:
753
SchemeEntry: Path entry information
754
"""
755
```
756
757
## Usage Examples
758
759
### Directory Operations
760
761
```python
762
import ydb
763
764
# Create driver and scheme client
765
driver = ydb.Driver(endpoint="grpc://localhost:2136", database="/local")
766
driver.wait(fail_fast=True)
767
768
scheme_client = ydb.SchemeClient(driver)
769
770
# Create directory hierarchy
771
scheme_client.make_directory("/local/app")
772
scheme_client.make_directory("/local/app/tables")
773
scheme_client.make_directory("/local/app/indexes")
774
775
# List directory contents
776
directory = scheme_client.list_directory("/local/app")
777
print(f"Directory: {directory.path}")
778
779
for entry in directory.children:
780
print(f" {entry.name} ({entry.type})")
781
if entry.is_directory:
782
print(f" Directory owned by: {entry.owner}")
783
elif entry.is_table:
784
print(f" Table size: {entry.size_bytes} bytes")
785
```
786
787
### Schema Inspection
788
789
```python
790
# Inspect table schema
791
def inspect_table_schema(driver, table_path):
792
scheme_client = ydb.SchemeClient(driver)
793
794
# Get table entry information
795
entry = scheme_client.describe_path(table_path)
796
print(f"Table: {entry.name}")
797
print(f"Owner: {entry.owner}")
798
print(f"Size: {entry.size_bytes} bytes")
799
800
# Get detailed table description through session
801
session_pool = ydb.SessionPool(driver)
802
803
def get_table_description(session):
804
return session.describe_table(table_path)
805
806
table_desc = session_pool.retry_operation_sync(get_table_description)
807
808
print("\nColumns:")
809
for column in table_desc.columns:
810
nullable = "NULL" if not column.not_null else "NOT NULL"
811
print(f" {column.name}: {column.type} {nullable}")
812
813
print(f"\nPrimary Key: {', '.join(table_desc.primary_key)}")
814
815
if table_desc.indexes:
816
print("\nIndexes:")
817
for index in table_desc.indexes:
818
index_type = "UNIQUE" if index.unique else "NON-UNIQUE"
819
print(f" {index.name}: {index_type} on {', '.join(index.index_columns)}")
820
821
inspect_table_schema(driver, "/local/users")
822
```
823
824
### Directory Tree Traversal
825
826
```python
827
def traverse_directory_tree(scheme_client, path, level=0):
828
"""Recursively traverse and display directory tree."""
829
indent = " " * level
830
831
try:
832
directory = scheme_client.list_directory(path)
833
print(f"{indent}{directory.self_entry.name}/")
834
835
# Process child entries
836
for entry in directory.children:
837
if entry.is_directory:
838
# Recursively traverse subdirectories
839
child_path = f"{path}/{entry.name}".replace("//", "/")
840
traverse_directory_tree(scheme_client, child_path, level + 1)
841
else:
842
# Display non-directory entries
843
entry_type = "table" if entry.is_table else "topic" if entry.is_topic else str(entry.type)
844
print(f"{indent} {entry.name} ({entry_type})")
845
846
except ydb.NotFoundError:
847
print(f"{indent}[Directory not found: {path}]")
848
849
# Usage
850
traverse_directory_tree(scheme_client, "/local")
851
```
852
853
### Permission Management
854
855
```python
856
# Modify permissions for a table
857
def grant_table_permissions(scheme_client, table_path, user, permissions):
858
"""Grant permissions to user for a table."""
859
860
permission_actions = [
861
ydb.PermissionAction(
862
action_type=ydb.PermissionActionType.GRANT,
863
subject=user,
864
permission_names=permissions
865
)
866
]
867
868
settings = ydb.ModifyPermissionsSettings(actions=permission_actions)
869
870
scheme_client.modify_permissions(table_path, settings)
871
print(f"Granted permissions {permissions} to {user} for {table_path}")
872
873
# Grant read/write permissions
874
grant_table_permissions(
875
scheme_client,
876
"/local/users",
877
"user@domain.com",
878
["ydb.generic.read", "ydb.generic.write"]
879
)
880
881
# Check current permissions
882
entry = scheme_client.describe_path("/local/users")
883
print("Current permissions:")
884
for perm in entry.permissions:
885
print(f" {perm.subject}: {perm.permission_names}")
886
```
887
888
### Schema Comparison
889
890
```python
891
def compare_table_schemas(scheme_client, session_pool, table1_path, table2_path):
892
"""Compare schemas of two tables."""
893
894
def get_table_info(session, table_path):
895
desc = session.describe_table(table_path)
896
return {
897
"columns": {col.name: col.type for col in desc.columns},
898
"primary_key": desc.primary_key,
899
"indexes": {idx.name: idx.index_columns for idx in desc.indexes}
900
}
901
902
# Get both table descriptions
903
table1_info = session_pool.retry_operation_sync(get_table_info, table1_path)
904
table2_info = session_pool.retry_operation_sync(get_table_info, table2_path)
905
906
# Compare columns
907
print("Column differences:")
908
all_columns = set(table1_info["columns"].keys()) | set(table2_info["columns"].keys())
909
910
for col_name in sorted(all_columns):
911
type1 = table1_info["columns"].get(col_name, "MISSING")
912
type2 = table2_info["columns"].get(col_name, "MISSING")
913
914
if type1 != type2:
915
print(f" {col_name}: {type1} vs {type2}")
916
917
# Compare primary keys
918
if table1_info["primary_key"] != table2_info["primary_key"]:
919
print(f"Primary key difference:")
920
print(f" Table 1: {table1_info['primary_key']}")
921
print(f" Table 2: {table2_info['primary_key']}")
922
923
# Compare indexes
924
if table1_info["indexes"] != table2_info["indexes"]:
925
print("Index differences:")
926
all_indexes = set(table1_info["indexes"].keys()) | set(table2_info["indexes"].keys())
927
928
for idx_name in sorted(all_indexes):
929
idx1 = table1_info["indexes"].get(idx_name, "MISSING")
930
idx2 = table2_info["indexes"].get(idx_name, "MISSING")
931
932
if idx1 != idx2:
933
print(f" {idx_name}: {idx1} vs {idx2}")
934
935
# Usage
936
compare_table_schemas(scheme_client, session_pool, "/local/users", "/local/users_backup")
937
```
938
939
## Type Definitions
940
941
```python { .api }
942
# Common type aliases
943
SchemaPath = str
944
EntryName = str
945
PermissionName = str
946
Subject = str
947
948
# Permission collections
949
ReadPermissions = ["ydb.generic.read"]
950
WritePermissions = ["ydb.generic.write"]
951
AdminPermissions = ["ydb.generic.read", "ydb.generic.write", "ydb.generic.manage"]
952
953
# Entry type predicates
954
IsDirectory = Callable[[SchemeEntry], bool]
955
IsTable = Callable[[SchemeEntry], bool]
956
IsAnyTable = Callable[[SchemeEntry], bool]
957
```