2 @message |
ValueError: Incorrect ARRAY field value.
LOG DETAILS:
2025-06-28 05:16:53.998
2025-06-28 05:16:54.007 act = <firebird.qa.plugin.Action object at [hex]>
2025-06-28 05:16:54.015 capsys = <_pytest.capture.CaptureFixture object at [hex]>
2025-06-28 05:16:54.023
2025-06-28 05:16:54.032 @pytest.mark.version('>=5.0.1')
2025-06-28 05:16:54.041 def test_1(act: Action, capsys):
2025-06-28 05:16:54.049
2025-06-28 05:16:54.056 with act.db.connect() as con:
2025-06-28 05:16:54.063 cur = con.cursor()
2025-06-28 05:16:54.071 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-28 05:16:54.078 con.commit()
2025-06-28 05:16:54.089
2025-06-28 05:16:54.099 data = (
2025-06-28 05:16:54.106 [ [87, 13, 16, 19], [25, 52, 73, 24], [81, 92, 63, 14] ]
2025-06-28 05:16:54.113 ,[ [21, 79, 63, 57], [34, 42, 13, 34], [71, 15, 73, 34] ]
2025-06-28 05:16:54.119 ,[ [31, 33, 55, 47], [17, 22, 33, 14], [91, 21, 93, 24] ]
2025-06-28 05:16:54.124 )
2025-06-28 05:16:54.129
2025-06-28 05:16:54.134 ps = cur.prepare("insert into array_table(arr) values (?)")
2025-06-28 05:16:54.138 for x in data:
2025-06-28 05:16:54.143 > cur.execute(ps, (x,))
2025-06-28 05:16:54.147
2025-06-28 05:16:54.152 tests/bugs/gh_8100_test.py:40:
2025-06-28 05:16:54.162 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-06-28 05:16:54.168
2025-06-28 05:16:54.174 self = <firebird.driver.core.Cursor object at [hex]>
2025-06-28 05:16:54.180 operation = Statement[insert into array_table(arr) values (?)]
2025-06-28 05:16:54.187 parameters = ([[87, 13, 16, 19], [25, 52, 73, 24], [81, 92, 63, 14]],)
2025-06-28 05:16:54.193
2025-06-28 05:16:54.200 def execute(self, operation: Union[str, Statement], parameters: Sequence[Any]=None) -> Cursor:
2025-06-28 05:16:54.207 """Executes SQL command or prepared `Statement`.
2025-06-28 05:16:54.214
2025-06-28 05:16:54.226 Starts new transaction if transaction manager associated with cursor is not active.
2025-06-28 05:16:54.234
2025-06-28 05:16:54.240 Arguments:
2025-06-28 05:16:54.246 operation: SQL command or prepared `Statement`.
2025-06-28 05:16:54.252 parameters: Sequence of parameters. Must contain one entry for each argument
2025-06-28 05:16:54.259 that the operation expects.
2025-06-28 05:16:54.270
2025-06-28 05:16:54.279 Returns:
2025-06-28 05:16:54.286 `self` so call to execute could be used as iterator over returned rows.
2025-06-28 05:16:54.292
2025-06-28 05:16:54.298 Note:
2025-06-28 05:16:54.305 If `operation` is a string with SQL command that is exactly the same as the
2025-06-28 05:16:54.311 last executed command, the internally prepared `Statement` from last execution
2025-06-28 05:16:54.316 is reused.
2025-06-28 05:16:54.322
2025-06-28 05:16:54.328 If cursor is open, it's closed before new statement is executed.
2025-06-28 05:16:54.340 """
2025-06-28 05:16:54.348 > self._execute(operation, parameters)
2025-06-28 05:16:54.355
2025-06-28 05:16:54.362 ../lib/python3.11/site-packages/firebird/driver/core.py:3861:
2025-06-28 05:16:54.370 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-06-28 05:16:54.382
2025-06-28 05:16:54.391 self = <firebird.driver.core.Cursor object at [hex]>
2025-06-28 05:16:54.399 operation = Statement[insert into array_table(arr) values (?)]
2025-06-28 05:16:54.407 parameters = ([[87, 13, 16, 19], [25, 52, 73, 24], [81, 92, 63, 14]],)
2025-06-28 05:16:54.417 flags = <CursorFlag.NONE: 0>
2025-06-28 05:16:54.425
2025-06-28 05:16:54.432 def _execute(self, operation: Union[str, Statement],
2025-06-28 05:16:54.439 parameters: Sequence=None, flags: CursorFlag=CursorFlag.NONE) -> None:
2025-06-28 05:16:54.446 if not self._transaction.is_active():
2025-06-28 05:16:54.452 self._transaction.begin()
2025-06-28 05:16:54.461 if isinstance(operation, Statement):
2025-06-28 05:16:54.469 if operation._connection() is not self._connection:
2025-06-28 05:16:54.476 raise InterfaceError('Cannot execute Statement that was created by different Connection.')
2025-06-28 05:16:54.482 self.close()
2025-06-28 05:16:54.487 self._stmt = operation
2025-06-28 05:16:54.493 self.__internal = False
2025-06-28 05:16:54.498 elif self._stmt is not None and self._stmt.sql == operation:
2025-06-28 05:16:54.502 # We should execute the same SQL string again
2025-06-28 05:16:54.507 self._clear()
2025-06-28 05:16:54.512 else:
2025-06-28 05:16:54.517 self.close()
2025-06-28 05:16:54.523 self._stmt = self._connection._prepare(operation, self._transaction)
2025-06-28 05:16:54.531 self.__internal = True
2025-06-28 05:16:54.538 self._cursor_flags = flags
2025-06-28 05:16:54.547 in_meta = None
2025-06-28 05:16:54.559 # Execute the statement
2025-06-28 05:16:54.569 try:
2025-06-28 05:16:54.577 if self._stmt._in_cnt > 0:
2025-06-28 05:16:54.589 > in_meta, self._stmt._in_buffer = self._pack_input(self._stmt._in_meta,
2025-06-28 05:16:54.598 self._stmt._in_buffer,
2025-06-28 05:16:54.604 parameters)
2025-06-28 05:16:54.610
2025-06-28 05:16:54.617 ../lib/python3.11/site-packages/firebird/driver/core.py:3738:
2025-06-28 05:16:54.623 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-06-28 05:16:54.630
2025-06-28 05:16:54.636 self = <firebird.driver.core.Cursor object at [hex]>
2025-06-28 05:16:54.644 meta = <firebird.driver.interfaces.iMessageMetadata object at [hex]>
2025-06-28 05:16:54.651 buffer = <ctypes.c_char_Array_10 object at [hex]>
2025-06-28 05:16:54.658 parameters = ([[87, 13, 16, 19], [25, 52, 73, 24], [81, 92, 63, 14]],)
2025-06-28 05:16:54.665
2025-06-28 05:16:54.672 def _pack_input(self, meta: iMessageMetadata, buffer: bytes,
2025-06-28 05:16:54.678 parameters: Sequence) -> Tuple[iMessageMetadata, bytes]:
2025-06-28 05:16:54.683 # pylint: disable=R1702
2025-06-28 05:16:54.688 in_cnt = meta.get_count()
2025-06-28 05:16:54.697 if len(parameters) != in_cnt:
2025-06-28 05:16:54.702 raise InterfaceError(f"Statement parameter sequence contains"
2025-06-28 05:16:54.708 f" {len(parameters)} items,"
2025-06-28 05:16:54.714 f" but exactly {in_cnt} are required")
2025-06-28 05:16:54.719 #
2025-06-28 05:16:54.725 buf_size = len(buffer)
2025-06-28 05:16:54.731 memset(buffer, 0, buf_size)
2025-06-28 05:16:54.736 # Adjust metadata where needed
2025-06-28 05:16:54.741 with meta.get_builder() as builder:
2025-06-28 05:16:54.746 for i in range(in_cnt):
2025-06-28 05:16:54.751 value = parameters[i]
2025-06-28 05:16:54.756 if _is_str_param(value, meta.get_type(i)):
2025-06-28 05:16:54.761 builder.set_type(i, SQLDataType.TEXT)
2025-06-28 05:16:54.766 if not isinstance(value, (str, bytes, bytearray)):
2025-06-28 05:16:54.772 value = str(value)
2025-06-28 05:16:54.778 builder.set_length(i, len(value.encode(self._encoding)) if isinstance(value, str) else len(value))
2025-06-28 05:16:54.783 in_meta = builder.get_metadata()
2025-06-28 05:16:54.794 new_size = in_meta.get_message_length()
2025-06-28 05:16:54.801 in_buffer = create_string_buffer(new_size) if buf_size < new_size else buffer
2025-06-28 05:16:54.807 buf_addr = addressof(in_buffer)
2025-06-28 05:16:54.813 with in_meta:
2025-06-28 05:16:54.820 for i in range(in_cnt):
2025-06-28 05:16:54.826 value = parameters[i]
2025-06-28 05:16:54.832 datatype = in_meta.get_type(i)
2025-06-28 05:16:54.838 length = in_meta.get_length(i)
2025-06-28 05:16:54.847 offset = in_meta.get_offset(i)
2025-06-28 05:16:54.857 # handle NULL value
2025-06-28 05:16:54.867 in_buffer[in_meta.get_null_offset(i)] = 1 if value is None else 0
2025-06-28 05:16:54.874 if value is None:
2025-06-28 05:16:54.881 continue
2025-06-28 05:16:54.887 # store parameter value
2025-06-28 05:16:54.894 if _is_str_param(value, datatype):
2025-06-28 05:16:54.901 # Implicit conversion to string
2025-06-28 05:16:54.908 if not isinstance(value, (str, bytes, bytearray)):
2025-06-28 05:16:54.918 value = str(value)
2025-06-28 05:16:54.929 if isinstance(value, str) and self._encoding:
2025-06-28 05:16:54.941 value = value.encode(self._encoding)
2025-06-28 05:16:54.950 if (datatype in (SQLDataType.TEXT, SQLDataType.VARYING)
2025-06-28 05:16:54.957 and len(value) > length):
2025-06-28 05:16:54.962 raise ValueError(f"Value of parameter ({i}) is too long,"
2025-06-28 05:16:54.966 f" expected {length}, found {len(value)}")
2025-06-28 05:16:54.971 memmove(buf_addr + offset, value, len(value))
2025-06-28 05:16:54.976 elif datatype in (SQLDataType.SHORT, SQLDataType.LONG, SQLDataType.INT64):
2025-06-28 05:16:54.981 # It's scalled integer?
2025-06-28 05:16:54.986 scale = in_meta.get_scale(i)
2025-06-28 05:16:54.991 if in_meta.get_subtype(i) or scale:
2025-06-28 05:16:54.996 if isinstance(value, decimal.Decimal):
2025-06-28 05:16:55.000 value = int((value * _tenTo[abs(scale)]).to_integral())
2025-06-28 05:16:55.005 elif isinstance(value, (int, float)):
2025-06-28 05:16:55.011 value = int(value * _tenTo[abs(scale)])
2025-06-28 05:16:55.016 else:
2025-06-28 05:16:55.021 raise TypeError(f'Objects of type {type(value)} are not '
2025-06-28 05:16:55.026 f' acceptable input for'
2025-06-28 05:16:55.031 f' a fixed-point column.')
2025-06-28 05:16:55.037 _check_integer_range(value, self._dialect, datatype,
2025-06-28 05:16:55.042 in_meta.get_subtype(i), scale)
2025-06-28 05:16:55.051 memmove(buf_addr + offset, value.to_bytes(length, 'little', signed=True), length)
2025-06-28 05:16:55.061 elif datatype == SQLDataType.DATE:
2025-06-28 05:16:55.070 memmove(buf_addr + offset, _util.encode_date(value).to_bytes(length, 'little', signed=True), length)
2025-06-28 05:16:55.076 elif datatype == SQLDataType.TIME:
2025-06-28 05:16:55.082 memmove(buf_addr + offset, _util.encode_time(value).to_bytes(length, 'little'), length)
2025-06-28 05:16:55.087 elif datatype == SQLDataType.TIME_TZ:
2025-06-28 05:16:55.093 memmove(buf_addr + offset, _util.encode_time_tz(value), length)
2025-06-28 05:16:55.098 elif datatype == SQLDataType.TIMESTAMP:
2025-06-28 05:16:55.106 memmove(buf_addr + offset, _encode_timestamp(value), length)
2025-06-28 05:16:55.113 elif datatype == SQLDataType.TIMESTAMP_TZ:
2025-06-28 05:16:55.124 memmove(buf_addr + offset, _util.encode_timestamp_tz(value), length)
2025-06-28 05:16:55.134 elif datatype == SQLDataType.DEC16:
2025-06-28 05:16:55.145 memmove(buf_addr + offset, byref(_util.get_decfloat16().from_str(str(value))), length)
2025-06-28 05:16:55.155 elif datatype == SQLDataType.DEC34:
2025-06-28 05:16:55.166 memmove(buf_addr + offset, _util.get_decfloat34().from_str(str(value)), length)
2025-06-28 05:16:55.178 elif datatype == SQLDataType.INT128:
2025-06-28 05:16:55.188 memmove(buf_addr + offset, _util.get_int128().from_str(str(value), in_meta.get_scale(i)), length)
2025-06-28 05:16:55.197 elif datatype == SQLDataType.FLOAT:
2025-06-28 05:16:55.205 memmove(buf_addr + offset, struct.pack('f', value), length)
2025-06-28 05:16:55.212 elif datatype == SQLDataType.DOUBLE:
2025-06-28 05:16:55.218 memmove(buf_addr + offset, struct.pack('d', value), length)
2025-06-28 05:16:55.225 elif datatype == SQLDataType.BOOLEAN:
2025-06-28 05:16:55.236 memmove(buf_addr + offset, (1 if value else 0).to_bytes(length, 'little'), length)
2025-06-28 05:16:55.247 elif datatype == SQLDataType.BLOB:
2025-06-28 05:16:55.257 blobid = a.ISC_QUAD(0, 0)
2025-06-28 05:16:55.269 if hasattr(value, 'read'):
2025-06-28 05:16:55.279 # It seems we've got file-like object, use stream BLOB
2025-06-28 05:16:55.291 blob_buf = _create_blob_buffer()
2025-06-28 05:16:55.301 blob: iBlob = self._connection._att.create_blob(self._transaction._tra,
2025-06-28 05:16:55.313 blobid, _bpb_stream)
2025-06-28 05:16:55.322 try:
2025-06-28 05:16:55.331 memmove(buf_addr + offset, addressof(blobid), length)
2025-06-28 05:16:55.343 while value_chunk := value.read(MAX_BLOB_SEGMENT_SIZE):
2025-06-28 05:16:55.353 blob_buf.raw = value_chunk.encode(self._encoding) if isinstance(value_chunk, str) else value_chunk
2025-06-28 05:16:55.363 blob.put_segment(len(value_chunk), blob_buf)
2025-06-28 05:16:55.374 memset(blob_buf, 0, MAX_BLOB_SEGMENT_SIZE)
2025-06-28 05:16:55.385 finally:
2025-06-28 05:16:55.397 blob.close()
2025-06-28 05:16:55.408 del blob_buf
2025-06-28 05:16:55.417 else:
2025-06-28 05:16:55.425 # Non-stream BLOB
2025-06-28 05:16:55.432 if isinstance(value, str):
2025-06-28 05:16:55.438 if in_meta.get_subtype(i) == 1:
2025-06-28 05:16:55.449 value = value.encode(self._encoding)
2025-06-28 05:16:55.456 else:
2025-06-28 05:16:55.463 raise TypeError('String value is not'
2025-06-28 05:16:55.475 ' acceptable type for'
2025-06-28 05:16:55.482 ' a non-textual BLOB column.')
2025-06-28 05:16:55.491 blob_buf = create_string_buffer(value)
2025-06-28 05:16:55.505 blob: iBlob = self._connection._att.create_blob(self._transaction._tra,
2025-06-28 05:16:55.514 blobid)
2025-06-28 05:16:55.525 try:
2025-06-28 05:16:55.535 memmove(buf_addr + offset, addressof(blobid), length)
2025-06-28 05:16:55.543 total_size = len(value)
2025-06-28 05:16:55.550 bytes_written_so_far = 0
2025-06-28 05:16:55.561 bytes_to_write_this_time = MAX_BLOB_SEGMENT_SIZE
2025-06-28 05:16:55.570 while bytes_written_so_far < total_size:
2025-06-28 05:16:55.579 if (total_size - bytes_written_so_far) < MAX_BLOB_SEGMENT_SIZE:
2025-06-28 05:16:55.589 bytes_to_write_this_time = (total_size - bytes_written_so_far)
2025-06-28 05:16:55.598 blob.put_segment(bytes_to_write_this_time,
2025-06-28 05:16:55.608 addressof(blob_buf) + bytes_written_so_far)
2025-06-28 05:16:55.619 bytes_written_so_far += bytes_to_write_this_time
2025-06-28 05:16:55.631 finally:
2025-06-28 05:16:55.645 blob.close()
2025-06-28 05:16:55.653 del blob_buf
2025-06-28 05:16:55.661 elif datatype == SQLDataType.ARRAY:
2025-06-28 05:16:55.668 arrayid = a.ISC_QUAD(0, 0)
2025-06-28 05:16:55.675 arrayid_ptr = pointer(arrayid)
2025-06-28 05:16:55.680 arraydesc = a.ISC_ARRAY_DESC(0)
2025-06-28 05:16:55.686 isc_status = a.ISC_STATUS_ARRAY()
2025-06-28 05:16:55.692 db_handle = self._connection._get_handle()
2025-06-28 05:16:55.698 tr_handle = self._transaction._get_handle()
2025-06-28 05:16:55.709 relname = in_meta.get_relation(i).encode(self._encoding)
2025-06-28 05:16:55.718 sqlname = in_meta.get_field(i).encode(self._encoding)
2025-06-28 05:16:55.725 api = a.get_api()
2025-06-28 05:16:55.732 sqlsubtype = self._connection._get_array_sqlsubtype(relname, sqlname)
2025-06-28 05:16:55.738 api.isc_array_lookup_bounds(isc_status, db_handle, tr_handle,
2025-06-28 05:16:55.744 relname, sqlname, arraydesc)
2025-06-28 05:16:55.750 if a.db_api_error(isc_status): # pragma: no cover
2025-06-28 05:16:55.760 raise a.exception_from_status(DatabaseError,
2025-06-28 05:16:55.769 isc_status,
2025-06-28 05:16:55.778 "Error in Cursor._pack_input:isc_array_lookup_bounds()")
2025-06-28 05:16:55.786 value_type = arraydesc.array_desc_dtype
2025-06-28 05:16:55.794 value_scale = arraydesc.array_desc_scale
2025-06-28 05:16:55.801 value_size = arraydesc.array_desc_length
2025-06-28 05:16:55.812 if value_type in (a.blr_varying, a.blr_varying2):
2025-06-28 05:16:55.823 value_size += 2
2025-06-28 05:16:55.832 dimensions = []
2025-06-28 05:16:55.839 total_num_elements = 1
2025-06-28 05:16:55.845 for dimension in range(arraydesc.array_desc_dimensions):
2025-06-28 05:16:55.858 bounds = arraydesc.array_desc_bounds[dimension]
2025-06-28 05:16:55.867 dimensions.append((bounds.array_bound_upper + 1) - bounds.array_bound_lower)
2025-06-28 05:16:55.874 total_num_elements *= dimensions[dimension]
2025-06-28 05:16:55.883 total_size = total_num_elements * value_size
2025-06-28 05:16:55.892 # Validate value to make sure it matches the array structure
2025-06-28 05:16:55.900 if not self._validate_array_value(0, dimensions, value_type,
2025-06-28 05:16:55.906 sqlsubtype, value_scale, value):
2025-06-28 05:16:55.911 > raise ValueError("Incorrect ARRAY field value.")
2025-06-28 05:16:55.916 E ValueError: Incorrect ARRAY field value.
2025-06-28 05:16:55.924
2025-06-28 05:16:55.935 ../lib/python3.11/site-packages/firebird/driver/core.py:3541: ValueError
2025-06-28 05:16:55.942 ---------------------------- Captured stdout setup -----------------------------
2025-06-28 05:16:55.951 Creating db: localhost:/var/tmp/qa_2024/test_11681/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
|