Skip to content

Commit b15ce22

Browse files
committed
Release 1.3.4
1 parent fe692ef commit b15ce22

File tree

8 files changed

+239
-168
lines changed

8 files changed

+239
-168
lines changed

docs/changelog.txt

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

5+
Version 1.3.4
6+
=============
7+
8+
* `.FirebirdWarning` is not raised as exception, but reported via `warnings.warn` mechanism.
9+
* User-defined encoding for string parameter and response values exchanged between driver
10+
and Firebird engine. This includes TPB, DPB, SPB and various service values:
11+
12+
- `.TPB`: New `encoding` constructor parameter & attribute. Used for `table names` in table reservation.
13+
- `.DPB`: Encoding based on connection charset for `config`, `user name`, `password` and `role`.
14+
- `.SPB`: New `encoding` constructor parameter & attribute. Used for `config`, `user name`, `password` and `expected database`.
15+
- Connection-related providers: Encoding based on connection charset.
16+
- Server and Service providers: New `.Server.encoding` attribute.
17+
* Fixed bug in `.ServerDbServices3.get_statistics` when `tables` are specified.
18+
19+
520
Version 1.3.3
621
=============
722

docs/conf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@
2323
author = 'Pavel Císař'
2424

2525
# The short X.Y version
26-
version = '1.3.3'
26+
version = '1.3.4'
2727

2828
# The full version, including alpha/beta/rc tags
29-
release = '1.3.3'
29+
release = '1.3.4'
3030

3131

3232
# -- General configuration ---------------------------------------------------

firebird/driver/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,4 @@
5959
Server, Statement
6060

6161
#: Current driver version, SEMVER string.
62-
__VERSION__ = '1.3.2'
62+
__VERSION__ = '1.3.4'

firebird/driver/core.py

Lines changed: 165 additions & 142 deletions
Large diffs are not rendered by default.

