2 @message |
ValueError: Incorrect ARRAY field value.
LOG DETAILS:
2025-07-02 06:41:20.251
2025-07-02 06:41:20.252 act = <firebird.qa.plugin.Action object at [hex]>
2025-07-02 06:41:20.252 capsys = <_pytest.capture.CaptureFixture object at [hex]>
2025-07-02 06:41:20.252
2025-07-02 06:41:20.252 @pytest.mark.version('>=5.0.1')
2025-07-02 06:41:20.252 def test_1(act: Action, capsys):
2025-07-02 06:41:20.252
2025-07-02 06:41:20.252 with act.db.connect() as con:
2025-07-02 06:41:20.252 cur = con.cursor()
2025-07-02 06:41:20.252 con.execute_immediate("recreate table array_table (id int generated by default as identity constraint pk_arr primary key, arr int[3,4])")
2025-07-02 06:41:20.252 con.commit()
2025-07-02 06:41:20.252
2025-07-02 06:41:20.252 data = (
2025-07-02 06:41:20.252 [ [87, 13, 16, 19], [25, 52, 73, 24], [81, 92, 63, 14] ]
2025-07-02 06:41:20.252 ,[ [21, 79, 63, 57], [34, 42, 13, 34], [71, 15, 73, 34] ]
2025-07-02 06:41:20.252 ,[ [31, 33, 55, 47], [17, 22, 33, 14], [91, 21, 93, 24] ]
2025-07-02 06:41:20.252 )
2025-07-02 06:41:20.252
2025-07-02 06:41:20.252 ps = cur.prepare("insert into array_table(arr) values (?)")
2025-07-02 06:41:20.252 for x in data:
2025-07-02 06:41:20.253 > cur.execute(ps, (x,))
2025-07-02 06:41:20.253
2025-07-02 06:41:20.253 tests\bugs\gh_8100_test.py:40:
2025-07-02 06:41:20.253 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 06:41:20.253
2025-07-02 06:41:20.253 self = <firebird.driver.core.Cursor object at [hex]>
2025-07-02 06:41:20.253 operation = Statement[insert into array_table(arr) values (?)]
2025-07-02 06:41:20.253 parameters = ([[87, 13, 16, 19], [25, 52, 73, 24], [81, 92, 63, 14]],)
2025-07-02 06:41:20.253
2025-07-02 06:41:20.253 def execute(self, operation: Union[str, Statement], parameters: Sequence[Any]=None) -> Cursor:
2025-07-02 06:41:20.253 """Executes SQL command or prepared `Statement`.
2025-07-02 06:41:20.253
2025-07-02 06:41:20.253 Starts new transaction if transaction manager associated with cursor is not active.
2025-07-02 06:41:20.253
2025-07-02 06:41:20.253 Arguments:
2025-07-02 06:41:20.253 operation: SQL command or prepared `Statement`.
2025-07-02 06:41:20.253 parameters: Sequence of parameters. Must contain one entry for each argument
2025-07-02 06:41:20.253 that the operation expects.
2025-07-02 06:41:20.253
2025-07-02 06:41:20.253 Returns:
2025-07-02 06:41:20.254 `self` so call to execute could be used as iterator over returned rows.
2025-07-02 06:41:20.254
2025-07-02 06:41:20.254 Note:
2025-07-02 06:41:20.254 If `operation` is a string with SQL command that is exactly the same as the
2025-07-02 06:41:20.254 last executed command, the internally prepared `Statement` from last execution
2025-07-02 06:41:20.254 is reused.
2025-07-02 06:41:20.254
2025-07-02 06:41:20.254 If cursor is open, it's closed before new statement is executed.
2025-07-02 06:41:20.254 """
2025-07-02 06:41:20.254 > self._execute(operation, parameters)
2025-07-02 06:41:20.254
2025-07-02 06:41:20.254 C:\Python3x\Lib\site-packages\firebird\driver\core.py:3862:
2025-07-02 06:41:20.254 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 06:41:20.254
2025-07-02 06:41:20.254 self = <firebird.driver.core.Cursor object at [hex]>
2025-07-02 06:41:20.254 operation = Statement[insert into array_table(arr) values (?)]
2025-07-02 06:41:20.254 parameters = ([[87, 13, 16, 19], [25, 52, 73, 24], [81, 92, 63, 14]],)
2025-07-02 06:41:20.254 flags = <CursorFlag.NONE: 0>
2025-07-02 06:41:20.254
2025-07-02 06:41:20.254 def _execute(self, operation: Union[str, Statement],
2025-07-02 06:41:20.254 parameters: Sequence=None, flags: CursorFlag=CursorFlag.NONE) -> None:
2025-07-02 06:41:20.255 if not self._transaction.is_active():
2025-07-02 06:41:20.255 self._transaction.begin()
2025-07-02 06:41:20.255 if isinstance(operation, Statement):
2025-07-02 06:41:20.255 if operation._connection() is not self._connection:
2025-07-02 06:41:20.255 raise InterfaceError('Cannot execute Statement that was created by different Connection.')
2025-07-02 06:41:20.255 self.close()
2025-07-02 06:41:20.255 self._stmt = operation
2025-07-02 06:41:20.255 self.__internal = False
2025-07-02 06:41:20.255 elif self._stmt is not None and self._stmt.sql == operation:
2025-07-02 06:41:20.255 # We should execute the same SQL string again
2025-07-02 06:41:20.255 self._clear()
2025-07-02 06:41:20.255 else:
2025-07-02 06:41:20.255 self.close()
2025-07-02 06:41:20.255 self._stmt = self._connection._prepare(operation, self._transaction)
2025-07-02 06:41:20.255 self.__internal = True
2025-07-02 06:41:20.255 self._cursor_flags = flags
2025-07-02 06:41:20.255 in_meta = None
2025-07-02 06:41:20.255 # Execute the statement
2025-07-02 06:41:20.255 try:
2025-07-02 06:41:20.255 if self._stmt._in_cnt > 0:
2025-07-02 06:41:20.256 > in_meta, self._stmt._in_buffer = self._pack_input(self._stmt._in_meta,
2025-07-02 06:41:20.256 self._stmt._in_buffer,
2025-07-02 06:41:20.256 parameters)
2025-07-02 06:41:20.256
2025-07-02 06:41:20.256 C:\Python3x\Lib\site-packages\firebird\driver\core.py:3739:
2025-07-02 06:41:20.256 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-02 06:41:20.256
2025-07-02 06:41:20.256 self = <firebird.driver.core.Cursor object at [hex]>
2025-07-02 06:41:20.256 meta = <firebird.driver.interfaces.iMessageMetadata object at [hex]>
2025-07-02 06:41:20.256 buffer = <ctypes.c_char_Array_10 object at [hex]>
2025-07-02 06:41:20.256 parameters = ([[87, 13, 16, 19], [25, 52, 73, 24], [81, 92, 63, 14]],)
2025-07-02 06:41:20.256
2025-07-02 06:41:20.256 def _pack_input(self, meta: iMessageMetadata, buffer: bytes,
2025-07-02 06:41:20.256 parameters: Sequence) -> Tuple[iMessageMetadata, bytes]:
2025-07-02 06:41:20.256 # pylint: disable=R1702
2025-07-02 06:41:20.256 in_cnt = meta.get_count()
2025-07-02 06:41:20.256 if len(parameters) != in_cnt:
2025-07-02 06:41:20.256 raise InterfaceError(f"Statement parameter sequence contains"
2025-07-02 06:41:20.256 f" {len(parameters)} items,"
2025-07-02 06:41:20.256 f" but exactly {in_cnt} are required")
2025-07-02 06:41:20.257 #
2025-07-02 06:41:20.257 buf_size = len(buffer)
2025-07-02 06:41:20.257 memset(buffer, 0, buf_size)
2025-07-02 06:41:20.257 # Adjust metadata where needed
2025-07-02 06:41:20.257 with meta.get_builder() as builder:
2025-07-02 06:41:20.257 for i in range(in_cnt):
2025-07-02 06:41:20.257 value = parameters[i]
2025-07-02 06:41:20.257 if _is_str_param(value, meta.get_type(i)):
2025-07-02 06:41:20.257 builder.set_type(i, SQLDataType.TEXT)
2025-07-02 06:41:20.257 if not isinstance(value, (str, bytes, bytearray)):
2025-07-02 06:41:20.257 value = str(value)
2025-07-02 06:41:20.257 builder.set_length(i, len(value.encode(self._encoding)) if isinstance(value, str) else len(value))
2025-07-02 06:41:20.257 in_meta = builder.get_metadata()
2025-07-02 06:41:20.257 new_size = in_meta.get_message_length()
2025-07-02 06:41:20.257 in_buffer = create_string_buffer(new_size) if buf_size < new_size else buffer
2025-07-02 06:41:20.257 buf_addr = addressof(in_buffer)
2025-07-02 06:41:20.257 with in_meta:
2025-07-02 06:41:20.257 for i in range(in_cnt):
2025-07-02 06:41:20.257 value = parameters[i]
2025-07-02 06:41:20.257 datatype = in_meta.get_type(i)
2025-07-02 06:41:20.258 length = in_meta.get_length(i)
2025-07-02 06:41:20.258 offset = in_meta.get_offset(i)
2025-07-02 06:41:20.258 # handle NULL value
2025-07-02 06:41:20.258 in_buffer[in_meta.get_null_offset(i)] = 1 if value is None else 0
2025-07-02 06:41:20.258 if value is None:
2025-07-02 06:41:20.258 continue
2025-07-02 06:41:20.258 # store parameter value
2025-07-02 06:41:20.258 if _is_str_param(value, datatype):
2025-07-02 06:41:20.258 # Implicit conversion to string
2025-07-02 06:41:20.258 if not isinstance(value, (str, bytes, bytearray)):
2025-07-02 06:41:20.258 value = str(value)
2025-07-02 06:41:20.258 if isinstance(value, str) and self._encoding:
2025-07-02 06:41:20.258 value = value.encode(self._encoding)
2025-07-02 06:41:20.258 if (datatype in (SQLDataType.TEXT, SQLDataType.VARYING)
2025-07-02 06:41:20.258 and len(value) > length):
2025-07-02 06:41:20.258 raise ValueError(f"Value of parameter ({i}) is too long,"
2025-07-02 06:41:20.258 f" expected {length}, found {len(value)}")
2025-07-02 06:41:20.258 memmove(buf_addr + offset, value, len(value))
2025-07-02 06:41:20.258 elif datatype in (SQLDataType.SHORT, SQLDataType.LONG, SQLDataType.INT64):
2025-07-02 06:41:20.258 # It's scalled integer?
2025-07-02 06:41:20.258 scale = in_meta.get_scale(i)
2025-07-02 06:41:20.259 if in_meta.get_subtype(i) or scale:
2025-07-02 06:41:20.259 if isinstance(value, decimal.Decimal):
2025-07-02 06:41:20.259 value = int((value * _tenTo[abs(scale)]).to_integral())
2025-07-02 06:41:20.259 elif isinstance(value, (int, float)):
2025-07-02 06:41:20.259 value = int(value * _tenTo[abs(scale)])
2025-07-02 06:41:20.259 else:
2025-07-02 06:41:20.259 raise TypeError(f'Objects of type {type(value)} are not '
2025-07-02 06:41:20.259 f' acceptable input for'
2025-07-02 06:41:20.259 f' a fixed-point column.')
2025-07-02 06:41:20.259 _check_integer_range(value, self._dialect, datatype,
2025-07-02 06:41:20.259 in_meta.get_subtype(i), scale)
2025-07-02 06:41:20.259 memmove(buf_addr + offset, value.to_bytes(length, 'little', signed=True), length)
2025-07-02 06:41:20.259 elif datatype == SQLDataType.DATE:
2025-07-02 06:41:20.259 memmove(buf_addr + offset, _util.encode_date(value).to_bytes(length, 'little', signed=True), length)
2025-07-02 06:41:20.259 elif datatype == SQLDataType.TIME:
2025-07-02 06:41:20.259 memmove(buf_addr + offset, _util.encode_time(value).to_bytes(length, 'little'), length)
2025-07-02 06:41:20.259 elif datatype == SQLDataType.TIME_TZ:
2025-07-02 06:41:20.259 memmove(buf_addr + offset, _util.encode_time_tz(value), length)
2025-07-02 06:41:20.260 elif datatype == SQLDataType.TIMESTAMP:
2025-07-02 06:41:20.260 memmove(buf_addr + offset, _encode_timestamp(value), length)
2025-07-02 06:41:20.260 elif datatype == SQLDataType.TIMESTAMP_TZ:
2025-07-02 06:41:20.260 memmove(buf_addr + offset, _util.encode_timestamp_tz(value), length)
2025-07-02 06:41:20.260 elif datatype == SQLDataType.DEC16:
2025-07-02 06:41:20.260 memmove(buf_addr + offset, byref(_util.get_decfloat16().from_str(str(value))), length)
2025-07-02 06:41:20.260 elif datatype == SQLDataType.DEC34:
2025-07-02 06:41:20.260 memmove(buf_addr + offset, _util.get_decfloat34().from_str(str(value)), length)
2025-07-02 06:41:20.260 elif datatype == SQLDataType.INT128:
2025-07-02 06:41:20.260 memmove(buf_addr + offset, _util.get_int128().from_str(str(value), in_meta.get_scale(i)), length)
2025-07-02 06:41:20.260 elif datatype == SQLDataType.FLOAT:
2025-07-02 06:41:20.260 memmove(buf_addr + offset, struct.pack('f', value), length)
2025-07-02 06:41:20.260 elif datatype == SQLDataType.DOUBLE:
2025-07-02 06:41:20.260 memmove(buf_addr + offset, struct.pack('d', value), length)
2025-07-02 06:41:20.260 elif datatype == SQLDataType.BOOLEAN:
2025-07-02 06:41:20.260 memmove(buf_addr + offset, (1 if value else 0).to_bytes(length, 'little'), length)
2025-07-02 06:41:20.260 elif datatype == SQLDataType.BLOB:
2025-07-02 06:41:20.260 blobid = a.ISC_QUAD(0, 0)
2025-07-02 06:41:20.260 if hasattr(value, 'read'):
2025-07-02 06:41:20.261 # It seems we've got file-like object, use stream BLOB
2025-07-02 06:41:20.261 blob_buf = _create_blob_buffer()
2025-07-02 06:41:20.261 blob: iBlob = self._connection._att.create_blob(self._transaction._tra,
2025-07-02 06:41:20.261 blobid, _bpb_stream)
2025-07-02 06:41:20.261 try:
2025-07-02 06:41:20.261 memmove(buf_addr + offset, addressof(blobid), length)
2025-07-02 06:41:20.261 while value_chunk := value.read(MAX_BLOB_SEGMENT_SIZE):
2025-07-02 06:41:20.261 blob_buf.raw = value_chunk.encode(self._encoding) if isinstance(value_chunk, str) else value_chunk
2025-07-02 06:41:20.261 blob.put_segment(len(value_chunk), blob_buf)
2025-07-02 06:41:20.261 memset(blob_buf, 0, MAX_BLOB_SEGMENT_SIZE)
2025-07-02 06:41:20.261 finally:
2025-07-02 06:41:20.261 blob.close()
2025-07-02 06:41:20.261 del blob_buf
2025-07-02 06:41:20.261 else:
2025-07-02 06:41:20.261 # Non-stream BLOB
2025-07-02 06:41:20.261 if isinstance(value, str):
2025-07-02 06:41:20.261 if in_meta.get_subtype(i) == 1:
2025-07-02 06:41:20.261 value = value.encode(self._encoding)
2025-07-02 06:41:20.262 else:
2025-07-02 06:41:20.262 raise TypeError('String value is not'
2025-07-02 06:41:20.262 ' acceptable type for'
2025-07-02 06:41:20.262 ' a non-textual BLOB column.')
2025-07-02 06:41:20.262 blob_buf = create_string_buffer(value)
2025-07-02 06:41:20.262 blob: iBlob = self._connection._att.create_blob(self._transaction._tra,
2025-07-02 06:41:20.262 blobid)
2025-07-02 06:41:20.262 try:
2025-07-02 06:41:20.262 memmove(buf_addr + offset, addressof(blobid), length)
2025-07-02 06:41:20.262 total_size = len(value)
2025-07-02 06:41:20.262 bytes_written_so_far = 0
2025-07-02 06:41:20.262 bytes_to_write_this_time = MAX_BLOB_SEGMENT_SIZE
2025-07-02 06:41:20.262 while bytes_written_so_far < total_size:
2025-07-02 06:41:20.262 if (total_size - bytes_written_so_far) < MAX_BLOB_SEGMENT_SIZE:
2025-07-02 06:41:20.262 bytes_to_write_this_time = (total_size - bytes_written_so_far)
2025-07-02 06:41:20.262 blob.put_segment(bytes_to_write_this_time,
2025-07-02 06:41:20.262 addressof(blob_buf) + bytes_written_so_far)
2025-07-02 06:41:20.262 bytes_written_so_far += bytes_to_write_this_time
2025-07-02 06:41:20.262 finally:
2025-07-02 06:41:20.262 blob.close()
2025-07-02 06:41:20.263 del blob_buf
2025-07-02 06:41:20.263 elif datatype == SQLDataType.ARRAY:
2025-07-02 06:41:20.263 arrayid = a.ISC_QUAD(0, 0)
2025-07-02 06:41:20.263 arrayid_ptr = pointer(arrayid)
2025-07-02 06:41:20.263 arraydesc = a.ISC_ARRAY_DESC(0)
2025-07-02 06:41:20.263 isc_status = a.ISC_STATUS_ARRAY()
2025-07-02 06:41:20.263 db_handle = self._connection._get_handle()
2025-07-02 06:41:20.263 tr_handle = self._transaction._get_handle()
2025-07-02 06:41:20.263 relname = in_meta.get_relation(i).encode(self._encoding)
2025-07-02 06:41:20.263 sqlname = in_meta.get_field(i).encode(self._encoding)
2025-07-02 06:41:20.263 api = a.get_api()
2025-07-02 06:41:20.263 sqlsubtype = self._connection._get_array_sqlsubtype(relname, sqlname)
2025-07-02 06:41:20.263 api.isc_array_lookup_bounds(isc_status, db_handle, tr_handle,
2025-07-02 06:41:20.263 relname, sqlname, arraydesc)
2025-07-02 06:41:20.263 if a.db_api_error(isc_status): # pragma: no cover
2025-07-02 06:41:20.263 raise a.exception_from_status(DatabaseError,
2025-07-02 06:41:20.263 isc_status,
2025-07-02 06:41:20.263 "Error in Cursor._pack_input:isc_array_lookup_bounds()")
2025-07-02 06:41:20.264 value_type = arraydesc.array_desc_dtype
2025-07-02 06:41:20.264 value_scale = arraydesc.array_desc_scale
2025-07-02 06:41:20.264 value_size = arraydesc.array_desc_length
2025-07-02 06:41:20.264 if value_type in (a.blr_varying, a.blr_varying2):
2025-07-02 06:41:20.264 value_size += 2
2025-07-02 06:41:20.264 dimensions = []
2025-07-02 06:41:20.264 total_num_elements = 1
2025-07-02 06:41:20.264 for dimension in range(arraydesc.array_desc_dimensions):
2025-07-02 06:41:20.264 bounds = arraydesc.array_desc_bounds[dimension]
2025-07-02 06:41:20.264 dimensions.append((bounds.array_bound_upper + 1) - bounds.array_bound_lower)
2025-07-02 06:41:20.264 total_num_elements *= dimensions[dimension]
2025-07-02 06:41:20.264 total_size = total_num_elements * value_size
2025-07-02 06:41:20.264 # Validate value to make sure it matches the array structure
2025-07-02 06:41:20.264 if not self._validate_array_value(0, dimensions, value_type,
2025-07-02 06:41:20.264 sqlsubtype, value_scale, value):
2025-07-02 06:41:20.264 > raise ValueError("Incorrect ARRAY field value.")
2025-07-02 06:41:20.264 E ValueError: Incorrect ARRAY field value.
2025-07-02 06:41:20.264
2025-07-02 06:41:20.264 C:\Python3x\Lib\site-packages\firebird\driver\core.py:3541: ValueError
2025-07-02 06:41:20.264 ---------------------------- Captured stdout setup ----------------------------
2025-07-02 06:41:20.265 Creating db: localhost:H:\QA\temp\qa2024.tmp\fbqa\test_11709\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)
C:\Python3x\Lib\site-packages\firebird\driver\core.py:3862:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
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)
C:\Python3x\Lib\site-packages\firebird\driver\core.py:3739:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
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.
C:\Python3x\Lib\site-packages\firebird\driver\core.py:3541: ValueError
|