2 @message |
assert
- Changed ID: -1
+ Dynamic SQL Error
+ -SQL error code = -901
+ -invalid request handle
+ gds codes:
+ 335544569
+ 335544436
+ 335544327
LOG DETAILS:
2024-12-24 23:47:16.927
2024-12-24 23:47:16.931 act = <firebird.qa.plugin.Action object at [hex]>
2024-12-24 23:47:16.937 capsys = <_pytest.capture.CaptureFixture object at [hex]>
2024-12-24 23:47:16.943
2024-12-24 23:47:16.949 @pytest.mark.version('>=5.0.0')
2024-12-24 23:47:16.956 def test_1(act: Action, capsys):
2024-12-24 23:47:16.963
2024-12-24 23:47:16.972 srv_cfg = driver_config.register_server(name = 'test_srv_gh_8185', config = '')
2024-12-24 23:47:16.982 db_cfg_name = f'db_cfg_8185'
2024-12-24 23:47:16.989 db_cfg_object = driver_config.register_database(name = db_cfg_name)
2024-12-24 23:47:16.995 db_cfg_object.server.value = srv_cfg.name
2024-12-24 23:47:17.000 db_cfg_object.database.value = str(act.db.db_path)
2024-12-24 23:47:17.005 db_cfg_object.config.value = f"""
2024-12-24 23:47:17.011 MaxStatementCacheSize = 1M
2024-12-24 23:47:17.020 """
2024-12-24 23:47:17.030
2024-12-24 23:47:17.040 # Pre-check:
2024-12-24 23:47:17.046 with connect(db_cfg_name, user = act.db.user, password = act.db.password) as con:
2024-12-24 23:47:17.053 cur = con.cursor()
2024-12-24 23:47:17.060 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")
2024-12-24 23:47:17.065 for r in cur:
2024-12-24 23:47:17.072 conn_protocol = r[0]
2024-12-24 23:47:17.077 db_sttm_cache_size = int(r[1])
2024-12-24 23:47:17.082 assert conn_protocol is None, "Test must use LOCAL protocol."
2024-12-24 23:47:17.087 assert db_sttm_cache_size > 0, "Parameter 'MaxStatementCacheSize' (per-database) must be greater than zero for this test."
2024-12-24 23:47:17.093
2024-12-24 23:47:17.098 #---------------------------------------------
2024-12-24 23:47:17.104
2024-12-24 23:47:17.110 CURSOR_NAME = 'k1'
2024-12-24 23:47:17.115 SELECT_STTM = 'select /* ps-1*/ id, f01 from test where id > 0 for update'
2024-12-24 23:47:17.120 UPDATE_STTM = f'update /* ps-2 */ test set id = -id where current of {CURSOR_NAME} returning id'
2024-12-24 23:47:17.126
2024-12-24 23:47:17.131 update_tpb = tpb( access_mode = TraAccessMode.WRITE,
2024-12-24 23:47:17.137 isolation = Isolation.READ_COMMITTED_RECORD_VERSION,
2024-12-24 23:47:17.142 lock_timeout = 1)
2024-12-24 23:47:17.147
2024-12-24 23:47:17.152 with connect(db_cfg_name, user = act.db.user, password = act.db.password) as con:
2024-12-24 23:47:17.157
2024-12-24 23:47:17.162 tx2 = con.transaction_manager(update_tpb)
2024-12-24 23:47:17.167 tx2.begin()
2024-12-24 23:47:17.171
2024-12-24 23:47:17.176 with con.cursor() as cur1, tx2.cursor() as cur2, con.cursor() as cur3:
2024-12-24 23:47:17.180
2024-12-24 23:47:17.185 ps1, rs1, ps2, rs2, ps3, rs3 = None, None, None, None, None, None
2024-12-24 23:47:17.190 try:
2024-12-24 23:47:17.195 ps1 = cur1.prepare(SELECT_STTM) # 1. [ticket, DS] Prepare statement 1 "select ... for update"
2024-12-24 23:47:17.202 ps1._istmt.set_cursor_name(CURSOR_NAME) # 2. [ticket, DS] Set cursor name for statement 1 // ~hack.
2024-12-24 23:47:17.210
2024-12-24 23:47:17.215 # DO NOT use it because subsequent update statement will get 'deadlock / update conflict' and not able to start:
2024-12-24 23:47:17.224 #rs1 = cur1.execute(ps1)
2024-12-24 23:47:17.232 #cur1.set_cursor_name(CURSOR_NAME)
2024-12-24 23:47:17.244
2024-12-24 23:47:17.254 # DS example: "// Prepare positioned update statement"
2024-12-24 23:47:17.261 ps2 = cur2.prepare(UPDATE_STTM) # 3. [ticket, DS] Prepare statement 2 "update ... where current of <cursor name from step 2>"
2024-12-24 23:47:17.266
2024-12-24 23:47:17.271 # DS .cpp: // fetch records from cursor and print them
2024-12-24 23:47:17.276 rs1 = cur1.execute(ps1)
2024-12-24 23:47:17.280 rs1.fetchall()
2024-12-24 23:47:17.285
2024-12-24 23:47:17.293 # DS .cpp: // IStatement* stmt2 = att->prepare(&status, tra, 0, "select * from pos where a > 1 for update",
2024-12-24 23:47:17.304 ps3 = cur3.prepare(SELECT_STTM) # 4. [ticket, DS] Prepare statement 3 similar to statement 1
2024-12-24 23:47:17.311
2024-12-24 23:47:17.317 rs1.close() # 5. [ticket, DS] Release statement 1 // see hvlad recipe, 26.10.2024
2024-12-24 23:47:17.322 ps1.free()
2024-12-24 23:47:17.327
2024-12-24 23:47:17.332 # DS .cpp: updStmt->free(&status);
2024-12-24 23:47:17.337 ps2.free() # 6. [ticket, DS] Release statement 2 // see hvlad recipe, 26.10.2024
2024-12-24 23:47:17.343
2024-12-24 23:47:17.350 # DS .cpp: stmt = stmt2
2024-12-24 23:47:17.356 ps3._istmt.set_cursor_name(CURSOR_NAME) # 7. [ticket, DS] Set cursor name to statement 3 as in step 2
2024-12-24 23:47:17.361
2024-12-24 23:47:17.367 ps2 = cur2.prepare(UPDATE_STTM) # 8. [ticket, DS] Prepare statement 2 again (it will be got from cache keeping reference to statement 1)
2024-12-24 23:47:17.372
2024-12-24 23:47:17.377 rs3 = cur3.execute(ps3)
2024-12-24 23:47:17.382 rs3.fetchone() # 9. [ticket, DS] Run statement 3 and fetch one record
2024-12-24 23:47:17.388
2024-12-24 23:47:17.393 # At step 10 you can get "Invalid handle" error or a crash if you swap steps 5 and 6.
2024-12-24 23:47:17.399 rs2 = cur2.execute(ps2) # 10. [ticket, DS] Execute statement 2
2024-12-24 23:47:17.406 data2 = rs2.fetchone()
2024-12-24 23:47:17.413 print('Changed ID:', data2[0])
2024-12-24 23:47:17.419 # print(f'{rs2.rowcount=}')
2024-12-24 23:47:17.426
2024-12-24 23:47:17.431 except DatabaseError as e:
2024-12-24 23:47:17.437 print(e.__str__())
2024-12-24 23:47:17.443 print('gds codes:')
2024-12-24 23:47:17.448 for i in e.gds_codes:
2024-12-24 23:47:17.453 print(i)
2024-12-24 23:47:17.458
2024-12-24 23:47:17.463 finally:
2024-12-24 23:47:17.470 if rs1:
2024-12-24 23:47:17.477 rs1.close()
2024-12-24 23:47:17.483 if ps1:
2024-12-24 23:47:17.489 ps1.free()
2024-12-24 23:47:17.497
2024-12-24 23:47:17.502 if rs2:
2024-12-24 23:47:17.507 rs2.close()
2024-12-24 23:47:17.512 if ps2:
2024-12-24 23:47:17.517 ps2.free()
2024-12-24 23:47:17.522
2024-12-24 23:47:17.527 if rs3:
2024-12-24 23:47:17.533 rs3.close()
2024-12-24 23:47:17.539 if ps3:
2024-12-24 23:47:17.547 ps3.free()
2024-12-24 23:47:17.552
2024-12-24 23:47:17.557 #---------------------------------------------
2024-12-24 23:47:17.563
2024-12-24 23:47:17.569 act.expected_stdout = 'Changed ID: -1'
2024-12-24 23:47:17.575 act.stdout = capsys.readouterr().out
2024-12-24 23:47:17.581 > assert act.clean_stdout == act.clean_expected_stdout
2024-12-24 23:47:17.587 E assert
2024-12-24 23:47:17.593 E - Changed ID: -1
2024-12-24 23:47:17.599 E + Dynamic SQL Error
2024-12-24 23:47:17.605 E + -SQL error code = -901
2024-12-24 23:47:17.611 E + -invalid request handle
2024-12-24 23:47:17.616 E + gds codes:
2024-12-24 23:47:17.625 E + 335544569
2024-12-24 23:47:17.632 E + 335544436
2024-12-24 23:47:17.637 E + 335544327
2024-12-24 23:47:17.641
2024-12-24 23:47:17.646 tests/bugs/gh_8185_test.py:155: AssertionError
2024-12-24 23:47:17.652 ---------------------------- Captured stdout setup -----------------------------
2024-12-24 23:47:17.657 Creating db: localhost:/var/tmp/qa_2024/test_11655/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.0')
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
|