Skip to content

Commit 812e503

Browse files
committed
Fixes for FB 5
1 parent 9cad4da commit 812e503

7 files changed

Lines changed: 55 additions & 15 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/)
55
and this project adheres to [Semantic Versioning](http://semver.org/).
66

7+
## [2.0.1] - 2026-04-20
8+
9+
### Added
10+
11+
- Missing Firebird 5 items `database_guid` and `table_size` items in `gstat` module.
12+
- Missing Firebird 5 items `parallel_workers` and `intermediate_gc` in `monitor` module.
13+
714
## [2.0.0] - 2025-04-30
815

916
### Changed

docs/changelog.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22
Changelog
33
#########
44

5+
6+
Version 2.0.1
7+
=============
8+
9+
- Added missing Firebird 5 items `database_guid` and `table_size` items in `gstat` module.
10+
- Added missing Firebird 5 items `parallel_workers` and `intermediate_gc` in `monitor` module.
11+
512
Version 2.0.0
613
=============
714

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ dependencies = [
5959
extra-args = ["--host=localhost"]
6060

6161
[[tool.hatch.envs.hatch-test.matrix]]
62-
python = ["3.11", "3.12", "3.13"]
62+
python = ["3.11", "3.12", "3.13", "3.14"]
6363

6464
[tool.hatch.envs.doc]
6565
detached = false

src/firebird/lib/__about__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# SPDX-FileCopyrightText: 2020-present The Firebird Projects <www.firebirdsql.org>
22
#
33
# SPDX-License-Identifier: MIT
4-
__version__ = "2.0.0"
4+
__version__ = "2.0.1"

src/firebird/lib/gstat.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,9 @@
8888
('Database backup GUID:', 's', 'backup_guid'),
8989
('Root file name:', 's', 'root_filename'),
9090
('Replay logging file:', 's', None),
91-
('Backup difference file:', 's', 'backup_diff_file')]
91+
('Backup difference file:', 's', 'backup_diff_file'),
92+
('Database GUID:', 's', None)
93+
]
9294

9395
items_tbl3: TLogItemSpec = [
9496
('Primary pointer page:', 'i', None),
@@ -119,7 +121,8 @@
119121
('blob pages:', 'i', None),
120122
('Level 0:', 'i', None),
121123
('Level 1:', 'i', None),
122-
('Level 2:', 'i', None)]
124+
('Level 2:', 'i', None),
125+
]
123126