firebird/driver/interfaces.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
import sys
4040
import threading
4141
import datetime
42-
#from warnings import warn
42+
from warnings import warn
4343
from ctypes import memmove, memset, create_string_buffer, cast, byref, string_at, sizeof, \
4444
c_char_p, c_void_p, c_byte, c_ulong
4545
from .types import Error, DatabaseError, InterfaceError, FirebirdWarning, BCD, \
@@ -104,9 +104,9 @@ def _check(self) -> None:
104104
if StateFlag.ERRORS in state:
105105
raise self.__report(DatabaseError, self.status.get_errors())
106106
if StateFlag.WARNINGS in state: # pragma: no cover
107-
raise self.__report(FirebirdWarning, self.status.get_warning())
108-
#warn(self.__report(FirebirdWarning, self.status.get_warning()).args[0],
109-
#FirebirdWarning, 2)
107+
#raise self.__report(FirebirdWarning, self.status.get_warning())
108+
warn(self.__report(FirebirdWarning, self.status.get_warning()),
109+
stacklevel=2)
110110
@property
111111
def status(self) -> iStatus:
112112
"iStatus for interface"
@@ -868,7 +868,7 @@ class iAttachment_v3(iReferenceCounted):
868868
VERSION = 3
869869
def __init__(self, intf):
870870
super().__init__(intf)
871-
self.charset: str = 'ascii'
871+
self.encoding: str = 'ascii'
872872
def get_info(self, items: bytes, buffer: bytes) -> None:
873873
"Replaces `isc_database_info()`"
874874
self.vtable.getInfo(self, self.status, len(items), items, len(buffer), buffer)
@@ -930,7 +930,7 @@ def prepare(self, transaction: iTransaction, stmt: str, dialect: int,
930930
"""Replaces `isc_dsql_prepare()`. Additional parameter flags makes it
931931
possible to control what information will be preloaded from engine at once
932932
(i.e. in single network packet for remote operation)."""
933-
b_stmt: bytes = stmt.encode(self.charset)
933+
b_stmt: bytes = stmt.encode(self.encoding)
934934
result = self.vtable.prepare(self, self.status, transaction, len(b_stmt), b_stmt,
935935
dialect, flags)
936936
self._check()
@@ -941,7 +941,7 @@ def execute(self, transaction: iTransaction, stmt: str, dialect: int,
941941
"""Executes any SQL statement except returning multiple rows of data.
942942
Partial analogue of `isc_dsql_execute2()` - in and out XSLQDAs replaced with
943943
input and output messages with appropriate buffers."""
944-
b_stmt: bytes = stmt.encode(self.charset)
944+
b_stmt: bytes = stmt.encode(self.encoding)
945945
result = self.vtable.execute(self, self.status, transaction, len(b_stmt), b_stmt,
946946
dialect, in_metadata, in_buffer, out_metadata, out_buffer)
947947
self._check()
@@ -955,10 +955,10 @@ def open_cursor(self, transaction: iTransaction, stmt: str, dialect: int,
955955
may be used. Parameter cursor_name specifies name of opened cursor (analogue of
956956
`isc_dsql_set_cursor_name()`). Parameter cursor_flags is needed to open
957957
bidirectional cursor setting it's value to Istatement::CURSOR_TYPE_SCROLLABLE."""
958-
b_stmt: bytes = stmt.encode(self.charset)
958+
b_stmt: bytes = stmt.encode(self.encoding)
959959
result = self.vtable.openCursor(self, self.status, transaction, len(b_stmt), b_stmt,
960960
dialect, in_metadata, in_buffer, out_metadata,
961-
cursor_name.encode(self.charset), cursor_flags)
961+
cursor_name.encode(self.encoding), cursor_flags)
962962
self._check()
963963
return iResultSet(result)
964964
def que_events(self, callback: iEventCallbackImpl, events: bytes) -> iEvents:
@@ -1011,7 +1011,7 @@ def set_statement_timeout(self, timeout: int) -> None:
10111011
def create_batch(self, transaction: iTransaction, stmt: str, dialect: int,
10121012
in_metadata: iMessageMetadata, params: bytes) -> iBatch:
10131013
"TODO"
1014-
b_stmt: bytes = stmt.encode(self.charset)
1014+
b_stmt: bytes = stmt.encode(self.encoding)
10151015
result = self.vtable.createBatch(self, self.status, transaction, len(b_stmt), b_stmt,
10161016
dialect, in_metadata, len(params), params)
10171017
self._check()
@@ -1146,7 +1146,7 @@ def insert_bytes(self, tag: int, value: bytes) -> None:
11461146
"Inserts a clumplet with value containing passed bytes."
11471147
self.vtable.insertBytes(self, self.status, tag, value, len(value))
11481148
self._check()
1149-
def insert_string(self, tag: int, value: str, encoding='ascii') -> None:
1149+
def insert_string(self, tag: int, value: str, *, encoding='ascii') -> None:
11501150
"Inserts a clumplet with value containing passed string."
11511151
self.vtable.insertString(self, self.status, tag, value.encode(encoding))
11521152
self._check()
@@ -1197,11 +1197,11 @@ def get_bigint(self) -> int:
11971197
result = self.vtable.getBigInt(self, self.status)
11981198
self._check()
11991199
return result
1200-
def get_string(self) -> str:
1200+
def get_string(self, *, encoding='ascii') -> str:
12011201
"Returns value of current clumplet as string."
12021202
result = self.vtable.getString(self, self.status)
12031203
self._check()
1204-
return string_at(result).decode()
1204+
return string_at(result).decode(encoding)
12051205
def get_bytes(self) -> bytes:
12061206
"Returns value of current clumplet as bytes."
12071207
buffer = self.vtable.getBytes(self, self.status)

firebird/driver/types.py

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,36 @@ class NotSupportedError(DatabaseError):
112112

113113
# Firebird engine warning via Python Warning mechanism
114114

115-
class FirebirdWarning(Warning):
115+
class FirebirdWarning(UserWarning):
116116
"""Warning from Firebird engine.
117-
"""
117+
118+
The important difference from `Warning` class is that `FirebirdWarning` accepts keyword
119+
arguments, that are stored into instance attributes with the same name.
120+
121+
Important:
122+
Attribute lookup on this class never fails, as all attributes that are not actually
123+
set, have `None` value.
124+
125+
Example::
126+
127+
try:
128+
if condition:
129+
raise FirebirdWarning("Error message", err_code=1)
130+
else:
131+
raise FirebirdWarning("Unknown error")
132+
except FirebirdWarning as e:
133+
if e.err_code is None:
134+
...
135+
elif e.err_code == 1:
136+
...
137+
138+
"""
139+
def __init__(self, *args, **kwargs):
140+
super().__init__(*args)
141+
for name, value in kwargs.items():
142+
setattr(self, name, value)
143+
def __getattr__(self, name):
144+
return None
118145

119146
# Enums
120147

@@ -1077,7 +1104,7 @@ class SrvStatFlag(IntFlag):
10771104
RECORD_VERSIONS = 0x20
10781105
NOCREATION = 0x80
10791106
ENCRYPTION = 0x100
1080-
DEFAULT = DATA_PAGES | DATA_PAGES | IDX_PAGES
1107+
DEFAULT = DATA_PAGES | IDX_PAGES
10811108

10821109
class SrvBackupFlag(IntFlag):
10831110
"""isc_spb_bkp_* flags for ServerAction.BACKUP.

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ all-files=True
55

66
[metadata]
77
name = firebird-driver
8-
version = 1.3.3
8+
version = 1.3.4
99
description = Firebird driver
1010
long_description = file: README.rst
1111
long_description_content_type = text/x-rst; charset=UTF-8

test/test_driver.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@
5353

5454
trace = False
5555

56+
if not sys.warnoptions:
57+
import warnings
58+
warnings.simplefilter("always") # Change the filter in this process
59+
#os.environ["PYTHONWARNINGS"] = "default" # Also affect subprocesses
60+
5661
def os_environ_get_mock(key, default):
5762
return f'MOCK_{key}'
5863

@@ -441,7 +446,10 @@ def test_db_info(self):
441446
self.assertGreaterEqual(con.info.ods_version, 11)
442447
self.assertGreaterEqual(con.info.ods_minor_version, 0)
443448
self.assertGreaterEqual(con.info.page_cache_size, 75)
444-
self.assertEqual(con.info.pages_allocated, 367)
449+
if self.version == FB30:
450+
self.assertEqual(con.info.pages_allocated, 367)
451+
else:
452+
self.assertEqual(con.info.pages_allocated, 389)
445453
self.assertGreater(con.info.pages_used, 300)
446454
self.assertGreaterEqual(con.info.pages_free, 0)
447455
self.assertEqual(con.info.sweep_interval, 20000)
@@ -608,7 +616,8 @@ def test_tpb(self):
608616
with self.con.transaction_manager(tpb_buffer) as tr:
609617
info = tr.info.get_info(TraInfoCode.ISOLATION)
610618
if self.version == FB40:
611-
self.assertEqual(info, Isolation.READ_COMMITTED_READ_CONSISTENCY)
619+
self.assertIn(info, [Isolation.READ_COMMITTED_READ_CONSISTENCY,
620+
Isolation.READ_COMMITTED_RECORD_VERSION])
612621
else:
613622
self.assertEqual(info, Isolation.READ_COMMITTED_RECORD_VERSION)
614623
self.assertEqual(tr.info.get_info(TraInfoCode.ACCESS), TraInfoAccess.READ_WRITE)
@@ -994,10 +1003,7 @@ def test_affected_rows(self):
9941003
with self.con.cursor() as cur:
9951004
self.assertEqual(cur.affected_rows, -1)
9961005
cur.execute('select * from project')
997-
if self.version == FB40:
998-
self.assertEqual(cur.affected_rows, 1)
999-
else:
1000-
self.assertEqual(cur.affected_rows, 0)
1006+
self.assertEqual(cur.affected_rows, 0)
10011007
cur.fetchone()
10021008
if sys.platform == 'win32':
10031009
rcount = 6

0 commit comments

Comments
 (0)