2 @message |
assert
- Changed ID: -1
+ Dynamic SQL Error
+ -SQL error code = -901
+ -invalid request handle
+ gds codes:
+ 335544569
+ 335544436
+ 335544327
LOG DETAILS:
2025-02-14 14:31:20.340
2025-02-14 14:31:20.356 act = <firebird.qa.plugin.Action object at [hex]>
2025-02-14 14:31:20.370 capsys = <_pytest.capture.CaptureFixture object at [hex]>
2025-02-14 14:31:20.385
2025-02-14 14:31:20.395 @pytest.mark.version('>=5.0.1')
2025-02-14 14:31:20.403 def test_1(act: Action, capsys):
2025-02-14 14:31:20.411
2025-02-14 14:31:20.420 srv_cfg = driver_config.register_server(name = 'test_srv_gh_8185', config = '')
2025-02-14 14:31:20.428 db_cfg_name = f'db_cfg_8185'
2025-02-14 14:31:20.435 db_cfg_object = driver_config.register_database(name = db_cfg_name)
2025-02-14 14:31:20.441 db_cfg_object.server.value = srv_cfg.name
2025-02-14 14:31:20.448 db_cfg_object.database.value = str(act.db.db_path)
2025-02-14 14:31:20.456 db_cfg_object.config.value = f"""
2025-02-14 14:31:20.469 MaxStatementCacheSize = 1M
2025-02-14 14:31:20.480 """
2025-02-14 14:31:20.491
2025-02-14 14:31:20.506 # Pre-check:
2025-02-14 14:31:20.517 with connect(db_cfg_name, user = act.db.user, password = act.db.password) as con:
2025-02-14 14:31:20.531 cur = con.cursor()
2025-02-14 14:31:20.544 cur.execute("select a.mon$remote_protocol, g.rdb$config_value from mon$attachments a left join rdb$config g on g.rdb$config_name = 'MaxStatementCacheSize' where a.mon$attachment_id = current_connection")
2025-02-14 14:31:20.553 for r in cur:
2025-02-14 14:31:20.567 conn_protocol = r[0]
2025-02-14 14:31:20.582 db_sttm_cache_size = int(r[1])
2025-02-14 14:31:20.593 assert conn_protocol is None, "Test must use LOCAL protocol."
2025-02-14 14:31:20.601 assert db_sttm_cache_size > 0, "Parameter 'MaxStatementCacheSize' (per-database) must be greater than zero for this test."
2025-02-14 14:31:20.609
2025-02-14 14:31:20.617 #---------------------------------------------
2025-02-14 14:31:20.624
2025-02-14 14:31:20.630 CURSOR_NAME = 'k1'
2025-02-14 14:31:20.643 SELECT_STTM = 'select /* ps-1*/ id, f01 from test where id > 0 for update'
2025-02-14 14:31:20.655 UPDATE_STTM = f'update /* ps-2 */ test set id = -id where current of {CURSOR_NAME} returning id'
2025-02-14 14:31:20.663
2025-02-14 14:31:20.676 update_tpb = tpb( access_mode = TraAccessMode.WRITE,
2025-02-14 14:31:20.692 isolation = Isolation.READ_COMMITTED_RECORD_VERSION,
2025-02-14 14:31:20.705 lock_timeout = 1)
2025-02-14 14:31:20.716
2025-02-14 14:31:20.724 with connect(db_cfg_name, user = act.db.user, password = act.db.password) as con:
2025-02-14 14:31:20.732
2025-02-14 14:31:20.741 tx2 = con.transaction_manager(update_tpb)
2025-02-14 14:31:20.753 tx2.begin()
2025-02-14 14:31:20.764
2025-02-14 14:31:20.772 with con.cursor() as cur1, tx2.cursor() as cur2, con.cursor() as cur3:
2025-02-14 14:31:20.779
2025-02-14 14:31:20.790 ps1, rs1, ps2, rs2, ps3, rs3 = None, None, None, None, None, None
2025-02-14 14:31:20.804 try:
2025-02-14 14:31:20.816 ps1 = cur1.prepare(SELECT_STTM) # 1. [ticket, DS] Prepare statement 1 "select ... for update"
2025-02-14 14:31:20.825 ps1._istmt.set_cursor_name(CURSOR_NAME) # 2. [ticket, DS] Set cursor name for statement 1 // ~hack.
2025-02-14 14:31:20.841
2025-02-14 14:31:20.856 # DO NOT use it because subsequent update statement will get 'deadlock / update conflict' and not able to start:
2025-02-14 14:31:20.868 #rs1 = cur1.execute(ps1)
2025-02-14 14:31:20.880 #cur1.set_cursor_name(CURSOR_NAME)
2025-02-14 14:31:20.889
2025-02-14 14:31:20.900 # DS example: "// Prepare positioned update statement"
2025-02-14 14:31:20.908 ps2 = cur2.prepare(UPDATE_STTM) # 3. [ticket, DS] Prepare statement 2 "update ... where current of <cursor name from step 2>"
2025-02-14 14:31:20.918
2025-02-14 14:31:20.929 # DS .cpp: // fetch records from cursor and print them
2025-02-14 14:31:20.937 rs1 = cur1.execute(ps1)
2025-02-14 14:31:20.944 rs1.fetchall()
2025-02-14 14:31:20.951
2025-02-14 14:31:20.960 # DS .cpp: // IStatement* stmt2 = att->prepare(&status, tra, 0, "select * from pos where a > 1 for update",
2025-02-14 14:31:20.969 ps3 = cur3.prepare(SELECT_STTM) # 4. [ticket, DS] Prepare statement 3 similar to statement 1
2025-02-14 14:31:20.978
2025-02-14 14:31:20.986 rs1.close() # 5. [ticket, DS] Release statement 1 // see hvlad recipe, 26.10.2024
2025-02-14 14:31:20.996 ps1.free()
2025-02-14 14:31:21.012
2025-02-14 14:31:21.022 # DS .cpp: updStmt->free(&status);
2025-02-14 14:31:21.031 ps2.free() # 6. [ticket, DS] Release statement 2 // see hvlad recipe, 26.10.2024
2025-02-14 14:31:21.038
2025-02-14 14:31:21.046 # DS .cpp: stmt = stmt2
2025-02-14 14:31:21.054 ps3._istmt.set_cursor_name(CURSOR_NAME) # 7. [ticket, DS] Set cursor name to statement 3 as in step 2
2025-02-14 14:31:21.063
2025-02-14 14:31:21.077 ps2 = cur2.prepare(UPDATE_STTM) # 8. [ticket, DS] Prepare statement 2 again (it will be got from cache keeping reference to statement 1)
2025-02-14 14:31:21.092
2025-02-14 14:31:21.103 rs3 = cur3.execute(ps3)
2025-02-14 14:31:21.112 rs3.fetchone() # 9. [ticket, DS] Run statement 3 and fetch one record
2025-02-14 14:31:21.119
2025-02-14 14:31:21.128 # At step 10 you can get "Invalid handle" error or a crash if you swap steps 5 and 6.
2025-02-14 14:31:21.137 rs2 = cur2.execute(ps2) # 10. [ticket, DS] Execute statement 2
2025-02-14 14:31:21.145 data2 = rs2.fetchone()
2025-02-14 14:31:21.154 print('Changed ID:', data2[0])
2025-02-14 14:31:21.165 # print(f'{rs2.rowcount=}')
2025-02-14 14:31:21.174
2025-02-14 14:31:21.182 except DatabaseError as e:
2025-02-14 14:31:21.192 print(e.__str__())
2025-02-14 14:31:21.200 print('gds codes:')
2025-02-14 14:31:21.207 for i in e.gds_codes:
2025-02-14 14:31:21.215 print(i)
2025-02-14 14:31:21.222
2025-02-14 14:31:21.232 finally:
2025-02-14 14:31:21.240 if rs1:
2025-02-14 14:31:21.252 rs1.close()
2025-02-14 14:31:21.265 if ps1:
2025-02-14 14:31:21.280 ps1.free()
2025-02-14 14:31:21.295
2025-02-14 14:31:21.312 if rs2:
2025-02-14 14:31:21.325 rs2.close()
2025-02-14 14:31:21.339 if ps2:
2025-02-14 14:31:21.351 ps2.free()
2025-02-14 14:31:21.364
2025-02-14 14:31:21.382 if rs3:
2025-02-14 14:31:21.397 rs3.close()
2025-02-14 14:31:21.407 if ps3:
2025-02-14 14:31:21.414 ps3.free()
2025-02-14 14:31:21.421
2025-02-14 14:31:21.434 #---------------------------------------------
2025-02-14 14:31:21.445
2025-02-14 14:31:21.455 act.expected_stdout = 'Changed ID: -1'
2025-02-14 14:31:21.463 act.stdout = capsys.readouterr().out
2025-02-14 14:31:21.472 > assert act.clean_stdout == act.clean_expected_stdout
2025-02-14 14:31:21.482 E assert
2025-02-14 14:31:21.490 E - Changed ID: -1
2025-02-14 14:31:21.498 E + Dynamic SQL Error
2025-02-14 14:31:21.506 E + -SQL error code = -901
2025-02-14 14:31:21.516 E + -invalid request handle
2025-02-14 14:31:21.529 E + gds codes:
2025-02-14 14:31:21.538 E + 335544569
2025-02-14 14:31:21.550 E + 335544436
2025-02-14 14:31:21.559 E + 335544327
2025-02-14 14:31:21.567
2025-02-14 14:31:21.576 tests/bugs/gh_8185_test.py:155: AssertionError
2025-02-14 14:31:21.585 ---------------------------- Captured stdout setup -----------------------------
2025-02-14 14:31:21.594 Creating db: localhost:/var/tmp/qa_2024/test_11656/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):
srv_cfg = driver_config.register_server(name = 'test_srv_gh_8185', config = '')
db_cfg_name = f'db_cfg_8185'
db_cfg_object = driver_config.register_database(name = db_cfg_name)
db_cfg_object.server.value = srv_cfg.name
db_cfg_object.database.value = str(act.db.db_path)
db_cfg_object.config.value = f"""
MaxStatementCacheSize = 1M
"""
# Pre-check:
with connect(db_cfg_name, user = act.db.user, password = act.db.password) as con:
cur = con.cursor()
cur.execute("select a.mon$remote_protocol, g.rdb$config_value from mon$attachments a left join rdb$config g on g.rdb$config_name = 'MaxStatementCacheSize' where a.mon$attachment_id = current_connection")
for r in cur:
conn_protocol = r[0]
db_sttm_cache_size = int(r[1])
assert conn_protocol is None, "Test must use LOCAL protocol."
assert db_sttm_cache_size > 0, "Parameter 'MaxStatementCacheSize' (per-database) must be greater than zero for this test."
#---------------------------------------------
CURSOR_NAME = 'k1'
SELECT_STTM = 'select /* ps-1*/ id, f01 from test where id > 0 for update'
UPDATE_STTM = f'update /* ps-2 */ test set id = -id where current of {CURSOR_NAME} returning id'
update_tpb = tpb( access_mode = TraAccessMode.WRITE,
isolation = Isolation.READ_COMMITTED_RECORD_VERSION,
lock_timeout = 1)
with connect(db_cfg_name, user = act.db.user, password = act.db.password) as con:
tx2 = con.transaction_manager(update_tpb)
tx2.begin()
with con.cursor() as cur1, tx2.cursor() as cur2, con.cursor() as cur3:
ps1, rs1, ps2, rs2, ps3, rs3 = None, None, None, None, None, None
try:
ps1 = cur1.prepare(SELECT_STTM) # 1. [ticket, DS] Prepare statement 1 "select ... for update"
ps1._istmt.set_cursor_name(CURSOR_NAME) # 2. [ticket, DS] Set cursor name for statement 1 // ~hack.
# DO NOT use it because subsequent update statement will get 'deadlock / update conflict' and not able to start:
#rs1 = cur1.execute(ps1)
#cur1.set_cursor_name(CURSOR_NAME)
# DS example: "// Prepare positioned update statement"
ps2 = cur2.prepare(UPDATE_STTM) # 3. [ticket, DS] Prepare statement 2 "update ... where current of <cursor name from step 2>"
# DS .cpp: // fetch records from cursor and print them
rs1 = cur1.execute(ps1)
rs1.fetchall()
# DS .cpp: // IStatement* stmt2 = att->prepare(&status, tra, 0, "select * from pos where a > 1 for update",
ps3 = cur3.prepare(SELECT_STTM) # 4. [ticket, DS] Prepare statement 3 similar to statement 1
rs1.close() # 5. [ticket, DS] Release statement 1 // see hvlad recipe, 26.10.2024
ps1.free()
# DS .cpp: updStmt->free(&status);
ps2.free() # 6. [ticket, DS] Release statement 2 // see hvlad recipe, 26.10.2024
# DS .cpp: stmt = stmt2
ps3._istmt.set_cursor_name(CURSOR_NAME) # 7. [ticket, DS] Set cursor name to statement 3 as in step 2
ps2 = cur2.prepare(UPDATE_STTM) # 8. [ticket, DS] Prepare statement 2 again (it will be got from cache keeping reference to statement 1)
rs3 = cur3.execute(ps3)
rs3.fetchone() # 9. [ticket, DS] Run statement 3 and fetch one record
# At step 10 you can get "Invalid handle" error or a crash if you swap steps 5 and 6.
rs2 = cur2.execute(ps2) # 10. [ticket, DS] Execute statement 2
data2 = rs2.fetchone()
print('Changed ID:', data2[0])
# print(f'{rs2.rowcount=}')
except DatabaseError as e:
print(e.__str__())
print('gds codes:')
for i in e.gds_codes:
print(i)
finally:
if rs1:
rs1.close()
if ps1:
ps1.free()
if rs2:
rs2.close()
if ps2:
ps2.free()
if rs3:
rs3.close()
if ps3:
ps3.free()
#---------------------------------------------
act.expected_stdout = 'Changed ID: -1'
act.stdout = capsys.readouterr().out
> assert act.clean_stdout == act.clean_expected_stdout
E assert
E - Changed ID: -1
E + Dynamic SQL Error
E + -SQL error code = -901
E + -invalid request handle
E + gds codes:
E + 335544569
E + 335544436
E + 335544327
tests/bugs/gh_8185_test.py:155: AssertionError
|