2 @message |
ValueError: Incorrect ARRAY field value.
LOG DETAILS:
2025-07-01 05:54:10.495
2025-07-01 05:54:10.501 act = <firebird.qa.plugin.Action object at [hex]>
2025-07-01 05:54:10.507 capsys = <_pytest.capture.CaptureFixture object at [hex]>
2025-07-01 05:54:10.513
2025-07-01 05:54:10.520 @pytest.mark.version('>=3.0.12')
2025-07-01 05:54:10.525 def test_1(act: Action, capsys):
2025-07-01 05:54:10.531 with act.db.connect() as con:
2025-07-01 05:54:10.537 cur = con.cursor()
2025-07-01 05:54:10.542
2025-07-01 05:54:10.546 arrayIn = [
2025-07-01 05:54:10.551 [1, 2, 3, 4],
2025-07-01 05:54:10.557 [5, 6, 7, 8],
2025-07-01 05:54:10.562 [9,10,11,12]
2025-07-01 05:54:10.570 ]
2025-07-01 05:54:10.580
2025-07-01 05:54:10.590 > cur.execute("insert into array_table values (?)", (arrayIn,))
2025-07-01 05:54:10.601
2025-07-01 05:54:10.610 tests/bugs/gh_7647_test.py:44:
2025-07-01 05:54:10.617 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 05:54:10.623
2025-07-01 05:54:10.630 self = <firebird.driver.core.Cursor object at [hex]>
2025-07-01 05:54:10.640 operation = 'insert into array_table values (?)'
2025-07-01 05:54:10.649 parameters = ([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]],)
2025-07-01 05:54:10.657
2025-07-01 05:54:10.664 def execute(self, operation: Union[str, Statement], parameters: Sequence[Any]=None) -> Cursor:
2025-07-01 05:54:10.670 """Executes SQL command or prepared `Statement`.
2025-07-01 05:54:10.677
2025-07-01 05:54:10.681 Starts new transaction if transaction manager associated with cursor is not active.
2025-07-01 05:54:10.686
2025-07-01 05:54:10.690 Arguments:
2025-07-01 05:54:10.696 operation: SQL command or prepared `Statement`.
2025-07-01 05:54:10.702 parameters: Sequence of parameters. Must contain one entry for each argument
2025-07-01 05:54:10.708 that the operation expects.
2025-07-01 05:54:10.714
2025-07-01 05:54:10.721 Returns:
2025-07-01 05:54:10.728 `self` so call to execute could be used as iterator over returned rows.
2025-07-01 05:54:10.735
2025-07-01 05:54:10.742 Note:
2025-07-01 05:54:10.749 If `operation` is a string with SQL command that is exactly the same as the
2025-07-01 05:54:10.757 last executed command, the internally prepared `Statement` from last execution
2025-07-01 05:54:10.764 is reused.
2025-07-01 05:54:10.771
2025-07-01 05:54:10.779 If cursor is open, it's closed before new statement is executed.
2025-07-01 05:54:10.787 """
2025-07-01 05:54:10.797 > self._execute(operation, parameters)
2025-07-01 05:54:10.806
2025-07-01 05:54:10.817 ../lib/python3.11/site-packages/firebird/driver/core.py:3861:
2025-07-01 05:54:10.826 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 05:54:10.835
2025-07-01 05:54:10.842 self = <firebird.driver.core.Cursor object at [hex]>
2025-07-01 05:54:10.848 operation = 'insert into array_table values (?)'
2025-07-01 05:54:10.853 parameters = ([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]],)
2025-07-01 05:54:10.858 flags = <CursorFlag.NONE: 0>
2025-07-01 05:54:10.863
2025-07-01 05:54:10.869 def _execute(self, operation: Union[str, Statement],
2025-07-01 05:54:10.875 parameters: Sequence=None, flags: CursorFlag=CursorFlag.NONE) -> None:
2025-07-01 05:54:10.881 if not self._transaction.is_active():
2025-07-01 05:54:10.888 self._transaction.begin()
2025-07-01 05:54:10.895 if isinstance(operation, Statement):
2025-07-01 05:54:10.902 if operation._connection() is not self._connection:
2025-07-01 05:54:10.911 raise InterfaceError('Cannot execute Statement that was created by different Connection.')
2025-07-01 05:54:10.919 self.close()
2025-07-01 05:54:10.928 self._stmt = operation
2025-07-01 05:54:10.938 self.__internal = False
2025-07-01 05:54:10.946 elif self._stmt is not None and self._stmt.sql == operation:
2025-07-01 05:54:10.953 # We should execute the same SQL string again
2025-07-01 05:54:10.960 self._clear()
2025-07-01 05:54:10.967 else:
2025-07-01 05:54:10.974 self.close()
2025-07-01 05:54:10.983 self._stmt = self._connection._prepare(operation, self._transaction)
2025-07-01 05:54:10.994 self.__internal = True
2025-07-01 05:54:11.002 self._cursor_flags = flags
2025-07-01 05:54:11.009 in_meta = None
2025-07-01 05:54:11.015 # Execute the statement
2025-07-01 05:54:11.020 try:
2025-07-01 05:54:11.025 if self._stmt._in_cnt > 0:
2025-07-01 05:54:11.030 > in_meta, self._stmt._in_buffer = self._pack_input(self._stmt._in_meta,
2025-07-01 05:54:11.035 self._stmt._in_buffer,
2025-07-01 05:54:11.039 parameters)
2025-07-01 05:54:11.044
2025-07-01 05:54:11.049 ../lib/python3.11/site-packages/firebird/driver/core.py:3738:
2025-07-01 05:54:11.055 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 05:54:11.061
2025-07-01 05:54:11.068 self = <firebird.driver.core.Cursor object at [hex]>
2025-07-01 05:54:11.074 meta = <firebird.driver.interfaces.iMessageMetadata object at [hex]>
2025-07-01 05:54:11.084 buffer = <ctypes.c_char_Array_10 object at [hex]>
2025-07-01 05:54:11.093 parameters = ([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]],)
2025-07-01 05:54:11.100
2025-07-01 05:54:11.106 def _pack_input(self, meta: iMessageMetadata, buffer: bytes,
2025-07-01 05:54:11.111 parameters: Sequence) -> Tuple[iMessageMetadata, bytes]:
2025-07-01 05:54:11.116 # pylint: disable=R1702
2025-07-01 05:54:11.121 in_cnt = meta.get_count()
2025-07-01 05:54:11.126 if len(parameters) != in_cnt:
2025-07-01 05:54:11.132 raise InterfaceError(f"Statement parameter sequence contains"
2025-07-01 05:54:11.140 f" {len(parameters)} items,"
2025-07-01 05:54:11.147 f" but exactly {in_cnt} are required")
2025-07-01 05:54:11.154 #
2025-07-01 05:54:11.165 buf_size = len(buffer)
2025-07-01 05:54:11.173 memset(buffer, 0, buf_size)
2025-07-01 05:54:11.180 # Adjust metadata where needed
2025-07-01 05:54:11.186 with meta.get_builder() as builder:
2025-07-01 05:54:11.192 for i in range(in_cnt):
2025-07-01 05:54:11.198 value = parameters[i]
2025-07-01 05:54:11.205 if _is_str_param(value, meta.get_type(i)):
2025-07-01 05:54:11.211 builder.set_type(i, SQLDataType.TEXT)
2025-07-01 05:54:11.219 if not isinstance(value, (str, bytes, bytearray)):
2025-07-01 05:54:11.226 value = str(value)
2025-07-01 05:54:11.231 builder.set_length(i, len(value.encode(self._encoding)) if isinstance(value, str) else len(value))
2025-07-01 05:54:11.236 in_meta = builder.get_metadata()
2025-07-01 05:54:11.242 new_size = in_meta.get_message_length()
2025-07-01 05:54:11.251 in_buffer = create_string_buffer(new_size) if buf_size < new_size else buffer
2025-07-01 05:54:11.257 buf_addr = addressof(in_buffer)
2025-07-01 05:54:11.266 with in_meta:
2025-07-01 05:54:11.276 for i in range(in_cnt):
2025-07-01 05:54:11.284 value = parameters[i]
2025-07-01 05:54:11.291 datatype = in_meta.get_type(i)
2025-07-01 05:54:11.297 length = in_meta.get_length(i)
2025-07-01 05:54:11.304 offset = in_meta.get_offset(i)
2025-07-01 05:54:11.310 # handle NULL value
2025-07-01 05:54:11.316 in_buffer[in_meta.get_null_offset(i)] = 1 if value is None else 0
2025-07-01 05:54:11.323 if value is None:
2025-07-01 05:54:11.329 continue
2025-07-01 05:54:11.335 # store parameter value
2025-07-01 05:54:11.347 if _is_str_param(value, datatype):
2025-07-01 05:54:11.360 # Implicit conversion to string
2025-07-01 05:54:11.370 if not isinstance(value, (str, bytes, bytearray)):
2025-07-01 05:54:11.377 value = str(value)
2025-07-01 05:54:11.384 if isinstance(value, str) and self._encoding:
2025-07-01 05:54:11.392 value = value.encode(self._encoding)
2025-07-01 05:54:11.397 if (datatype in (SQLDataType.TEXT, SQLDataType.VARYING)
2025-07-01 05:54:11.403 and len(value) > length):
2025-07-01 05:54:11.407 raise ValueError(f"Value of parameter ({i}) is too long,"
2025-07-01 05:54:11.412 f" expected {length}, found {len(value)}")
2025-07-01 05:54:11.417 memmove(buf_addr + offset, value, len(value))
2025-07-01 05:54:11.422 elif datatype in (SQLDataType.SHORT, SQLDataType.LONG, SQLDataType.INT64):
2025-07-01 05:54:11.427 # It's scalled integer?
2025-07-01 05:54:11.433 scale = in_meta.get_scale(i)
2025-07-01 05:54:11.438 if in_meta.get_subtype(i) or scale:
2025-07-01 05:54:11.444 if isinstance(value, decimal.Decimal):
2025-07-01 05:54:11.451 value = int((value * _tenTo[abs(scale)]).to_integral())
2025-07-01 05:54:11.457 elif isinstance(value, (int, float)):
2025-07-01 05:54:11.464 value = int(value * _tenTo[abs(scale)])
2025-07-01 05:54:11.471 else:
2025-07-01 05:54:11.480 raise TypeError(f'Objects of type {type(value)} are not '
2025-07-01 05:54:11.492 f' acceptable input for'
2025-07-01 05:54:11.503 f' a fixed-point column.')
2025-07-01 05:54:11.514 _check_integer_range(value, self._dialect, datatype,
2025-07-01 05:54:11.524 in_meta.get_subtype(i), scale)
2025-07-01 05:54:11.533 memmove(buf_addr + offset, value.to_bytes(length, 'little', signed=True), length)
2025-07-01 05:54:11.542 elif datatype == SQLDataType.DATE:
2025-07-01 05:54:11.554 memmove(buf_addr + offset, _util.encode_date(value).to_bytes(length, 'little', signed=True), length)
2025-07-01 05:54:11.563 elif datatype == SQLDataType.TIME:
2025-07-01 05:54:11.572 memmove(buf_addr + offset, _util.encode_time(value).to_bytes(length, 'little'), length)
2025-07-01 05:54:11.579 elif datatype == SQLDataType.TIME_TZ:
2025-07-01 05:54:11.586 memmove(buf_addr + offset, _util.encode_time_tz(value), length)
2025-07-01 05:54:11.592 elif datatype == SQLDataType.TIMESTAMP:
2025-07-01 05:54:11.604 memmove(buf_addr + offset, _encode_timestamp(value), length)
2025-07-01 05:54:11.613 elif datatype == SQLDataType.TIMESTAMP_TZ:
2025-07-01 05:54:11.621 memmove(buf_addr + offset, _util.encode_timestamp_tz(value), length)
2025-07-01 05:54:11.629 elif datatype == SQLDataType.DEC16:
2025-07-01 05:54:11.636 memmove(buf_addr + offset, byref(_util.get_decfloat16().from_str(str(value))), length)
2025-07-01 05:54:11.642 elif datatype == SQLDataType.DEC34:
2025-07-01 05:54:11.650 memmove(buf_addr + offset, _util.get_decfloat34().from_str(str(value)), length)
2025-07-01 05:54:11.662 elif datatype == SQLDataType.INT128:
2025-07-01 05:54:11.672 memmove(buf_addr + offset, _util.get_int128().from_str(str(value), in_meta.get_scale(i)), length)
2025-07-01 05:54:11.683 elif datatype == SQLDataType.FLOAT:
2025-07-01 05:54:11.693 memmove(buf_addr + offset, struct.pack('f', value), length)
2025-07-01 05:54:11.706 elif datatype == SQLDataType.DOUBLE:
2025-07-01 05:54:11.717 memmove(buf_addr + offset, struct.pack('d', value), length)
2025-07-01 05:54:11.726 elif datatype == SQLDataType.BOOLEAN:
2025-07-01 05:54:11.735 memmove(buf_addr + offset, (1 if value else 0).to_bytes(length, 'little'), length)
2025-07-01 05:54:11.748 elif datatype == SQLDataType.BLOB:
2025-07-01 05:54:11.760 blobid = a.ISC_QUAD(0, 0)
2025-07-01 05:54:11.769 if hasattr(value, 'read'):
2025-07-01 05:54:11.782 # It seems we've got file-like object, use stream BLOB
2025-07-01 05:54:11.791 blob_buf = _create_blob_buffer()
2025-07-01 05:54:11.799 blob: iBlob = self._connection._att.create_blob(self._transaction._tra,
2025-07-01 05:54:11.807 blobid, _bpb_stream)
2025-07-01 05:54:11.817 try:
2025-07-01 05:54:11.830 memmove(buf_addr + offset, addressof(blobid), length)
2025-07-01 05:54:11.840 while value_chunk := value.read(MAX_BLOB_SEGMENT_SIZE):
2025-07-01 05:54:11.853 blob_buf.raw = value_chunk.encode(self._encoding) if isinstance(value_chunk, str) else value_chunk
2025-07-01 05:54:11.862 blob.put_segment(len(value_chunk), blob_buf)
2025-07-01 05:54:11.874 memset(blob_buf, 0, MAX_BLOB_SEGMENT_SIZE)
2025-07-01 05:54:11.884 finally:
2025-07-01 05:54:11.898 blob.close()
2025-07-01 05:54:11.907 del blob_buf
2025-07-01 05:54:11.915 else:
2025-07-01 05:54:11.922 # Non-stream BLOB
2025-07-01 05:54:11.932 if isinstance(value, str):
2025-07-01 05:54:11.944 if in_meta.get_subtype(i) == 1:
2025-07-01 05:54:11.956 value = value.encode(self._encoding)
2025-07-01 05:54:11.964 else:
2025-07-01 05:54:11.971 raise TypeError('String value is not'
2025-07-01 05:54:11.978 ' acceptable type for'
2025-07-01 05:54:11.985 ' a non-textual BLOB column.')
2025-07-01 05:54:11.990 blob_buf = create_string_buffer(value)
2025-07-01 05:54:11.997 blob: iBlob = self._connection._att.create_blob(self._transaction._tra,
2025-07-01 05:54:12.008 blobid)
2025-07-01 05:54:12.019 try:
2025-07-01 05:54:12.028 memmove(buf_addr + offset, addressof(blobid), length)
2025-07-01 05:54:12.034 total_size = len(value)
2025-07-01 05:54:12.040 bytes_written_so_far = 0
2025-07-01 05:54:12.046 bytes_to_write_this_time = MAX_BLOB_SEGMENT_SIZE
2025-07-01 05:54:12.051 while bytes_written_so_far < total_size:
2025-07-01 05:54:12.058 if (total_size - bytes_written_so_far) < MAX_BLOB_SEGMENT_SIZE:
2025-07-01 05:54:12.067 bytes_to_write_this_time = (total_size - bytes_written_so_far)
2025-07-01 05:54:12.073 blob.put_segment(bytes_to_write_this_time,
2025-07-01 05:54:12.079 addressof(blob_buf) + bytes_written_so_far)
2025-07-01 05:54:12.085 bytes_written_so_far += bytes_to_write_this_time
2025-07-01 05:54:12.091 finally:
2025-07-01 05:54:12.097 blob.close()
2025-07-01 05:54:12.106 del blob_buf
2025-07-01 05:54:12.113 elif datatype == SQLDataType.ARRAY:
2025-07-01 05:54:12.119 arrayid = a.ISC_QUAD(0, 0)
2025-07-01 05:54:12.124 arrayid_ptr = pointer(arrayid)
2025-07-01 05:54:12.129 arraydesc = a.ISC_ARRAY_DESC(0)
2025-07-01 05:54:12.134 isc_status = a.ISC_STATUS_ARRAY()
2025-07-01 05:54:12.139 db_handle = self._connection._get_handle()
2025-07-01 05:54:12.146 tr_handle = self._transaction._get_handle()
2025-07-01 05:54:12.150 relname = in_meta.get_relation(i).encode(self._encoding)
2025-07-01 05:54:12.156 sqlname = in_meta.get_field(i).encode(self._encoding)
2025-07-01 05:54:12.162 api = a.get_api()
2025-07-01 05:54:12.168 sqlsubtype = self._connection._get_array_sqlsubtype(relname, sqlname)
2025-07-01 05:54:12.174 api.isc_array_lookup_bounds(isc_status, db_handle, tr_handle,
2025-07-01 05:54:12.182 relname, sqlname, arraydesc)
2025-07-01 05:54:12.192 if a.db_api_error(isc_status): # pragma: no cover
2025-07-01 05:54:12.200 raise a.exception_from_status(DatabaseError,
2025-07-01 05:54:12.206 isc_status,
2025-07-01 05:54:12.212 "Error in Cursor._pack_input:isc_array_lookup_bounds()")
2025-07-01 05:54:12.218 value_type = arraydesc.array_desc_dtype
2025-07-01 05:54:12.225 value_scale = arraydesc.array_desc_scale
2025-07-01 05:54:12.231 value_size = arraydesc.array_desc_length
2025-07-01 05:54:12.239 if value_type in (a.blr_varying, a.blr_varying2):
2025-07-01 05:54:12.250 value_size += 2
2025-07-01 05:54:12.262 dimensions = []
2025-07-01 05:54:12.272 total_num_elements = 1
2025-07-01 05:54:12.282 for dimension in range(arraydesc.array_desc_dimensions):
2025-07-01 05:54:12.288 bounds = arraydesc.array_desc_bounds[dimension]
2025-07-01 05:54:12.296 dimensions.append((bounds.array_bound_upper + 1) - bounds.array_bound_lower)
2025-07-01 05:54:12.304 total_num_elements *= dimensions[dimension]
2025-07-01 05:54:12.316 total_size = total_num_elements * value_size
2025-07-01 05:54:12.324 # Validate value to make sure it matches the array structure
2025-07-01 05:54:12.331 if not self._validate_array_value(0, dimensions, value_type,
2025-07-01 05:54:12.338 sqlsubtype, value_scale, value):
2025-07-01 05:54:12.343 > raise ValueError("Incorrect ARRAY field value.")
2025-07-01 05:54:12.348 E ValueError: Incorrect ARRAY field value.
2025-07-01 05:54:12.352
2025-07-01 05:54:12.357 ../lib/python3.11/site-packages/firebird/driver/core.py:3541: ValueError
2025-07-01 05:54:12.362 ---------------------------- Captured stdout setup -----------------------------
2025-07-01 05:54:12.368 Creating db: localhost:/var/tmp/qa_2024/test_11596/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('>=3.0.12')
def test_1(act: Action, capsys):
with act.db.connect() as con:
cur = con.cursor()
arrayIn = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9,10,11,12]
]
> cur.execute("insert into array_table values (?)", (arrayIn,))
tests/bugs/gh_7647_test.py:44:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <firebird.driver.core.Cursor pytest object at [hex]>
operation = 'insert into array_table values (?)'
parameters = ([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]],)
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 = 'insert into array_table values (?)'
parameters = ([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]],)
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 = ([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]],)
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
|