2 @message |
ValueError: Incorrect ARRAY field value.
LOG DETAILS:
2025-06-29 05:35:07.196
2025-06-29 05:35:07.205 act = <firebird.qa.plugin.Action object at [hex]>
2025-06-29 05:35:07.215 capsys = <_pytest.capture.CaptureFixture object at [hex]>
2025-06-29 05:35:07.229
2025-06-29 05:35:07.237 @pytest.mark.version('>=5.0.1')
2025-06-29 05:35:07.244 def test_1(act: Action, capsys):
2025-06-29 05:35:07.251
2025-06-29 05:35:07.257 with act.db.connect() as con:
2025-06-29 05:35:07.263 cur = con.cursor()
2025-06-29 05:35:07.270 con.execute_immediate("recreate table array_table (id int generated by default as identity constraint pk_arr primary key, arr int[3,4])")
2025-06-29 05:35:07.277 con.commit()
2025-06-29 05:35:07.283
2025-06-29 05:35:07.293 data = (
2025-06-29 05:35:07.306 [ [87, 13, 16, 19], [25, 52, 73, 24], [81, 92, 63, 14] ]
2025-06-29 05:35:07.319 ,[ [21, 79, 63, 57], [34, 42, 13, 34], [71, 15, 73, 34] ]
2025-06-29 05:35:07.330 ,[ [31, 33, 55, 47], [17, 22, 33, 14], [91, 21, 93, 24] ]
2025-06-29 05:35:07.342 )
2025-06-29 05:35:07.353
2025-06-29 05:35:07.363 ps = cur.prepare("insert into array_table(arr) values (?)")
2025-06-29 05:35:07.371 for x in data:
2025-06-29 05:35:07.379 > cur.execute(ps, (x,))
2025-06-29 05:35:07.386
2025-06-29 05:35:07.392 tests/bugs/gh_8100_test.py:40:
2025-06-29 05:35:07.399 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-06-29 05:35:07.407
2025-06-29 05:35:07.419 self = <firebird.driver.core.Cursor object at [hex]>
2025-06-29 05:35:07.427 operation = Statement[insert into array_table(arr) values (?)]
2025-06-29 05:35:07.435 parameters = ([[87, 13, 16, 19], [25, 52, 73, 24], [81, 92, 63, 14]],)
2025-06-29 05:35:07.442
2025-06-29 05:35:07.449 def execute(self, operation: Union[str, Statement], parameters: Sequence[Any]=None) -> Cursor:
2025-06-29 05:35:07.455 """Executes SQL command or prepared `Statement`.
2025-06-29 05:35:07.461
2025-06-29 05:35:07.467 Starts new transaction if transaction manager associated with cursor is not active.
2025-06-29 05:35:07.475
2025-06-29 05:35:07.486 Arguments:
2025-06-29 05:35:07.494 operation: SQL command or prepared `Statement`.
2025-06-29 05:35:07.503 parameters: Sequence of parameters. Must contain one entry for each argument
2025-06-29 05:35:07.515 that the operation expects.
2025-06-29 05:35:07.524
2025-06-29 05:35:07.532 Returns:
2025-06-29 05:35:07.539 `self` so call to execute could be used as iterator over returned rows.
2025-06-29 05:35:07.546
2025-06-29 05:35:07.552 Note:
2025-06-29 05:35:07.558 If `operation` is a string with SQL command that is exactly the same as the
2025-06-29 05:35:07.571 last executed command, the internally prepared `Statement` from last execution
2025-06-29 05:35:07.579 is reused.
2025-06-29 05:35:07.586
2025-06-29 05:35:07.594 If cursor is open, it's closed before new statement is executed.
2025-06-29 05:35:07.600 """
2025-06-29 05:35:07.606 > self._execute(operation, parameters)
2025-06-29 05:35:07.612
2025-06-29 05:35:07.619 ../lib/python3.11/site-packages/firebird/driver/core.py:3861:
2025-06-29 05:35:07.625 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-06-29 05:35:07.631
2025-06-29 05:35:07.638 self = <firebird.driver.core.Cursor object at [hex]>
2025-06-29 05:35:07.644 operation = Statement[insert into array_table(arr) values (?)]
2025-06-29 05:35:07.650 parameters = ([[87, 13, 16, 19], [25, 52, 73, 24], [81, 92, 63, 14]],)
2025-06-29 05:35:07.657 flags = <CursorFlag.NONE: 0>
2025-06-29 05:35:07.662
2025-06-29 05:35:07.673 def _execute(self, operation: Union[str, Statement],
2025-06-29 05:35:07.685 parameters: Sequence=None, flags: CursorFlag=CursorFlag.NONE) -> None:
2025-06-29 05:35:07.695 if not self._transaction.is_active():
2025-06-29 05:35:07.702 self._transaction.begin()
2025-06-29 05:35:07.713 if isinstance(operation, Statement):
2025-06-29 05:35:07.722 if operation._connection() is not self._connection:
2025-06-29 05:35:07.730 raise InterfaceError('Cannot execute Statement that was created by different Connection.')
2025-06-29 05:35:07.743 self.close()
2025-06-29 05:35:07.753 self._stmt = operation
2025-06-29 05:35:07.761 self.__internal = False
2025-06-29 05:35:07.768 elif self._stmt is not None and self._stmt.sql == operation:
2025-06-29 05:35:07.774 # We should execute the same SQL string again
2025-06-29 05:35:07.784 self._clear()
2025-06-29 05:35:07.791 else:
2025-06-29 05:35:07.798 self.close()
2025-06-29 05:35:07.807 self._stmt = self._connection._prepare(operation, self._transaction)
2025-06-29 05:35:07.819 self.__internal = True
2025-06-29 05:35:07.828 self._cursor_flags = flags
2025-06-29 05:35:07.835 in_meta = None
2025-06-29 05:35:07.842 # Execute the statement
2025-06-29 05:35:07.849 try:
2025-06-29 05:35:07.855 if self._stmt._in_cnt > 0:
2025-06-29 05:35:07.861 > in_meta, self._stmt._in_buffer = self._pack_input(self._stmt._in_meta,
2025-06-29 05:35:07.868 self._stmt._in_buffer,
2025-06-29 05:35:07.874 parameters)
2025-06-29 05:35:07.879
2025-06-29 05:35:07.892 ../lib/python3.11/site-packages/firebird/driver/core.py:3738:
2025-06-29 05:35:07.904 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-06-29 05:35:07.912
2025-06-29 05:35:07.920 self = <firebird.driver.core.Cursor object at [hex]>
2025-06-29 05:35:07.927 meta = <firebird.driver.interfaces.iMessageMetadata object at [hex]>
2025-06-29 05:35:07.934 buffer = <ctypes.c_char_Array_10 object at [hex]>
2025-06-29 05:35:07.940 parameters = ([[87, 13, 16, 19], [25, 52, 73, 24], [81, 92, 63, 14]],)
2025-06-29 05:35:07.946
2025-06-29 05:35:07.953 def _pack_input(self, meta: iMessageMetadata, buffer: bytes,
2025-06-29 05:35:07.965 parameters: Sequence) -> Tuple[iMessageMetadata, bytes]:
2025-06-29 05:35:07.979 # pylint: disable=R1702
2025-06-29 05:35:07.990 in_cnt = meta.get_count()
2025-06-29 05:35:07.999 if len(parameters) != in_cnt:
2025-06-29 05:35:08.009 raise InterfaceError(f"Statement parameter sequence contains"
2025-06-29 05:35:08.018 f" {len(parameters)} items,"
2025-06-29 05:35:08.026 f" but exactly {in_cnt} are required")
2025-06-29 05:35:08.032 #
2025-06-29 05:35:08.039 buf_size = len(buffer)
2025-06-29 05:35:08.046 memset(buffer, 0, buf_size)
2025-06-29 05:35:08.057 # Adjust metadata where needed
2025-06-29 05:35:08.065 with meta.get_builder() as builder:
2025-06-29 05:35:08.076 for i in range(in_cnt):
2025-06-29 05:35:08.086 value = parameters[i]
2025-06-29 05:35:08.094 if _is_str_param(value, meta.get_type(i)):
2025-06-29 05:35:08.102 builder.set_type(i, SQLDataType.TEXT)
2025-06-29 05:35:08.108 if not isinstance(value, (str, bytes, bytearray)):
2025-06-29 05:35:08.114 value = str(value)
2025-06-29 05:35:08.121 builder.set_length(i, len(value.encode(self._encoding)) if isinstance(value, str) else len(value))
2025-06-29 05:35:08.127 in_meta = builder.get_metadata()
2025-06-29 05:35:08.133 new_size = in_meta.get_message_length()
2025-06-29 05:35:08.139 in_buffer = create_string_buffer(new_size) if buf_size < new_size else buffer
2025-06-29 05:35:08.145 buf_addr = addressof(in_buffer)
2025-06-29 05:35:08.150 with in_meta:
2025-06-29 05:35:08.163 for i in range(in_cnt):
2025-06-29 05:35:08.171 value = parameters[i]
2025-06-29 05:35:08.178 datatype = in_meta.get_type(i)
2025-06-29 05:35:08.187 length = in_meta.get_length(i)
2025-06-29 05:35:08.196 offset = in_meta.get_offset(i)
2025-06-29 05:35:08.203 # handle NULL value
2025-06-29 05:35:08.209 in_buffer[in_meta.get_null_offset(i)] = 1 if value is None else 0
2025-06-29 05:35:08.215 if value is None:
2025-06-29 05:35:08.223 continue
2025-06-29 05:35:08.233 # store parameter value
2025-06-29 05:35:08.241 if _is_str_param(value, datatype):
2025-06-29 05:35:08.248 # Implicit conversion to string
2025-06-29 05:35:08.254 if not isinstance(value, (str, bytes, bytearray)):
2025-06-29 05:35:08.260 value = str(value)
2025-06-29 05:35:08.266 if isinstance(value, str) and self._encoding:
2025-06-29 05:35:08.272 value = value.encode(self._encoding)
2025-06-29 05:35:08.278 if (datatype in (SQLDataType.TEXT, SQLDataType.VARYING)
2025-06-29 05:35:08.288 and len(value) > length):
2025-06-29 05:35:08.298 raise ValueError(f"Value of parameter ({i}) is too long,"
2025-06-29 05:35:08.306 f" expected {length}, found {len(value)}")
2025-06-29 05:35:08.315 memmove(buf_addr + offset, value, len(value))
2025-06-29 05:35:08.326 elif datatype in (SQLDataType.SHORT, SQLDataType.LONG, SQLDataType.INT64):
2025-06-29 05:35:08.340 # It's scalled integer?
2025-06-29 05:35:08.349 scale = in_meta.get_scale(i)
2025-06-29 05:35:08.361 if in_meta.get_subtype(i) or scale:
2025-06-29 05:35:08.371 if isinstance(value, decimal.Decimal):
2025-06-29 05:35:08.379 value = int((value * _tenTo[abs(scale)]).to_integral())
2025-06-29 05:35:08.387 elif isinstance(value, (int, float)):
2025-06-29 05:35:08.397 value = int(value * _tenTo[abs(scale)])
2025-06-29 05:35:08.409 else:
2025-06-29 05:35:08.419 raise TypeError(f'Objects of type {type(value)} are not '
2025-06-29 05:35:08.429 f' acceptable input for'
2025-06-29 05:35:08.439 f' a fixed-point column.')
2025-06-29 05:35:08.452 _check_integer_range(value, self._dialect, datatype,
2025-06-29 05:35:08.461 in_meta.get_subtype(i), scale)
2025-06-29 05:35:08.470 memmove(buf_addr + offset, value.to_bytes(length, 'little', signed=True), length)
2025-06-29 05:35:08.477 elif datatype == SQLDataType.DATE:
2025-06-29 05:35:08.484 memmove(buf_addr + offset, _util.encode_date(value).to_bytes(length, 'little', signed=True), length)
2025-06-29 05:35:08.490 elif datatype == SQLDataType.TIME:
2025-06-29 05:35:08.497 memmove(buf_addr + offset, _util.encode_time(value).to_bytes(length, 'little'), length)
2025-06-29 05:35:08.504 elif datatype == SQLDataType.TIME_TZ:
2025-06-29 05:35:08.511 memmove(buf_addr + offset, _util.encode_time_tz(value), length)
2025-06-29 05:35:08.517 elif datatype == SQLDataType.TIMESTAMP:
2025-06-29 05:35:08.528 memmove(buf_addr + offset, _encode_timestamp(value), length)
2025-06-29 05:35:08.540 elif datatype == SQLDataType.TIMESTAMP_TZ:
2025-06-29 05:35:08.549 memmove(buf_addr + offset, _util.encode_timestamp_tz(value), length)
2025-06-29 05:35:08.556 elif datatype == SQLDataType.DEC16:
2025-06-29 05:35:08.564 memmove(buf_addr + offset, byref(_util.get_decfloat16().from_str(str(value))), length)
2025-06-29 05:35:08.571 elif datatype == SQLDataType.DEC34:
2025-06-29 05:35:08.579 memmove(buf_addr + offset, _util.get_decfloat34().from_str(str(value)), length)
2025-06-29 05:35:08.589 elif datatype == SQLDataType.INT128:
2025-06-29 05:35:08.602 memmove(buf_addr + offset, _util.get_int128().from_str(str(value), in_meta.get_scale(i)), length)
2025-06-29 05:35:08.612 elif datatype == SQLDataType.FLOAT:
2025-06-29 05:35:08.618 memmove(buf_addr + offset, struct.pack('f', value), length)
2025-06-29 05:35:08.625 elif datatype == SQLDataType.DOUBLE:
2025-06-29 05:35:08.633 memmove(buf_addr + offset, struct.pack('d', value), length)
2025-06-29 05:35:08.639 elif datatype == SQLDataType.BOOLEAN:
2025-06-29 05:35:08.647 memmove(buf_addr + offset, (1 if value else 0).to_bytes(length, 'little'), length)
2025-06-29 05:35:08.659 elif datatype == SQLDataType.BLOB:
2025-06-29 05:35:08.670 blobid = a.ISC_QUAD(0, 0)
2025-06-29 05:35:08.681 if hasattr(value, 'read'):
2025-06-29 05:35:08.692 # It seems we've got file-like object, use stream BLOB
2025-06-29 05:35:08.701 blob_buf = _create_blob_buffer()
2025-06-29 05:35:08.714 blob: iBlob = self._connection._att.create_blob(self._transaction._tra,
2025-06-29 05:35:08.725 blobid, _bpb_stream)
2025-06-29 05:35:08.733 try:
2025-06-29 05:35:08.741 memmove(buf_addr + offset, addressof(blobid), length)
2025-06-29 05:35:08.748 while value_chunk := value.read(MAX_BLOB_SEGMENT_SIZE):
2025-06-29 05:35:08.755 blob_buf.raw = value_chunk.encode(self._encoding) if isinstance(value_chunk, str) else value_chunk
2025-06-29 05:35:08.769 blob.put_segment(len(value_chunk), blob_buf)
2025-06-29 05:35:08.782 memset(blob_buf, 0, MAX_BLOB_SEGMENT_SIZE)
2025-06-29 05:35:08.791 finally:
2025-06-29 05:35:08.800 blob.close()
2025-06-29 05:35:08.807 del blob_buf
2025-06-29 05:35:08.813 else:
2025-06-29 05:35:08.819 # Non-stream BLOB
2025-06-29 05:35:08.825 if isinstance(value, str):
2025-06-29 05:35:08.835 if in_meta.get_subtype(i) == 1:
2025-06-29 05:35:08.846 value = value.encode(self._encoding)
2025-06-29 05:35:08.855 else:
2025-06-29 05:35:08.864 raise TypeError('String value is not'
2025-06-29 05:35:08.872 ' acceptable type for'
2025-06-29 05:35:08.878 ' a non-textual BLOB column.')
2025-06-29 05:35:08.885 blob_buf = create_string_buffer(value)
2025-06-29 05:35:08.892 blob: iBlob = self._connection._att.create_blob(self._transaction._tra,
2025-06-29 05:35:08.899 blobid)
2025-06-29 05:35:08.905 try:
2025-06-29 05:35:08.911 memmove(buf_addr + offset, addressof(blobid), length)
2025-06-29 05:35:08.918 total_size = len(value)
2025-06-29 05:35:08.927 bytes_written_so_far = 0
2025-06-29 05:35:08.940 bytes_to_write_this_time = MAX_BLOB_SEGMENT_SIZE
2025-06-29 05:35:08.950 while bytes_written_so_far < total_size:
2025-06-29 05:35:08.959 if (total_size - bytes_written_so_far) < MAX_BLOB_SEGMENT_SIZE:
2025-06-29 05:35:08.972 bytes_to_write_this_time = (total_size - bytes_written_so_far)
2025-06-29 05:35:08.981 blob.put_segment(bytes_to_write_this_time,
2025-06-29 05:35:08.989 addressof(blob_buf) + bytes_written_so_far)
2025-06-29 05:35:09.001 bytes_written_so_far += bytes_to_write_this_time
2025-06-29 05:35:09.013 finally:
2025-06-29 05:35:09.024 blob.close()
2025-06-29 05:35:09.032 del blob_buf
2025-06-29 05:35:09.038 elif datatype == SQLDataType.ARRAY:
2025-06-29 05:35:09.051 arrayid = a.ISC_QUAD(0, 0)
2025-06-29 05:35:09.062 arrayid_ptr = pointer(arrayid)
2025-06-29 05:35:09.071 arraydesc = a.ISC_ARRAY_DESC(0)
2025-06-29 05:35:09.080 isc_status = a.ISC_STATUS_ARRAY()
2025-06-29 05:35:09.087 db_handle = self._connection._get_handle()
2025-06-29 05:35:09.094 tr_handle = self._transaction._get_handle()
2025-06-29 05:35:09.103 relname = in_meta.get_relation(i).encode(self._encoding)
2025-06-29 05:35:09.115 sqlname = in_meta.get_field(i).encode(self._encoding)
2025-06-29 05:35:09.126 api = a.get_api()
2025-06-29 05:35:09.135 sqlsubtype = self._connection._get_array_sqlsubtype(relname, sqlname)
2025-06-29 05:35:09.143 api.isc_array_lookup_bounds(isc_status, db_handle, tr_handle,
2025-06-29 05:35:09.151 relname, sqlname, arraydesc)
2025-06-29 05:35:09.162 if a.db_api_error(isc_status): # pragma: no cover
2025-06-29 05:35:09.171 raise a.exception_from_status(DatabaseError,
2025-06-29 05:35:09.179 isc_status,
2025-06-29 05:35:09.186 "Error in Cursor._pack_input:isc_array_lookup_bounds()")
2025-06-29 05:35:09.192 value_type = arraydesc.array_desc_dtype
2025-06-29 05:35:09.199 value_scale = arraydesc.array_desc_scale
2025-06-29 05:35:09.207 value_size = arraydesc.array_desc_length
2025-06-29 05:35:09.218 if value_type in (a.blr_varying, a.blr_varying2):
2025-06-29 05:35:09.227 value_size += 2
2025-06-29 05:35:09.235 dimensions = []
2025-06-29 05:35:09.246 total_num_elements = 1
2025-06-29 05:35:09.255 for dimension in range(arraydesc.array_desc_dimensions):
2025-06-29 05:35:09.263 bounds = arraydesc.array_desc_bounds[dimension]
2025-06-29 05:35:09.270 dimensions.append((bounds.array_bound_upper + 1) - bounds.array_bound_lower)
2025-06-29 05:35:09.278 total_num_elements *= dimensions[dimension]
2025-06-29 05:35:09.289 total_size = total_num_elements * value_size
2025-06-29 05:35:09.301 # Validate value to make sure it matches the array structure
2025-06-29 05:35:09.311 if not self._validate_array_value(0, dimensions, value_type,
2025-06-29 05:35:09.322 sqlsubtype, value_scale, value):
2025-06-29 05:35:09.336 > raise ValueError("Incorrect ARRAY field value.")
2025-06-29 05:35:09.349 E ValueError: Incorrect ARRAY field value.
2025-06-29 05:35:09.359
2025-06-29 05:35:09.369 ../lib/python3.11/site-packages/firebird/driver/core.py:3541: ValueError
2025-06-29 05:35:09.380 ---------------------------- Captured stdout setup -----------------------------
2025-06-29 05:35:09.392 Creating db: localhost:/var/tmp/qa_2024/test_11683/test.fdb [page_size=None, sql_dialect=None, charset='NONE', user=SYSDBA, password=masterkey]
|
3 #text |
act = <firebird.qa.plugin.Action pytest object at [hex]>
capsys = <_pytest.capture.CaptureFixture pytest object at [hex]>
@pytest.mark.version('>=5.0.1')
def test_1(act: Action, capsys):
with act.db.connect() as con:
cur = con.cursor()
con.execute_immediate("recreate table array_table (id int generated by default as identity constraint pk_arr primary key, arr int[3,4])")
con.commit()
data = (
[ [87, 13, 16, 19], [25, 52, 73, 24], [81, 92, 63, 14] ]
,[ [21, 79, 63, 57], [34, 42, 13, 34], [71, 15, 73, 34] ]
,[ [31, 33, 55, 47], [17, 22, 33, 14], [91, 21, 93, 24] ]
)
ps = cur.prepare("insert into array_table(arr) values (?)")
for x in data:
> cur.execute(ps, (x,))
tests/bugs/gh_8100_test.py:40:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <firebird.driver.core.Cursor pytest object at [hex]>
operation = Statement[insert into array_table(arr) values (?)]
parameters = ([[87, 13, 16, 19], [25, 52, 73, 24], [81, 92, 63, 14]],)
def execute(self, operation: Union[str, Statement], parameters: Sequence[Any]=None) -> Cursor:
"""Executes SQL command or prepared `Statement`.
Starts new transaction if transaction manager associated with cursor is not active.
Arguments:
operation: SQL command or prepared `Statement`.
parameters: Sequence of parameters. Must contain one entry for each argument
that the operation expects.
Returns:
`self` so call to execute could be used as iterator over returned rows.
Note:
If `operation` is a string with SQL command that is exactly the same as the
last executed command, the internally prepared `Statement` from last execution
is reused.
If cursor is open, it's closed before new statement is executed.
"""
> self._execute(operation, parameters)
../lib/python3.11/site-packages/firebird/driver/core.py:3861:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <firebird.driver.core.Cursor pytest object at [hex]>
operation = Statement[insert into array_table(arr) values (?)]
parameters = ([[87, 13, 16, 19], [25, 52, 73, 24], [81, 92, 63, 14]],)
flags = <CursorFlag.NONE: 0>
def _execute(self, operation: Union[str, Statement],
parameters: Sequence=None, flags: CursorFlag=CursorFlag.NONE) -> None:
if not self._transaction.is_active():
self._transaction.begin()
if isinstance(operation, Statement):
if operation._connection() is not self._connection:
raise InterfaceError('Cannot execute Statement that was created by different Connection.')
self.close()
self._stmt = operation
self.__internal = False
elif self._stmt is not None and self._stmt.sql == operation:
# We should execute the same SQL string again
self._clear()
else:
self.close()
self._stmt = self._connection._prepare(operation, self._transaction)
self.__internal = True
self._cursor_flags = flags
in_meta = None
# Execute the statement
try:
if self._stmt._in_cnt > 0:
> in_meta, self._stmt._in_buffer = self._pack_input(self._stmt._in_meta,
self._stmt._in_buffer,
parameters)
../lib/python3.11/site-packages/firebird/driver/core.py:3738:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <firebird.driver.core.Cursor pytest object at [hex]>
meta = <firebird.driver.interfaces.iMessageMetadata pytest object at [hex]>
buffer = <ctypes.c_char_Array_10 pytest object at [hex]>
parameters = ([[87, 13, 16, 19], [25, 52, 73, 24], [81, 92, 63, 14]],)
def _pack_input(self, meta: iMessageMetadata, buffer: bytes,
parameters: Sequence) -> Tuple[iMessageMetadata, bytes]:
# pylint: disable=R1702
in_cnt = meta.get_count()
if len(parameters) != in_cnt:
raise InterfaceError(f"Statement parameter sequence contains"
f" {len(parameters)} items,"
f" but exactly {in_cnt} are required")
#
buf_size = len(buffer)
memset(buffer, 0, buf_size)
# Adjust metadata where needed
with meta.get_builder() as builder:
for i in range(in_cnt):
value = parameters[i]
if _is_str_param(value, meta.get_type(i)):
builder.set_type(i, SQLDataType.TEXT)
if not isinstance(value, (str, bytes, bytearray)):
value = str(value)
builder.set_length(i, len(value.encode(self._encoding)) if isinstance(value, str) else len(value))
in_meta = builder.get_metadata()
new_size = in_meta.get_message_length()
in_buffer = create_string_buffer(new_size) if buf_size < new_size else buffer
buf_addr = addressof(in_buffer)
with in_meta:
for i in range(in_cnt):
value = parameters[i]
datatype = in_meta.get_type(i)
length = in_meta.get_length(i)
offset = in_meta.get_offset(i)
# handle NULL value
in_buffer[in_meta.get_null_offset(i)] = 1 if value is None else 0
if value is None:
continue
# store parameter value
if _is_str_param(value, datatype):
# Implicit conversion to string
if not isinstance(value, (str, bytes, bytearray)):
value = str(value)
if isinstance(value, str) and self._encoding:
value = value.encode(self._encoding)
if (datatype in (SQLDataType.TEXT, SQLDataType.VARYING)
and len(value) > length):
raise ValueError(f"Value of parameter ({i}) is too long,"
f" expected {length}, found {len(value)}")
memmove(buf_addr + offset, value, len(value))
elif datatype in (SQLDataType.SHORT, SQLDataType.LONG, SQLDataType.INT64):
# It's scalled integer?
scale = in_meta.get_scale(i)
if in_meta.get_subtype(i) or scale:
if isinstance(value, decimal.Decimal):
value = int((value * _tenTo[abs(scale)]).to_integral())
elif isinstance(value, (int, float)):
value = int(value * _tenTo[abs(scale)])
else:
raise TypeError(f'Objects of type {type(value)} are not '
f' acceptable input for'
f' a fixed-point column.')
_check_integer_range(value, self._dialect, datatype,
in_meta.get_subtype(i), scale)
memmove(buf_addr + offset, value.to_bytes(length, 'little', signed=True), length)
elif datatype == SQLDataType.DATE:
memmove(buf_addr + offset, _util.encode_date(value).to_bytes(length, 'little', signed=True), length)
elif datatype == SQLDataType.TIME:
memmove(buf_addr + offset, _util.encode_time(value).to_bytes(length, 'little'), length)
elif datatype == SQLDataType.TIME_TZ:
memmove(buf_addr + offset, _util.encode_time_tz(value), length)
elif datatype == SQLDataType.TIMESTAMP:
memmove(buf_addr + offset, _encode_timestamp(value), length)
elif datatype == SQLDataType.TIMESTAMP_TZ:
memmove(buf_addr + offset, _util.encode_timestamp_tz(value), length)
elif datatype == SQLDataType.DEC16:
memmove(buf_addr + offset, byref(_util.get_decfloat16().from_str(str(value))), length)
elif datatype == SQLDataType.DEC34:
memmove(buf_addr + offset, _util.get_decfloat34().from_str(str(value)), length)
elif datatype == SQLDataType.INT128:
memmove(buf_addr + offset, _util.get_int128().from_str(str(value), in_meta.get_scale(i)), length)
elif datatype == SQLDataType.FLOAT:
memmove(buf_addr + offset, struct.pack('f', value), length)
elif datatype == SQLDataType.DOUBLE:
memmove(buf_addr + offset, struct.pack('d', value), length)
elif datatype == SQLDataType.BOOLEAN:
memmove(buf_addr + offset, (1 if value else 0).to_bytes(length, 'little'), length)
elif datatype == SQLDataType.BLOB:
blobid = a.ISC_QUAD(0, 0)
if hasattr(value, 'read'):
# It seems we've got file-like object, use stream BLOB
blob_buf = _create_blob_buffer()
blob: iBlob = self._connection._att.create_blob(self._transaction._tra,
blobid, _bpb_stream)
try:
memmove(buf_addr + offset, addressof(blobid), length)
while value_chunk := value.read(MAX_BLOB_SEGMENT_SIZE):
blob_buf.raw = value_chunk.encode(self._encoding) if isinstance(value_chunk, str) else value_chunk
blob.put_segment(len(value_chunk), blob_buf)
memset(blob_buf, 0, MAX_BLOB_SEGMENT_SIZE)
finally:
blob.close()
del blob_buf
else:
# Non-stream BLOB
if isinstance(value, str):
if in_meta.get_subtype(i) == 1:
value = value.encode(self._encoding)
else:
raise TypeError('String value is not'
' acceptable type for'
' a non-textual BLOB column.')
blob_buf = create_string_buffer(value)
blob: iBlob = self._connection._att.create_blob(self._transaction._tra,
blobid)
try:
memmove(buf_addr + offset, addressof(blobid), length)
total_size = len(value)
bytes_written_so_far = 0
bytes_to_write_this_time = MAX_BLOB_SEGMENT_SIZE
while bytes_written_so_far < total_size:
if (total_size - bytes_written_so_far) < MAX_BLOB_SEGMENT_SIZE:
bytes_to_write_this_time = (total_size - bytes_written_so_far)
blob.put_segment(bytes_to_write_this_time,
addressof(blob_buf) + bytes_written_so_far)
bytes_written_so_far += bytes_to_write_this_time
finally:
blob.close()
del blob_buf
elif datatype == SQLDataType.ARRAY:
arrayid = a.ISC_QUAD(0, 0)
arrayid_ptr = pointer(arrayid)
arraydesc = a.ISC_ARRAY_DESC(0)
isc_status = a.ISC_STATUS_ARRAY()
db_handle = self._connection._get_handle()
tr_handle = self._transaction._get_handle()
relname = in_meta.get_relation(i).encode(self._encoding)
sqlname = in_meta.get_field(i).encode(self._encoding)
api = a.get_api()
sqlsubtype = self._connection._get_array_sqlsubtype(relname, sqlname)
api.isc_array_lookup_bounds(isc_status, db_handle, tr_handle,
relname, sqlname, arraydesc)
if a.db_api_error(isc_status): # pragma: no cover
raise a.exception_from_status(DatabaseError,
isc_status,
"Error in Cursor._pack_input:isc_array_lookup_bounds()")
value_type = arraydesc.array_desc_dtype
value_scale = arraydesc.array_desc_scale
value_size = arraydesc.array_desc_length
if value_type in (a.blr_varying, a.blr_varying2):
value_size += 2
dimensions = []
total_num_elements = 1
for dimension in range(arraydesc.array_desc_dimensions):
bounds = arraydesc.array_desc_bounds[dimension]
dimensions.append((bounds.array_bound_upper + 1) - bounds.array_bound_lower)
total_num_elements *= dimensions[dimension]
total_size = total_num_elements * value_size
# Validate value to make sure it matches the array structure
if not self._validate_array_value(0, dimensions, value_type,
sqlsubtype, value_scale, value):
> raise ValueError("Incorrect ARRAY field value.")
E ValueError: Incorrect ARRAY field value.
../lib/python3.11/site-packages/firebird/driver/core.py:3541: ValueError
|