2 @message |
ValueError: Incorrect ARRAY field value.
LOG DETAILS:
2025-06-28 05:16:28.492
2025-06-28 05:16:28.498 act = <firebird.qa.plugin.Action object at [hex]>
2025-06-28 05:16:28.504 capsys = <_pytest.capture.CaptureFixture object at [hex]>
2025-06-28 05:16:28.509
2025-06-28 05:16:28.514 @pytest.mark.version('>=3.0.12')
2025-06-28 05:16:28.523 def test_1(act: Action, capsys):
2025-06-28 05:16:28.530 with act.db.connect() as con:
2025-06-28 05:16:28.538 cur = con.cursor()
2025-06-28 05:16:28.545
2025-06-28 05:16:28.552 arrayIn = [
2025-06-28 05:16:28.558 [1, 2, 3, 4],
2025-06-28 05:16:28.564 [5, 6, 7, 8],
2025-06-28 05:16:28.570 [9,10,11,12]
2025-06-28 05:16:28.575 ]
2025-06-28 05:16:28.581
2025-06-28 05:16:28.587 > cur.execute("insert into array_table values (?)", (arrayIn,))
2025-06-28 05:16:28.592
2025-06-28 05:16:28.598 tests/bugs/gh_7647_test.py:44:
2025-06-28 05:16:28.607 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-06-28 05:16:28.618
2025-06-28 05:16:28.629 self = <firebird.driver.core.Cursor object at [hex]>
2025-06-28 05:16:28.641 operation = 'insert into array_table values (?)'
2025-06-28 05:16:28.652 parameters = ([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]],)
2025-06-28 05:16:28.661
2025-06-28 05:16:28.667 def execute(self, operation: Union[str, Statement], parameters: Sequence[Any]=None) -> Cursor:
2025-06-28 05:16:28.673 """Executes SQL command or prepared `Statement`.
2025-06-28 05:16:28.684
2025-06-28 05:16:28.694 Starts new transaction if transaction manager associated with cursor is not active.
2025-06-28 05:16:28.702
2025-06-28 05:16:28.712 Arguments:
2025-06-28 05:16:28.723 operation: SQL command or prepared `Statement`.
2025-06-28 05:16:28.736 parameters: Sequence of parameters. Must contain one entry for each argument
2025-06-28 05:16:28.747 that the operation expects.
2025-06-28 05:16:28.756
2025-06-28 05:16:28.763 Returns:
2025-06-28 05:16:28.770 `self` so call to execute could be used as iterator over returned rows.
2025-06-28 05:16:28.776
2025-06-28 05:16:28.783 Note:
2025-06-28 05:16:28.795 If `operation` is a string with SQL command that is exactly the same as the
2025-06-28 05:16:28.808 last executed command, the internally prepared `Statement` from last execution
2025-06-28 05:16:28.821 is reused.
2025-06-28 05:16:28.834
2025-06-28 05:16:28.846 If cursor is open, it's closed before new statement is executed.
2025-06-28 05:16:28.856 """
2025-06-28 05:16:28.864 > self._execute(operation, parameters)
2025-06-28 05:16:28.871
2025-06-28 05:16:28.878 ../lib/python3.11/site-packages/firebird/driver/core.py:3861:
2025-06-28 05:16:28.887 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-06-28 05:16:28.899
2025-06-28 05:16:28.908 self = <firebird.driver.core.Cursor object at [hex]>
2025-06-28 05:16:28.915 operation = 'insert into array_table values (?)'
2025-06-28 05:16:28.923 parameters = ([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]],)
2025-06-28 05:16:28.931 flags = <CursorFlag.NONE: 0>
2025-06-28 05:16:28.939
2025-06-28 05:16:28.947 def _execute(self, operation: Union[str, Statement],
2025-06-28 05:16:28.953 parameters: Sequence=None, flags: CursorFlag=CursorFlag.NONE) -> None:
2025-06-28 05:16:28.959 if not self._transaction.is_active():
2025-06-28 05:16:28.965 self._transaction.begin()
2025-06-28 05:16:28.971 if isinstance(operation, Statement):
2025-06-28 05:16:28.978 if operation._connection() is not self._connection:
2025-06-28 05:16:28.985 raise InterfaceError('Cannot execute Statement that was created by different Connection.')
2025-06-28 05:16:28.991 self.close()
2025-06-28 05:16:28.997 self._stmt = operation
2025-06-28 05:16:29.011 self.__internal = False
2025-06-28 05:16:29.022 elif self._stmt is not None and self._stmt.sql == operation:
2025-06-28 05:16:29.032 # We should execute the same SQL string again
2025-06-28 05:16:29.039 self._clear()
2025-06-28 05:16:29.046 else:
2025-06-28 05:16:29.052 self.close()
2025-06-28 05:16:29.059 self._stmt = self._connection._prepare(operation, self._transaction)
2025-06-28 05:16:29.066 self.__internal = True
2025-06-28 05:16:29.077 self._cursor_flags = flags
2025-06-28 05:16:29.085 in_meta = None
2025-06-28 05:16:29.093 # Execute the statement
2025-06-28 05:16:29.099 try:
2025-06-28 05:16:29.107 if self._stmt._in_cnt > 0:
2025-06-28 05:16:29.117 > in_meta, self._stmt._in_buffer = self._pack_input(self._stmt._in_meta,
2025-06-28 05:16:29.125 self._stmt._in_buffer,
2025-06-28 05:16:29.133 parameters)
2025-06-28 05:16:29.144
2025-06-28 05:16:29.155 ../lib/python3.11/site-packages/firebird/driver/core.py:3738:
2025-06-28 05:16:29.165 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-06-28 05:16:29.173
2025-06-28 05:16:29.179 self = <firebird.driver.core.Cursor object at [hex]>
2025-06-28 05:16:29.186 meta = <firebird.driver.interfaces.iMessageMetadata object at [hex]>
2025-06-28 05:16:29.192 buffer = <ctypes.c_char_Array_10 object at [hex]>
2025-06-28 05:16:29.205 parameters = ([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]],)
2025-06-28 05:16:29.216
2025-06-28 05:16:29.228 def _pack_input(self, meta: iMessageMetadata, buffer: bytes,
2025-06-28 05:16:29.239 parameters: Sequence) -> Tuple[iMessageMetadata, bytes]:
2025-06-28 05:16:29.249 # pylint: disable=R1702
2025-06-28 05:16:29.261 in_cnt = meta.get_count()
2025-06-28 05:16:29.271 if len(parameters) != in_cnt:
2025-06-28 05:16:29.279 raise InterfaceError(f"Statement parameter sequence contains"
2025-06-28 05:16:29.287 f" {len(parameters)} items,"
2025-06-28 05:16:29.295 f" but exactly {in_cnt} are required")
2025-06-28 05:16:29.305 #
2025-06-28 05:16:29.314 buf_size = len(buffer)
2025-06-28 05:16:29.321 memset(buffer, 0, buf_size)
2025-06-28 05:16:29.327 # Adjust metadata where needed
2025-06-28 05:16:29.333 with meta.get_builder() as builder:
2025-06-28 05:16:29.342 for i in range(in_cnt):
2025-06-28 05:16:29.354 value = parameters[i]
2025-06-28 05:16:29.363 if _is_str_param(value, meta.get_type(i)):
2025-06-28 05:16:29.371 builder.set_type(i, SQLDataType.TEXT)
2025-06-28 05:16:29.379 if not isinstance(value, (str, bytes, bytearray)):
2025-06-28 05:16:29.389 value = str(value)
2025-06-28 05:16:29.397 builder.set_length(i, len(value.encode(self._encoding)) if isinstance(value, str) else len(value))
2025-06-28 05:16:29.403 in_meta = builder.get_metadata()
2025-06-28 05:16:29.409 new_size = in_meta.get_message_length()
2025-06-28 05:16:29.414 in_buffer = create_string_buffer(new_size) if buf_size < new_size else buffer
2025-06-28 05:16:29.419 buf_addr = addressof(in_buffer)
2025-06-28 05:16:29.424 with in_meta:
2025-06-28 05:16:29.429 for i in range(in_cnt):
2025-06-28 05:16:29.434 value = parameters[i]
2025-06-28 05:16:29.440 datatype = in_meta.get_type(i)
2025-06-28 05:16:29.446 length = in_meta.get_length(i)
2025-06-28 05:16:29.452 offset = in_meta.get_offset(i)
2025-06-28 05:16:29.459 # handle NULL value
2025-06-28 05:16:29.469 in_buffer[in_meta.get_null_offset(i)] = 1 if value is None else 0
2025-06-28 05:16:29.477 if value is None:
2025-06-28 05:16:29.483 continue
2025-06-28 05:16:29.490 # store parameter value
2025-06-28 05:16:29.495 if _is_str_param(value, datatype):
2025-06-28 05:16:29.500 # Implicit conversion to string
2025-06-28 05:16:29.505 if not isinstance(value, (str, bytes, bytearray)):
2025-06-28 05:16:29.510 value = str(value)
2025-06-28 05:16:29.516 if isinstance(value, str) and self._encoding:
2025-06-28 05:16:29.527 value = value.encode(self._encoding)
2025-06-28 05:16:29.535 if (datatype in (SQLDataType.TEXT, SQLDataType.VARYING)
2025-06-28 05:16:29.542 and len(value) > length):
2025-06-28 05:16:29.549 raise ValueError(f"Value of parameter ({i}) is too long,"
2025-06-28 05:16:29.557 f" expected {length}, found {len(value)}")
2025-06-28 05:16:29.564 memmove(buf_addr + offset, value, len(value))
2025-06-28 05:16:29.570 elif datatype in (SQLDataType.SHORT, SQLDataType.LONG, SQLDataType.INT64):
2025-06-28 05:16:29.580 # It's scalled integer?
2025-06-28 05:16:29.590 scale = in_meta.get_scale(i)
2025-06-28 05:16:29.597 if in_meta.get_subtype(i) or scale:
2025-06-28 05:16:29.604 if isinstance(value, decimal.Decimal):
2025-06-28 05:16:29.610 value = int((value * _tenTo[abs(scale)]).to_integral())
2025-06-28 05:16:29.620 elif isinstance(value, (int, float)):
2025-06-28 05:16:29.630 value = int(value * _tenTo[abs(scale)])
2025-06-28 05:16:29.639 else:
2025-06-28 05:16:29.650 raise TypeError(f'Objects of type {type(value)} are not '
2025-06-28 05:16:29.660 f' acceptable input for'
2025-06-28 05:16:29.669 f' a fixed-point column.')
2025-06-28 05:16:29.676 _check_integer_range(value, self._dialect, datatype,
2025-06-28 05:16:29.683 in_meta.get_subtype(i), scale)
2025-06-28 05:16:29.689 memmove(buf_addr + offset, value.to_bytes(length, 'little', signed=True), length)
2025-06-28 05:16:29.695 elif datatype == SQLDataType.DATE:
2025-06-28 05:16:29.702 memmove(buf_addr + offset, _util.encode_date(value).to_bytes(length, 'little', signed=True), length)
2025-06-28 05:16:29.712 elif datatype == SQLDataType.TIME:
2025-06-28 05:16:29.721 memmove(buf_addr + offset, _util.encode_time(value).to_bytes(length, 'little'), length)
2025-06-28 05:16:29.728 elif datatype == SQLDataType.TIME_TZ:
2025-06-28 05:16:29.735 memmove(buf_addr + offset, _util.encode_time_tz(value), length)
2025-06-28 05:16:29.743 elif datatype == SQLDataType.TIMESTAMP:
2025-06-28 05:16:29.754 memmove(buf_addr + offset, _encode_timestamp(value), length)
2025-06-28 05:16:29.762 elif datatype == SQLDataType.TIMESTAMP_TZ:
2025-06-28 05:16:29.768 memmove(buf_addr + offset, _util.encode_timestamp_tz(value), length)
2025-06-28 05:16:29.774 elif datatype == SQLDataType.DEC16:
2025-06-28 05:16:29.781 memmove(buf_addr + offset, byref(_util.get_decfloat16().from_str(str(value))), length)
2025-06-28 05:16:29.786 elif datatype == SQLDataType.DEC34:
2025-06-28 05:16:29.792 memmove(buf_addr + offset, _util.get_decfloat34().from_str(str(value)), length)
2025-06-28 05:16:29.798 elif datatype == SQLDataType.INT128:
2025-06-28 05:16:29.804 memmove(buf_addr + offset, _util.get_int128().from_str(str(value), in_meta.get_scale(i)), length)
2025-06-28 05:16:29.810 elif datatype == SQLDataType.FLOAT:
2025-06-28 05:16:29.815 memmove(buf_addr + offset, struct.pack('f', value), length)
2025-06-28 05:16:29.822 elif datatype == SQLDataType.DOUBLE:
2025-06-28 05:16:29.828 memmove(buf_addr + offset, struct.pack('d', value), length)
2025-06-28 05:16:29.834 elif datatype == SQLDataType.BOOLEAN:
2025-06-28 05:16:29.844 memmove(buf_addr + offset, (1 if value else 0).to_bytes(length, 'little'), length)
2025-06-28 05:16:29.854 elif datatype == SQLDataType.BLOB:
2025-06-28 05:16:29.863 blobid = a.ISC_QUAD(0, 0)
2025-06-28 05:16:29.870 if hasattr(value, 'read'):
2025-06-28 05:16:29.877 # It seems we've got file-like object, use stream BLOB
2025-06-28 05:16:29.883 blob_buf = _create_blob_buffer()
2025-06-28 05:16:29.889 blob: iBlob = self._connection._att.create_blob(self._transaction._tra,
2025-06-28 05:16:29.895 blobid, _bpb_stream)
2025-06-28 05:16:29.902 try:
2025-06-28 05:16:29.913 memmove(buf_addr + offset, addressof(blobid), length)
2025-06-28 05:16:29.921 while value_chunk := value.read(MAX_BLOB_SEGMENT_SIZE):
2025-06-28 05:16:29.927 blob_buf.raw = value_chunk.encode(self._encoding) if isinstance(value_chunk, str) else value_chunk
2025-06-28 05:16:29.934 blob.put_segment(len(value_chunk), blob_buf)
2025-06-28 05:16:29.945 memset(blob_buf, 0, MAX_BLOB_SEGMENT_SIZE)
2025-06-28 05:16:29.955 finally:
2025-06-28 05:16:29.963 blob.close()
2025-06-28 05:16:29.970 del blob_buf
2025-06-28 05:16:29.980 else:
2025-06-28 05:16:29.989 # Non-stream BLOB
2025-06-28 05:16:29.997 if isinstance(value, str):
2025-06-28 05:16:30.004 if in_meta.get_subtype(i) == 1:
2025-06-28 05:16:30.011 value = value.encode(self._encoding)
2025-06-28 05:16:30.017 else:
2025-06-28 05:16:30.023 raise TypeError('String value is not'
2025-06-28 05:16:30.029 ' acceptable type for'
2025-06-28 05:16:30.034 ' a non-textual BLOB column.')
2025-06-28 05:16:30.045 blob_buf = create_string_buffer(value)
2025-06-28 05:16:30.055 blob: iBlob = self._connection._att.create_blob(self._transaction._tra,
2025-06-28 05:16:30.063 blobid)
2025-06-28 05:16:30.071 try:
2025-06-28 05:16:30.081 memmove(buf_addr + offset, addressof(blobid), length)
2025-06-28 05:16:30.091 total_size = len(value)
2025-06-28 05:16:30.103 bytes_written_so_far = 0
2025-06-28 05:16:30.115 bytes_to_write_this_time = MAX_BLOB_SEGMENT_SIZE
2025-06-28 05:16:30.124 while bytes_written_so_far < total_size:
2025-06-28 05:16:30.133 if (total_size - bytes_written_so_far) < MAX_BLOB_SEGMENT_SIZE:
2025-06-28 05:16:30.140 bytes_to_write_this_time = (total_size - bytes_written_so_far)
2025-06-28 05:16:30.146 blob.put_segment(bytes_to_write_this_time,
2025-06-28 05:16:30.153 addressof(blob_buf) + bytes_written_so_far)
2025-06-28 05:16:30.158 bytes_written_so_far += bytes_to_write_this_time
2025-06-28 05:16:30.164 finally:
2025-06-28 05:16:30.170 blob.close()
2025-06-28 05:16:30.180 del blob_buf
2025-06-28 05:16:30.191 elif datatype == SQLDataType.ARRAY:
2025-06-28 05:16:30.202 arrayid = a.ISC_QUAD(0, 0)
2025-06-28 05:16:30.212 arrayid_ptr = pointer(arrayid)
2025-06-28 05:16:30.221 arraydesc = a.ISC_ARRAY_DESC(0)
2025-06-28 05:16:30.228 isc_status = a.ISC_STATUS_ARRAY()
2025-06-28 05:16:30.235 db_handle = self._connection._get_handle()
2025-06-28 05:16:30.241 tr_handle = self._transaction._get_handle()
2025-06-28 05:16:30.247 relname = in_meta.get_relation(i).encode(self._encoding)
2025-06-28 05:16:30.255 sqlname = in_meta.get_field(i).encode(self._encoding)
2025-06-28 05:16:30.266 api = a.get_api()
2025-06-28 05:16:30.275 sqlsubtype = self._connection._get_array_sqlsubtype(relname, sqlname)
2025-06-28 05:16:30.282 api.isc_array_lookup_bounds(isc_status, db_handle, tr_handle,
2025-06-28 05:16:30.294 relname, sqlname, arraydesc)
2025-06-28 05:16:30.303 if a.db_api_error(isc_status): # pragma: no cover
2025-06-28 05:16:30.310 raise a.exception_from_status(DatabaseError,
2025-06-28 05:16:30.320 isc_status,
2025-06-28 05:16:30.330 "Error in Cursor._pack_input:isc_array_lookup_bounds()")
2025-06-28 05:16:30.338 value_type = arraydesc.array_desc_dtype
2025-06-28 05:16:30.350 value_scale = arraydesc.array_desc_scale
2025-06-28 05:16:30.359 value_size = arraydesc.array_desc_length
2025-06-28 05:16:30.369 if value_type in (a.blr_varying, a.blr_varying2):
2025-06-28 05:16:30.382 value_size += 2
2025-06-28 05:16:30.391 dimensions = []
2025-06-28 05:16:30.399 total_num_elements = 1
2025-06-28 05:16:30.405 for dimension in range(arraydesc.array_desc_dimensions):
2025-06-28 05:16:30.416 bounds = arraydesc.array_desc_bounds[dimension]
2025-06-28 05:16:30.425 dimensions.append((bounds.array_bound_upper + 1) - bounds.array_bound_lower)
2025-06-28 05:16:30.432 total_num_elements *= dimensions[dimension]
2025-06-28 05:16:30.439 total_size = total_num_elements * value_size
2025-06-28 05:16:30.446 # Validate value to make sure it matches the array structure
2025-06-28 05:16:30.458 if not self._validate_array_value(0, dimensions, value_type,
2025-06-28 05:16:30.466 sqlsubtype, value_scale, value):
2025-06-28 05:16:30.473 > raise ValueError("Incorrect ARRAY field value.")
2025-06-28 05:16:30.478 E ValueError: Incorrect ARRAY field value.
2025-06-28 05:16:30.483
2025-06-28 05:16:30.488 ../lib/python3.11/site-packages/firebird/driver/core.py:3541: ValueError
2025-06-28 05:16:30.493 ---------------------------- Captured stdout setup -----------------------------
2025-06-28 05:16:30.498 Creating db: localhost:/var/tmp/qa_2024/test_11591/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
|