124127
items_idx3: TLogItemSpec = [
125128
('Root page:', 'i', None),
@@ -271,6 +274,8 @@ def __init__(self):
271274
self.level_1: int | None = None
272275
#: Number of Level 2 BLOB values
273276
self.level_2: int | None = None
277+
#: Table size
278+
self.table_size: int | None = None
274279

275280
class StatIndex:
276281
"""Statistics for a single database index, populated from gstat output.
@@ -387,6 +392,8 @@ def __init__(self):
387392
self.encrypted_blob_pages: int | None = None
388393
#: Database file names
389394
self.continuation_files: list[str] = []
395+
#: Database GUID
396+
self.database_guid: str | None = None
390397
#
391398
self.__line_no: int = 0
392399
self.__table: StatTable | None = None
@@ -537,6 +544,8 @@ def __parse_table(self, line: str) -> None:
537544
self.__table.distribution[i] = int(fill_value.strip())
538545
elif line.startswith('Fill distribution:'):
539546
pass
547+
elif line.startswith('Table size:'):
548+
self.__table.table_size = int(line[12:-6]) # Extract value from 'Table size: <VALUE> bytes'
540549
else:
541550
raise Error(f'Unknown information (line {self.__line_no})')
542551
def __parse_index(self, line: str) -> None:

src/firebird/lib/monitor.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ class State(IntEnum):
8787
"""
8888
IDLE = 0
8989
ACTIVE = 1
90+
STALLED = 2
9091

9192
class IsolationMode(IntEnum):
9293
"""Transaction solation mode.
@@ -346,7 +347,7 @@ def _strip_attribute(self, attr: str) -> None:
346347
if self._attributes.get(attr):
347348
self._attributes[attr] = self._attributes[attr].strip()
348349
@property
349-
def stat_id(self) -> Group | None:
350+
def stat_id(self) -> int | None:
350351
"""The statistic ID (`MON$STAT_ID`) for this monitored item."""
351352
return self._attributes.get('MON$STAT_ID')
352353

@@ -689,6 +690,14 @@ def session_timezone(self) -> str | None:
689690
.. versionadded:: 1.4.0
690691
"""
691692
return self._attributes.get('MON$SESSION_TIMEZONE')
693+
@property
694+
def parallel_workers(self) -> str | None:
695+
"""Maximum number of parallel workers for this connection, 1 means no parallel workers.
696+
“Garbage Collector” and “Cache Writer” connections may report 0.
697+
698+
.. versionadded:: 2.0.1
699+
"""
700+
return self._attributes.get('MON$PARALLEL_WORKERS')
692701

693702
class TransactionInfo(InfoItem):
694703
"""Information about transaction.
@@ -1204,6 +1213,14 @@ def fragment_reads(self) -> int:
12041213
def repeated_reads(self) -> int:
12051214
"""Number of repeated record reads."""
12061215
return self._attributes['MON$RECORD_RPT_READS']
1216+
# Firebird 4
1217+
@property
1218+
def intermediate_gc(self) -> int | None:
1219+
"""Number of records processed by the intermediate garbage collection.
1220+
1221+
.. versionadded:: 2.0.1
1222+
"""
1223+
return self._attributes.get('MON$RECORD_IMGC')
12071224

12081225
class ContextVariableInfo(InfoItem):
12091226
"""Information about context variable.

tests/test_gstat.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ def test_01_parse30_h(data_path):
114114
'completed': datetime.datetime(2018, 4, 4, 15, 41, 34),
115115
'continuation_file': None, 'continuation_files': 0,
116116
'creation_date': datetime.datetime(2015, 11, 27, 11, 19, 39),
117-
'database_dialect': 3, 'encrypted_blob_pages': None,
117+
'database_dialect': 3, 'database_guid': None, 'encrypted_blob_pages': None,
118118
'encrypted_data_pages': None, 'encrypted_index_pages': None,
119119
'executed': datetime.datetime(2018, 4, 4, 15, 41, 34),
120120
'filename': '/home/fdb/test/FBTEST30.FDB', 'flags': 0,
@@ -146,7 +146,7 @@ def test_02_parse30_a(data_path):
146146
expected_db_data = {'attributes': 1, 'backup_diff_file': None, 'backup_guid': '{F978F787-7023-4C4A-F79D-8D86645B0487}',
147147
'completed': datetime.datetime(2018, 4, 4, 15, 42),
148148
'continuation_file': None, 'continuation_files': 0, 'creation_date': datetime.datetime(2015, 11, 27, 11, 19, 39),
149-
'database_dialect': 3, 'encrypted_blob_pages': None, 'encrypted_data_pages': None, 'encrypted_index_pages': None,
149+
'database_dialect': 3, 'database_guid': None, 'encrypted_blob_pages': None, 'encrypted_data_pages': None, 'encrypted_index_pages': None,
150150
'executed': datetime.datetime(2018, 4, 4, 15, 42), 'filename': '/home/fdb/test/FBTEST30.FDB', 'flags': 0,
151151
'generation': 2176, 'gstat_version': 3, 'implementation': 'HW=AMD/Intel/x64 little-endian OS=Linux CC=gcc',
152152
'indices': 39, 'last_logical_page': None, 'next_attachment_id': 1199, 'next_header_page': 0,
@@ -169,14 +169,14 @@ def test_02_parse30_a(data_path):
169169
'data_page_slots': 3, 'data_pages': 3, 'distribution': FillDistribution(d20=0, d40=0, d60=0, d80=1, d100=2),
170170
'empty_pages': 0, 'full_pages': 1, 'index_root_page': 299, 'indices': 0, 'level_0': None, 'level_1': None, 'level_2': None,
171171
'max_fragments': None, 'max_versions': None, 'name': 'AR', 'pointer_pages': 1, 'primary_pages': 1,
172-
'primary_pointer_page': 297, 'secondary_pages': 2, 'swept_pages': 0, 'table_id': 140, 'total_formats': None,
172+
'primary_pointer_page': 297, 'secondary_pages': 2, 'swept_pages': 0, 'table_id': 140, 'table_size': None, 'total_formats': None,
173173
'total_fragments': None, 'total_records': None, 'total_versions': None, 'used_formats': None},
174174
{'avg_fill': 8, 'avg_fragment_length': None, 'avg_record_length': None, 'avg_unpacked_length': None,
175175
'avg_version_length': None, 'blob_pages': None, 'blobs': None, 'blobs_total_length': None, 'compression_ratio': None,
176176
'data_page_slots': 1, 'data_pages': 1, 'distribution': FillDistribution(d20=1, d40=0, d60=0, d80=0, d100=0),
177177
'empty_pages': 0, 'full_pages': 0, 'index_root_page': 183, 'indices': 1, 'level_0': None, 'level_1': None, 'level_2': None,
178178
'max_fragments': None, 'max_versions': None, 'name': 'COUNTRY', 'pointer_pages': 1, 'primary_pages': 1,
179-
'primary_pointer_page': 182, 'secondary_pages': 0, 'swept_pages': 0, 'table_id': 128, 'total_formats': None,
179+
'primary_pointer_page': 182, 'secondary_pages': 0, 'swept_pages': 0, 'table_id': 128, 'table_size': None, 'total_formats': None,
180180
'total_fragments': None, 'total_records': None, 'total_versions': None, 'used_formats': None},
181181
# ... Add more table data checks if needed ...
182182
]
@@ -223,7 +223,7 @@ def test_03_parse30_d(data_path):
223223
'data_page_slots': 3, 'data_pages': 3, 'distribution': FillDistribution(d20=0, d40=0, d60=0, d80=1, d100=2),
224224
'empty_pages': 0, 'full_pages': 1, 'index_root_page': 299, 'indices': 0, 'level_0': None, 'level_1': None, 'level_2': None,
225225
'max_fragments': None, 'max_versions': None, 'name': 'AR', 'pointer_pages': 1, 'primary_pages': 1,
226-
'primary_pointer_page': 297, 'secondary_pages': 2, 'swept_pages': 0, 'table_id': 140, 'total_formats': None,
226+
'primary_pointer_page': 297, 'secondary_pages': 2, 'swept_pages': 0, 'table_id': 140, 'table_size': None, 'total_formats': None,
227227
'total_fragments': None, 'total_records': None, 'total_versions': None, 'used_formats': None
228228
}
229229
assert get_object_data(db.tables[0]) == expected_ar_table # Assuming AR is the first table
@@ -237,7 +237,7 @@ def test_04_parse30_e(data_path):
237237
expected_data = {'attributes': 1, 'backup_diff_file': None, 'backup_guid': '{F978F787-7023-4C4A-F79D-8D86645B0487}',
238238
'completed': datetime.datetime(2018, 4, 4, 15, 45, 6),
239239
'continuation_file': None, 'continuation_files': 0, 'creation_date': datetime.datetime(2015, 11, 27, 11, 19, 39),
240-
'database_dialect': 3,
240+
'database_dialect': 3, 'database_guid': None,
241241
# Compare Encryption objects directly or their attributes
242242
'encrypted_blob_pages': Encryption(pages=11, encrypted=0, unencrypted=11),
243243
'encrypted_data_pages': Encryption(pages=121, encrypted=0, unencrypted=121),
@@ -298,7 +298,7 @@ def test_06_parse30_i(data_path):
298298
'data_page_slots': None, 'data_pages': None, 'distribution': None, 'empty_pages': None, 'full_pages': None,
299299
'index_root_page': None, 'indices': 1, 'level_0': None, 'level_1': None, 'level_2': None, 'max_fragments': None,
300300
'max_versions': None, 'name': 'COUNTRY', 'pointer_pages': None, 'primary_pages': None, 'primary_pointer_page': None,
301-
'secondary_pages': None, 'swept_pages': None, 'table_id': 128, 'total_formats': None, 'total_fragments': None,
301+
'secondary_pages': None, 'swept_pages': None, 'table_id': 128, 'table_size': None, 'total_formats': None, 'total_fragments': None,
302302
'total_records': None, 'total_versions': None, 'used_formats': None
303303
}
304304
# Find the COUNTRY table (order might vary)
@@ -341,7 +341,7 @@ def test_07_parse30_r(data_path):
341341
'data_page_slots': 3, 'data_pages': 3, 'distribution': FillDistribution(d20=0, d40=0, d60=0, d80=1, d100=2),
342342
'empty_pages': 0, 'full_pages': 1, 'index_root_page': 299, 'indices': 0, 'level_0': 125, 'level_1': 0, 'level_2': 0,
343343
'max_fragments': 0, 'max_versions': 1, 'name': 'AR', 'pointer_pages': 1, 'primary_pages': 1, 'primary_pointer_page': 297,
344-
'secondary_pages': 2, 'swept_pages': 0, 'table_id': 140, 'total_formats': 1, 'total_fragments': 0, 'total_records': 120,
344+
'secondary_pages': 2, 'swept_pages': 0, 'table_id': 140, 'table_size': None, 'total_formats': 1, 'total_fragments': 0, 'total_records': 120,
345345
'total_versions': 105, 'used_formats': 1
346346
}
347347
ar_table = next((t for t in db.tables if t.name == 'AR'), None)
@@ -382,7 +382,7 @@ def test_09_push30_h(data_path):
382382
'completed': datetime.datetime(2018, 4, 4, 15, 41, 34),
383383
'continuation_file': None, 'continuation_files': 0,
384384
'creation_date': datetime.datetime(2015, 11, 27, 11, 19, 39),
385-
'database_dialect': 3, 'encrypted_blob_pages': None,
385+
'database_dialect': 3, 'database_guid': None, 'encrypted_blob_pages': None,
386386
'encrypted_data_pages': None, 'encrypted_index_pages': None,
387387
'executed': datetime.datetime(2018, 4, 4, 15, 41, 34),
388388
'filename': '/home/fdb/test/FBTEST30.FDB', 'flags': 0,
@@ -414,7 +414,7 @@ def test_10_push30_a(data_path):
414414
expected_db_data = {'attributes': 1, 'backup_diff_file': None, 'backup_guid': '{F978F787-7023-4C4A-F79D-8D86645B0487}',
415415
'completed': datetime.datetime(2018, 4, 4, 15, 42),
416416
'continuation_file': None, 'continuation_files': 0, 'creation_date': datetime.datetime(2015, 11, 27, 11, 19, 39),
417-
'database_dialect': 3, 'encrypted_blob_pages': None, 'encrypted_data_pages': None, 'encrypted_index_pages': None,
417+
'database_dialect': 3, 'database_guid': None, 'encrypted_blob_pages': None, 'encrypted_data_pages': None, 'encrypted_index_pages': None,
418418
'executed': datetime.datetime(2018, 4, 4, 15, 42), 'filename': '/home/fdb/test/FBTEST30.FDB', 'flags': 0,
419419
'generation': 2176, 'gstat_version': 3, 'implementation': 'HW=AMD/Intel/x64 little-endian OS=Linux CC=gcc',
420420
'indices': 39, 'last_logical_page': None, 'next_attachment_id': 1199, 'next_header_page': 0,

0 commit comments

Comments
 (0)