From 3d2386af2bf83b150d83ba4d0dc31249bf17de03 Mon Sep 17 00:00:00 2001 From: Victor Seva Date: Thu, 5 Mar 2026 22:22:19 +0100 Subject: [PATCH 01/20] ioutils: result rows act like named tuples > https://docs.sqlalchemy.org/en/14/changelog/migration_20.html#result-rows-act-like-named-tuples --- kamcli/ioutils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kamcli/ioutils.py b/kamcli/ioutils.py index 514b5ec..4fd9c36 100644 --- a/kamcli/ioutils.py +++ b/kamcli/ioutils.py @@ -50,18 +50,18 @@ def ioutils_dbres_print(ctx, oformat, ostyle, res): if oformat == "json": jdata = [] - for row in res: + for row in res.mappings(): jdata.append(dict(row)) print(json.dumps(jdata, indent=4)) print() elif oformat == "yaml": ydata = [] - for row in res: + for row in res.mappings(): ydata.append(dict(row)) print(yaml.dump(ydata, indent=4)) print() elif oformat == "dict": - for row in res: + for row in res.mappings(): print(dict(row)) # pprint.pprint(dict(row), indent=4) print() From d09551946975222e16f4d7177dec26aeab15f8a2 Mon Sep 17 00:00:00 2001 From: Victor Seva Date: Thu, 5 Mar 2026 10:36:11 +0100 Subject: [PATCH 02/20] cmd_acc: remove implicit execution --- kamcli/commands/cmd_acc.py | 88 +++++++++++++++++++++++++------------- kamcli/dbutils.py | 4 +- 2 files changed, 61 insertions(+), 31 deletions(-) diff --git a/kamcli/commands/cmd_acc.py b/kamcli/commands/cmd_acc.py index 273a42a..4755112 100644 --- a/kamcli/commands/cmd_acc.py +++ b/kamcli/commands/cmd_acc.py @@ -16,7 +16,7 @@ def cli(ctx): pass -def acc_acc_struct_update_exec(ctx, e): +def acc_acc_struct_update_exec(ctx, c): sqltext = """ ALTER TABLE acc ADD COLUMN src_user VARCHAR(64) NOT NULL DEFAULT ''; ALTER TABLE acc ADD COLUMN src_domain VARCHAR(128) NOT NULL DEFAULT ''; @@ -26,7 +26,7 @@ def acc_acc_struct_update_exec(ctx, e): ALTER TABLE acc ADD COLUMN dst_domain VARCHAR(128) NOT NULL DEFAULT ''; ALTER TABLE acc ADD COLUMN cdr_id INTEGER NOT NULL DEFAULT 0; """ - dbutils_exec_sqltext(ctx, e, sqltext) + dbutils_exec_sqltext(ctx, c, sqltext) @cli.command( @@ -38,10 +38,12 @@ def acc_acc_struct_update(ctx): """Run SQL statements to update acc table structure""" ctx.vlog("Run statements to update acc table structure") e = create_engine(ctx.gconfig.get("db", "rwurl")) - acc_acc_struct_update_exec(ctx, e) + with e.connect() as c: + acc_acc_struct_update_exec(ctx, e) + c.commit() -def acc_acc_struct_reset_exec(ctx, e): +def acc_acc_struct_reset_exec(ctx, c): sqltext = """ ALTER TABLE acc DROP COLUMN src_user; ALTER TABLE acc DROP COLUMN src_domain; @@ -51,7 +53,7 @@ def acc_acc_struct_reset_exec(ctx, e): ALTER TABLE acc DROP COLUMN dst_domain; ALTER TABLE acc DROP COLUMN cdr_id; """ - dbutils_exec_sqltext(ctx, e, sqltext) + dbutils_exec_sqltext(ctx, c, sqltext) @cli.command( @@ -63,10 +65,12 @@ def acc_acc_struct_reset(ctx): """Run SQL statements to reset acc table structure""" ctx.vlog("Run statements to reset acc table structure") e = create_engine(ctx.gconfig.get("db", "rwurl")) - acc_acc_struct_reset_exec(ctx, e) + with e.connect() as c: + acc_acc_struct_reset_exec(ctx, c) + c.commit() -def acc_mc_struct_update_exec(ctx, e): +def acc_mc_struct_update_exec(ctx, c): sqltext = """ ALTER TABLE missed_calls ADD COLUMN src_user VARCHAR(64) NOT NULL DEFAULT ''; ALTER TABLE missed_calls ADD COLUMN src_domain VARCHAR(128) NOT NULL DEFAULT ''; @@ -76,7 +80,7 @@ def acc_mc_struct_update_exec(ctx, e): ALTER TABLE missed_calls ADD COLUMN dst_domain VARCHAR(128) NOT NULL DEFAULT ''; ALTER TABLE missed_calls ADD COLUMN cdr_id INTEGER NOT NULL DEFAULT 0; """ - dbutils_exec_sqltext(ctx, e, sqltext) + dbutils_exec_sqltext(ctx, c, sqltext) @cli.command( @@ -88,10 +92,12 @@ def acc_mc_struct_update(ctx): """Run SQL statements to update missed_calls table structure""" ctx.vlog("Run statements to update missed_calls table structure") e = create_engine(ctx.gconfig.get("db", "rwurl")) - acc_mc_struct_update_exec(ctx, e) + with e.connect() as c: + acc_mc_struct_update_exec(ctx, c) + c.commit() -def acc_mc_struct_reset_exec(ctx, e): +def acc_mc_struct_reset_exec(ctx, c): sqltext = """ ALTER TABLE missed_calls DROP COLUMN src_user; ALTER TABLE missed_calls DROP COLUMN src_domain; @@ -101,7 +107,7 @@ def acc_mc_struct_reset_exec(ctx, e): ALTER TABLE missed_calls DROP COLUMN dst_domain; ALTER TABLE missed_calls DROP COLUMN cdr_id; """ - dbutils_exec_sqltext(ctx, e, sqltext) + dbutils_exec_sqltext(ctx, c, sqltext) @cli.command( @@ -113,7 +119,9 @@ def acc_mc_struct_reset(ctx): """Run SQL statements to reset missed_calls table structure""" ctx.vlog("Run statements to reset missed_calls table structure") e = create_engine(ctx.gconfig.get("db", "rwurl")) - acc_mc_struct_reset_exec(ctx, e) + with e.connect() as c: + acc_mc_struct_reset_exec(ctx, c) + c.commit() @cli.command( @@ -125,8 +133,10 @@ def acc_tables_struct_update(ctx): """Run SQL statements to update acc and missed_calls tables structures""" ctx.vlog("Run statements to update acc and missed_calls tables structures") e = create_engine(ctx.gconfig.get("db", "rwurl")) - acc_acc_struct_update_exec(ctx, e) - acc_mc_struct_update_exec(ctx, e) + with e.connect() as c: + acc_acc_struct_update_exec(ctx, c) + acc_mc_struct_update_exec(ctx, c) + c.commit() @cli.command( @@ -159,7 +169,9 @@ def acc_cdrs_table_create(ctx): UNIQUE KEY `uk_cft` (`sip_call_id`,`sip_from_tag`,`sip_to_tag`) ); """ - e.execute(sqltext) + with e.connect() as c: + c.execute(text(sqltext)) + c.commit() @cli.command( @@ -212,7 +224,9 @@ def acc_cdrs_proc_create(ctx): UNTIL done END REPEAT; END """ - e.execute(sqltext) + with e.connect() as c: + c.execute(text(sqltext)) + c.commit() @cli.command( @@ -235,7 +249,9 @@ def acc_rates_table_create(ctx): UNIQUE KEY `uk_rp` (`rate_group`,`prefix`) ); """ - e.execute(sqltext) + with e.connect() as c: + c.execute(text(sqltext)) + c.commit() @cli.command( @@ -278,7 +294,8 @@ def acc_list(ctx, oformat, ostyle, limit): query = "select * from acc order by id desc" else: query = "select * from acc order by id desc limit {0}".format(limit) - res = e.execute(query) + with e.connect() as c: + res = c.execute(text(query)) ioutils_dbres_print(ctx, oformat, ostyle, res) @@ -324,7 +341,8 @@ def acc_mc_list(ctx, oformat, ostyle, limit): query = "select * from missed_calls order by id desc limit {0}".format( limit ) - res = e.execute(query) + with e.connect() as c: + res = c.execute(text(query)) ioutils_dbres_print(ctx, oformat, ostyle, res) @@ -339,7 +357,7 @@ def acc_cdrs_generate(ctx): e = create_engine(ctx.gconfig.get("db", "rwurl")) with e.connect() as c: t = c.begin() - c.execute("call kamailio_cdrs()") + c.execute(text("call kamailio_cdrs()")) t.commit() @@ -385,7 +403,8 @@ def acc_cdrs_list(ctx, oformat, ostyle, limit): query = "select * from cdrs order by cdr_id desc limit {0}".format( limit ) - res = e.execute(query) + with e.connect() as c: + res = c.execute(text(query)) ioutils_dbres_print(ctx, oformat, ostyle, res) @@ -421,12 +440,15 @@ def acc_rates_add(ctx, dbtname, rate_group, prefix, rate_unit, time_unit): v_dbtname = dbtname.encode("ascii", "ignore").decode() v_rate_group = rate_group.encode("ascii", "ignore").decode() v_prefix = prefix.encode("ascii", "ignore").decode() - e.execute( + sqltext = ( "insert into {0} (rate_group, prefix, rate_unit, time_unit) values " "({1!r}, {2!r}, {3}, {4})".format( v_dbtname, v_rate_group, v_prefix, rate_unit, time_unit ) ) + with e.connect() as c: + c.execute(text(sqltext)) + c.commit() @cli.command("rates-rm", short_help="Remove a rating record from database") @@ -457,13 +479,17 @@ def acc_rates_rm(ctx, dbtname, rate_group, prefix): v_dbtname = dbtname.encode("ascii", "ignore").decode() v_rate_group = rate_group.encode("ascii", "ignore").decode() v_prefix = prefix.encode("ascii", "ignore").decode() - e.execute( + + sqltext = ( "delete from {0} where rate_group=({1!r} and prefix={2!r}".format( v_dbtname, v_rate_group, v_prefix, ) ) + with e.connect() as c: + c.execute(text(sqltext)) + c.commit() @cli.command( @@ -503,7 +529,9 @@ def acc_rates_proc_create(ctx): UNTIL done END REPEAT; END """ - e.execute(sqltext) + with e.connect() as c: + c.execute(text(sqltext)) + c.commit() @cli.command( @@ -526,10 +554,10 @@ def acc_rates_generate(ctx, rate_group): with e.connect() as c: t = c.begin() if not rate_group: - c.execute('call kamailio_rating("default")') + c.execute(text('call kamailio_rating("default")')) else: for rg in rate_group: - c.execute("call kamailio_rating({0!r})".format(rg)) + c.execute(text("call kamailio_rating({0!r})".format(rg))) t.commit() @@ -607,7 +635,8 @@ def acc_report(ctx, oformat, ostyle, limit, interval, name): if limit > 0: query = query + " LIMIT {0}".format(limit) - res = e.execute(query) + with e.connect() as c: + res = c.execute(text(query)) ioutils_dbres_print(ctx, oformat, ostyle, res) @@ -668,7 +697,8 @@ def acc_method_stats(ctx, oformat, ostyle, limit, interval): if limit > 0: query = query + " LIMIT {0}".format(limit) - res = e.execute(query) + with e.connect() as c: + res = c.execute(text(query)) acc_records = {} acc_records["invite"] = 0 @@ -680,7 +710,7 @@ def acc_method_stats(ctx, oformat, ostyle, limit, interval): acc_records["invite487"] = 0 acc_records["inviteXYZ"] = 0 - for row in res: + for row in res.mappings(): if row["method"] == "INVITE": acc_records["invite"] += 1 if row["sip_code"] == "200": diff --git a/kamcli/dbutils.py b/kamcli/dbutils.py index e9199c6..6e9fe9a 100644 --- a/kamcli/dbutils.py +++ b/kamcli/dbutils.py @@ -38,7 +38,7 @@ def dbutils_exec_sqlfile(ctx, sqlengine, fname): sql_command = "" -def dbutils_exec_sqltext(ctx, sqlengine, sqltext): +def dbutils_exec_sqltext(ctx, conn, sqltext): sql_command = "" for line in sqltext.splitlines(): tline = line.strip(" \t\r\n") @@ -46,7 +46,7 @@ def dbutils_exec_sqltext(ctx, sqlengine, sqltext): sql_command += " " + tline if sql_command.endswith(";"): try: - sqlengine.execute(text(sql_command)) + conn.execute(text(sql_command)) except SQLAlchemyError: ctx.log( "failed to execute sql statements [%s]", From dbbf2242f1aa16125455108958977cd2aa38d82c Mon Sep 17 00:00:00 2001 From: Victor Seva Date: Thu, 5 Mar 2026 10:49:22 +0100 Subject: [PATCH 03/20] cmd_address: remove implicit execution --- kamcli/commands/cmd_address.py | 42 ++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/kamcli/commands/cmd_address.py b/kamcli/commands/cmd_address.py index a9addba..f2c805f 100644 --- a/kamcli/commands/cmd_address.py +++ b/kamcli/commands/cmd_address.py @@ -1,5 +1,6 @@ import click from sqlalchemy import create_engine +from sqlalchemy import text from kamcli.ioutils import ioutils_dbres_print from kamcli.cli import pass_context from kamcli.iorpc import command_ctl @@ -36,7 +37,7 @@ def address_add(ctx, mask, port, tag, group, address): """ ctx.vlog("Adding to group id [%d] address [%s]", group, address) e = create_engine(ctx.gconfig.get("db", "rwurl")) - e.execute( + sqltext = ( "insert into address (grp, ip_addr, mask, port, tag) values " "({0}, {1!r}, {2}, {3}, {4!r})".format( group, @@ -46,6 +47,9 @@ def address_add(ctx, mask, port, tag, group, address): tag.encode("ascii", "ignore").decode(), ) ) + with e.connect() as c: + c.execute(text(sqltext)) + c.commit() @cli.command("rm", short_help="Remove a record from address db table") @@ -66,27 +70,34 @@ def address_rm(ctx, mask, port, group, address): addr = address.encode("ascii", "ignore").decode() if not mask: if not port: - e.execute( + sqltext = ( "delete from address where grp={0} and ip_addr={1!r}".format( group, addr ) ) + else: - e.execute( - "delete from address where grp={0} and ip_addr={1!r} " - "and port={2}".format(group, addr, port) + sqltext = ( + "delete from address where grp={0} " + "and ip_addr={1!r} and port={2}".format(group, addr, port) ) else: if not port: - e.execute( - "delete from address where grp={0} and " - "ip_addr={1!r} and mask={2}".format(group, addr, mask) + sqltext = ( + "delete from address where grp={0} " + "and ip_addr={1!r} and mask={2}".format(group, addr, mask) ) + else: - e.execute( - "delete from address where setid={0} and destination={1!r} " - "and mask={2} and port={3}".format(group, addr, mask, port) + sqltext = ( + "delete from address where setid={0} " + "and destination={1!r} and mask={2} and port={3}".format( + group, addr, mask, port + ) ) + with e.connect() as c: + c.execute(text(sqltext)) + c.commit() @cli.command("showdb", short_help="Show address records in database") @@ -117,10 +128,12 @@ def address_showdb(ctx, oformat, ostyle, group): e = create_engine(ctx.gconfig.get("db", "rwurl")) if not group: ctx.vlog("Showing all address records") - res = e.execute("select * from address") + sqltext = "select * from address" else: ctx.vlog("Showing address records for group") - res = e.execute("select * from address where group={0}".format(group)) + sqltext = "select * from address where group={0}".format(group) + with e.connect() as c: + res = c.execute(text(sqltext)) ioutils_dbres_print(ctx, oformat, ostyle, res) @@ -159,6 +172,5 @@ def address_list(ctx, mode, group): ) @pass_context def address_reload(ctx): - """Reload address records from database into memory - """ + """Reload address records from database into memory""" command_ctl(ctx, "permissions.addressReload", []) From e87094d80e85f8fb586e8f37d305201af5c0ab3e Mon Sep 17 00:00:00 2001 From: Victor Seva Date: Thu, 5 Mar 2026 10:56:24 +0100 Subject: [PATCH 04/20] cmd_aliasdb: remove implicit execution --- kamcli/commands/cmd_aliasdb.py | 39 ++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/kamcli/commands/cmd_aliasdb.py b/kamcli/commands/cmd_aliasdb.py index 9449648..ca45c1b 100644 --- a/kamcli/commands/cmd_aliasdb.py +++ b/kamcli/commands/cmd_aliasdb.py @@ -1,5 +1,6 @@ import click from sqlalchemy import create_engine +from sqlalchemy import text from kamcli.ioutils import ioutils_dbres_print from kamcli.cli import pass_context from kamcli.cli import parse_user_spec @@ -43,7 +44,7 @@ def aliasdb_add(ctx, table, userid, aliasid): adata["domain"], ) e = create_engine(ctx.gconfig.get("db", "rwurl")) - e.execute( + sqltext = ( "insert into {0} (username, domain, alias_username, " "alias_domain) values ({1!r}, {2!r}, {3!r}, {4!r})".format( table, @@ -53,6 +54,9 @@ def aliasdb_add(ctx, table, userid, aliasid): adata["domain"], ) ) + with e.connect() as c: + c.execute(text(sqltext)) + c.commit() @cli.command("rm", short_help="Remove records for a user and/or alias") @@ -86,22 +90,26 @@ def aliasdb_rm(ctx, table, matchalias, userid, aliasid): e = create_engine(ctx.gconfig.get("db", "rwurl")) if not aliasid: if matchalias: - e.execute( + sqltext = ( "delete from {0} where alias_username={1!r} and " "alias_domain={2!r}".format( - table, udata["username"], udata["domain"], + table, + udata["username"], + udata["domain"], ) ) else: - e.execute( + sqltext = ( "delete from {0} where username={1!r} and domain={2!r}".format( - table, udata["username"], udata["domain"], + table, + udata["username"], + udata["domain"], ) ) else: for a in aliasid: adata = parse_user_spec(ctx, a) - e.execute( + sqltext = ( "delete from {0} where username={1!r} and domain={2!r} " "and alias_username={3!r} and alias_domain={4!r}".format( table, @@ -111,6 +119,9 @@ def aliasdb_rm(ctx, table, matchalias, userid, aliasid): adata["domain"], ) ) + with e.connect() as c: + c.execute(text(sqltext)) + c.commit() @cli.command("show", short_help="Show user aliases") @@ -155,7 +166,7 @@ def aliasdb_show(ctx, oformat, ostyle, table, matchalias, userid): if not userid: ctx.vlog("Showing all records") e = create_engine(ctx.gconfig.get("db", "rwurl")) - res = e.execute("select * from {0}".format(table)) + res = e.execute(text("select * from {0}".format(table))) ioutils_dbres_print(ctx, oformat, ostyle, res) else: for u in userid: @@ -168,10 +179,12 @@ def aliasdb_show(ctx, oformat, ostyle, table, matchalias, userid): udata["username"], udata["domain"], ) - res = e.execute( + sqltext = ( "select * from {0} where alias_username={1!r} " "and alias_domain={2!r}".format( - table, udata["username"], udata["domain"], + table, + udata["username"], + udata["domain"], ) ) else: @@ -180,10 +193,14 @@ def aliasdb_show(ctx, oformat, ostyle, table, matchalias, userid): udata["username"], udata["domain"], ) - res = e.execute( + sqltext = ( "select * from {0} where username={1!r} and " "domain={2!r}".format( - table, udata["username"], udata["domain"], + table, + udata["username"], + udata["domain"], ) ) + with e.connect() as c: + c.execute(text(sqltext)) ioutils_dbres_print(ctx, oformat, ostyle, res) From 0341b07b18c8873261c4b5724d395f7ad8412c3c Mon Sep 17 00:00:00 2001 From: Victor Seva Date: Thu, 5 Mar 2026 11:00:14 +0100 Subject: [PATCH 05/20] cmd_avp: remove implicit execution --- kamcli/commands/cmd_avp.py | 81 +++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/kamcli/commands/cmd_avp.py b/kamcli/commands/cmd_avp.py index 02644bf..f2d3b2e 100644 --- a/kamcli/commands/cmd_avp.py +++ b/kamcli/commands/cmd_avp.py @@ -1,5 +1,6 @@ import click from sqlalchemy import create_engine +from sqlalchemy import text from kamcli.ioutils import ioutils_dbres_print from kamcli.cli import pass_context from kamcli.cli import parse_user_spec @@ -129,55 +130,52 @@ def avp_dbadd( v_value = value.encode("ascii", "ignore").decode() if isuuid: - e.execute( - "insert into {0} ({1}, {2}, {3}, {4}) values ({5!r}, {6!r}, {7}, {8!r})".format( + sqltext = "insert into {0} ({1}, {2}, {3}, {4}) values ({5!r}, {6!r}, {7}, {8!r})".format( + dbname, + c_uuid, + c_attribute, + c_type, + c_value, + v_userid, + v_attribute, + atype, + v_value, + ) + else: + udata = parse_user_spec(ctx, userid) + if setuuid: + sqltext = "insert into {0} ({1}, {2}, {3}, {4}, {5}, {6}) values ({7!r}, {8!r}, {9!r}, {10!r}, {11}, {12!r})".format( dbname, c_uuid, + c_username, + c_domain, c_attribute, c_type, c_value, - v_userid, + udata["username"], + udata["username"], + udata["domain"], v_attribute, atype, v_value, ) - ) - else: - udata = parse_user_spec(ctx, userid) - if setuuid: - e.execute( - "insert into {0} ({1}, {2}, {3}, {4}, {5}, {6}) values ({7!r}, {8!r}, {9!r}, {10!r}, {11}, {12!r})".format( - dbname, - c_uuid, - c_username, - c_domain, - c_attribute, - c_type, - c_value, - udata["username"], - udata["username"], - udata["domain"], - v_attribute, - atype, - v_value, - ) - ) else: - e.execute( - "insert into {0} ({1}, {2}, {3}, {4}, {5}) values ({6!r}, {7!r}, {8!r}, {9}, {10!r})".format( - dbname, - c_username, - c_domain, - c_attribute, - c_type, - c_value, - udata["username"], - udata["domain"], - v_attribute, - atype, - v_value, - ) + sqltext = "insert into {0} ({1}, {2}, {3}, {4}, {5}) values ({6!r}, {7!r}, {8!r}, {9}, {10!r})".format( + dbname, + c_username, + c_domain, + c_attribute, + c_type, + c_value, + udata["username"], + udata["domain"], + v_attribute, + atype, + v_value, ) + with e.connect() as c: + c.execute(text(sqltext)) + c.commit() @cli.command("db-rm", short_help="Delete AVPs from database") @@ -309,7 +307,9 @@ def avp_dbrm( if v_value != "*": sqlquery += " AND {0}={1!r}".format(c_value, v_value) - e.execute(sqlquery) + with e.connect() as c: + c.execute(text(sqlquery)) + c.commit() @cli.command("db-show", short_help="Show AVPs from database") @@ -458,5 +458,6 @@ def avp_dbshow( if v_value != "*": sqlquery += " AND {0}={1!r}".format(c_value, v_value) - res = e.execute(sqlquery) + with e.connect() as c: + res = c.execute(text(sqlquery)) ioutils_dbres_print(ctx, oformat, ostyle, res) From 7622dc02c7c4e8779074725cb702827dfbab49ba Mon Sep 17 00:00:00 2001 From: Victor Seva Date: Thu, 5 Mar 2026 11:50:55 +0100 Subject: [PATCH 06/20] cmd_db: remove implicit execution --- kamcli/commands/cmd_db.py | 227 +++++++++++++++++++++----------------- kamcli/dbutils.py | 4 +- 2 files changed, 126 insertions(+), 105 deletions(-) diff --git a/kamcli/commands/cmd_db.py b/kamcli/commands/cmd_db.py index 1e69aff..4929487 100644 --- a/kamcli/commands/cmd_db.py +++ b/kamcli/commands/cmd_db.py @@ -93,7 +93,10 @@ def cli(ctx): @pass_context def db_query(ctx, oformat, ostyle, query): e = create_engine(ctx.gconfig.get("db", "rwurl")) - res = e.execute(query.encode("ascii", "ignore").decode()) + sqlquery = query.encode("ascii", "ignore").decode() + with e.connect() as c: + res = c.execute(text(sqlquery)) + c.commit() ioutils_dbres_print(ctx, oformat, ostyle, res) @@ -250,7 +253,9 @@ def db_clishowg(ctx, table): def db_show(ctx, oformat, ostyle, table): ctx.vlog("Content of database table [%s]", table) e = create_engine(ctx.gconfig.get("db", "rwurl")) - res = e.execute("select * from {0}".format(table)) + sqlquery = "select * from {0}".format(table) + with e.connect() as c: + res = c.execute(text(sqlquery)) ioutils_dbres_print(ctx, oformat, ostyle, res) @@ -279,7 +284,9 @@ def db_showcreate(ctx, oformat, ostyle, table): dbtype = ctx.gconfig.get("db", "type") if dbtype == "mysql": e = create_engine(ctx.gconfig.get("db", "rwurl")) - res = e.execute("show create table {0}".format(table)) + sqlquery = "show create table {0}".format(table) + with e.connect() as c: + res = c.execute(text(sqlquery)) ioutils_dbres_print(ctx, oformat, ostyle, res) elif dbtype == "postgresql": scmd = ('psql "postgresql://{0}:{1}@{2}/{3}" -c "\\d {4} "').format( @@ -312,12 +319,14 @@ def db_runfile(ctx, fname): """ ctx.vlog("Run statements in the file [%s]", fname) e = create_engine(ctx.gconfig.get("db", "rwurl")) - dbutils_exec_sqlfile(ctx, e, fname) + with e.connect() as c: + dbutils_exec_sqlfile(ctx, c, fname) + c.commit() def db_create_mysql_host_users( ctx, - e, + c, nousers, nogrants, dbname, @@ -328,32 +337,28 @@ def db_create_mysql_host_users( dbropassword, ): if not nousers: - e.execute( - "CREATE USER {0!r}@{1!r} IDENTIFIED BY {2!r}".format( - dbrwuser, dbhost, dbrwpassword - ) + sqlquery = "CREATE USER {0!r}@{1!r} IDENTIFIED BY {2!r}".format( + dbrwuser, dbhost, dbrwpassword ) + c.execute(text(sqlquery)) if not nogrants: - e.execute( - "GRANT ALL PRIVILEGES ON {0}.* TO {1!r}@{2!r}".format( - dbname, dbrwuser, dbhost - ) + sqlquery = "GRANT ALL PRIVILEGES ON {0}.* TO {1!r}@{2!r}".format( + dbname, dbrwuser, dbhost ) + c.execute(text(sqlquery)) if not nousers: - e.execute( - "CREATE USER {0!r}@{1!r} IDENTIFIED BY {2!r}".format( - dbrouser, dbhost, dbropassword - ) + sqlquery = "CREATE USER {0!r}@{1!r} IDENTIFIED BY {2!r}".format( + dbrouser, dbhost, dbropassword ) + c.execute(text(sqlquery)) if not nogrants: - e.execute( - "GRANT SELECT ON {0}.* TO {1!r}@{2!r}".format( - dbname, dbrouser, dbhost - ) + sqlquery = "GRANT SELECT ON {0}.* TO {1!r}@{2!r}".format( + dbname, dbrouser, dbhost ) + c.execute(text(sqlquery)) -def db_create_mysql_users(ctx, e, dbname, nousers, nogrants): +def db_create_mysql_users(ctx, c, dbname, nousers, nogrants): dbhost = ctx.gconfig.get("db", "host") dbrwuser = ctx.gconfig.get("db", "rwuser") dbrwpassword = ctx.gconfig.get("db", "rwpassword") @@ -362,7 +367,7 @@ def db_create_mysql_users(ctx, e, dbname, nousers, nogrants): dbaccesshost = ctx.gconfig.get("db", "accesshost") db_create_mysql_host_users( ctx, - e, + c, nousers, nogrants, dbname, @@ -375,7 +380,7 @@ def db_create_mysql_users(ctx, e, dbname, nousers, nogrants): if dbhost != "localhost": db_create_mysql_host_users( ctx, - e, + c, nousers, nogrants, dbname, @@ -388,7 +393,7 @@ def db_create_mysql_users(ctx, e, dbname, nousers, nogrants): if len(dbaccesshost) > 0: db_create_mysql_host_users( ctx, - e, + c, nousers, nogrants, dbname, @@ -400,42 +405,44 @@ def db_create_mysql_users(ctx, e, dbname, nousers, nogrants): ) -def db_create_sql_group(ctx, e, dirpath, dbgroup): +def db_create_sql_group(ctx, c, dirpath, dbgroup): for t in dbgroup: fname = dirpath + "/" + t + "-create.sql" - dbutils_exec_sqlfile(ctx, e, fname) + dbutils_exec_sqlfile(ctx, c, fname) -def db_create_sql_table_groups(ctx, e, ldirectory, alltables): - db_create_sql_group(ctx, e, ldirectory, KDB_GROUP_BASIC) - db_create_sql_group(ctx, e, ldirectory, KDB_GROUP_STANDARD) +def db_create_sql_table_groups(ctx, c, ldirectory, alltables): + db_create_sql_group(ctx, c, ldirectory, KDB_GROUP_BASIC) + db_create_sql_group(ctx, c, ldirectory, KDB_GROUP_STANDARD) option = "y" if not alltables: print("Do you want to create extra tables? (y/n):", end=" ") option = input() if option == "y": - db_create_sql_group(ctx, e, ldirectory, KDB_GROUP_EXTRA) + db_create_sql_group(ctx, c, ldirectory, KDB_GROUP_EXTRA) if not alltables: print("Do you want to create presence tables? (y/n):", end=" ") option = input() if option == "y": - db_create_sql_group(ctx, e, ldirectory, KDB_GROUP_PRESENCE) + db_create_sql_group(ctx, c, ldirectory, KDB_GROUP_PRESENCE) if not alltables: print("Do you want to create uid tables? (y/n):", end=" ") option = input() if option == "y": - db_create_sql_group(ctx, e, ldirectory, KDB_GROUP_UID) + db_create_sql_group(ctx, c, ldirectory, KDB_GROUP_UID) def db_create_mysql(ctx, ldbname, ldirectory, nousers, nogrants, alltables): e = create_engine(ctx.gconfig.get("db", "adminurl")) - e.execute("create database {0}".format(ldbname)) - db_create_mysql_users(ctx, e, ldbname, nousers, nogrants) - e.execute("use {0}".format(ldbname)) - db_create_sql_table_groups(ctx, e, ldirectory, alltables) + with e.connect() as c: + c.execute(text("create database {0}".format(ldbname))) + db_create_mysql_users(ctx, c, ldbname, nousers, nogrants) + c.execute(text("use {0}".format(ldbname))) + db_create_sql_table_groups(ctx, c, ldirectory, alltables) + c.commit() def db_create_postgresql( @@ -451,33 +458,32 @@ def db_create_postgresql( ) os.system(scmd) e = create_engine(ctx.gconfig.get("db", "adminurl")) - if not nogrants: - e.execute( - "CREATE USER {0} WITH PASSWORD '{1}';".format( + with e.connect() as c: + if not nogrants: + sqlquery = "CREATE USER {0} WITH PASSWORD '{1}';".format( ctx.gconfig.get("db", "rwuser"), ctx.gconfig.get("db", "rwpassword"), ) - ) - e.execute( - "GRANT CONNECT ON DATABASE {0} TO {1};".format( + c.execute(text(sqlquery)) + sqlquery = "GRANT CONNECT ON DATABASE {0} TO {1};".format( ldbname, ctx.gconfig.get("db", "rwuser"), ) - ) - if ctx.gconfig.get("db", "rwuser") != ctx.gconfig.get("db", "rouser"): - e.execute( - "CREATE USER {0} WITH PASSWORD '{1}';".format( + c.execute(text(sqlquery)) + if ctx.gconfig.get("db", "rwuser") != ctx.gconfig.get( + "db", "rouser" + ): + sqlquery = "CREATE USER {0} WITH PASSWORD '{1}';".format( ctx.gconfig.get("db", "rouser"), ctx.gconfig.get("db", "ropassword"), ) - ) - e.execute( - "GRANT CONNECT ON DATABASE {0} TO {1};".format( + c.execute(text(sqlquery)) + sqlquery = "GRANT CONNECT ON DATABASE {0} TO {1};".format( ldbname, ctx.gconfig.get("db", "rouser"), ) - ) - e.dispose() + c.execute(text(sqlquery)) + c.commit() e = create_engine( "{0}+{1}://{2}:{3}@{4}/{5}".format( ctx.gconfig.get("db", "type"), @@ -488,30 +494,31 @@ def db_create_postgresql( ldbname, ) ) - if not nofunctions: - e.execute( - "CREATE FUNCTION concat(text, text) RETURNS text AS 'SELECT $1 || $2;' LANGUAGE 'sql';" - ) - e.execute( - "CREATE FUNCTION rand() RETURNS double precision AS 'SELECT random();' LANGUAGE 'sql';" - ) - db_create_sql_table_groups(ctx, e, ldirectory, alltables) - e.dispose() + with e.connect() as c: + if not nofunctions: + sqlquery = "CREATE FUNCTION concat(text, text) RETURNS text AS 'SELECT $1 || $2;' LANGUAGE 'sql';" + c.execute(text(sqlquery)) + sqlquery = "CREATE FUNCTION rand() RETURNS double precision AS 'SELECT random();' LANGUAGE 'sql';" + c.execute(text(sqlquery)) + db_create_sql_table_groups(ctx, c, ldirectory, alltables) + c.commit() e = create_engine(ctx.gconfig.get("db", "adminurl")) - if not nogrants: - e.execute( - "GRANT ALL PRIVILEGES ON DATABASE {0} TO {1};".format( + with e.connect() as c: + if not nogrants: + sqlquery = "GRANT ALL PRIVILEGES ON DATABASE {0} TO {1};".format( ldbname, ctx.gconfig.get("db", "rwuser"), ) - ) - if ctx.gconfig.get("db", "rwuser") != ctx.gconfig.get("db", "rouser"): - e.execute( - "GRANT SELECT ON DATABASE {0} TO {1};".format( + c.execute(text(sqlquery)) + if ctx.gconfig.get("db", "rwuser") != ctx.gconfig.get( + "db", "rouser" + ): + sqlquery = "GRANT SELECT ON DATABASE {0} TO {1};".format( ldbname, ctx.gconfig.get("db", "rouser"), ) - ) + c.execute(text(sqlquery)) + c.commit() def db_create_sqlite(ctx, ldbname, ldirectory, alltables): @@ -522,7 +529,9 @@ def db_create_sqlite(ctx, ldbname, ldirectory, alltables): ldbname, ) ) - db_create_sql_table_groups(ctx, e, ldirectory, alltables) + with e.connect() as c: + db_create_sql_table_groups(ctx, c, ldirectory, alltables) + c.commit() @cli.command("create", short_help="Create database structure") @@ -630,7 +639,9 @@ def db_create_dbonly(ctx, dbname): if dbtype == "mysql": e = create_engine(ctx.gconfig.get("db", "adminurl")) - e.execute("create database {0}".format(ldbname)) + with e.connect() as c: + c.execute(text("create database {0}".format(ldbname))) + c.commit() elif dbtype == "postgresql": scmd = ( 'psql "postgresql://{0}:{1}@{2}" -c "create database {3} "' @@ -687,7 +698,9 @@ def db_drop(ctx, dbname, yes): if dbtype == "mysql": e = create_engine(ctx.gconfig.get("db", "adminurl")) - e.execute("drop database {0}".format(ldbname)) + with e.connect() as c: + c.execute(text("drop database {0}".format(ldbname))) + c.commit() elif dbtype == "postgresql": scmd = ( 'psql "postgresql://{0}:{1}@{2}" -c "drop database {3} "' @@ -718,7 +731,9 @@ def db_create_tables_list(ctx, directory, group): if len(directory) > 0: ldirectory = directory e = create_engine(ctx.gconfig.get("db", "rwurl")) - db_create_sql_group(ctx, e, ldirectory, group) + with e.connect() as c: + db_create_sql_group(ctx, c, ldirectory, group) + c.commit() @cli.command("create-tables-basic", short_help="Create basic database tables") @@ -850,7 +865,9 @@ def db_create_tables_group(ctx, scriptsdirectory, gname): ldirectory = scriptsdirectory e = create_engine(ctx.gconfig.get("db", "rwurl")) fpath = ldirectory + "/" + gname + "-create.sql" - dbutils_exec_sqlfile(ctx, e, fpath) + with e.connect() as c: + dbutils_exec_sqlfile(ctx, c, fpath) + c.commit() @cli.command( @@ -861,7 +878,9 @@ def db_create_tables_group(ctx, scriptsdirectory, gname): @pass_context def db_create_table_like(ctx, newname, oldname): e = create_engine(ctx.gconfig.get("db", "rwurl")) - e.execute("CREATE TABLE {0} LIKE {1}".format(newname, oldname)) + with e.connect() as c: + c.execute(text("CREATE TABLE {0} LIKE {1}".format(newname, oldname))) + c.commit() @cli.command("grant", short_help="Create db access users and grant privileges") @@ -887,34 +906,34 @@ def db_grant(ctx, dbname): ldbname = dbname ctx.vlog("Creating only database [%s]", ldbname) e = create_engine(ctx.gconfig.get("db", "adminurl")) - db_create_mysql_users(ctx, e, ldbname, False, False) + with e.connect() as c: + db_create_mysql_users(ctx, c, ldbname, False, False) + c.commit() -def db_revoke_host_users(ctx, e, dbname, dbhost, dbrwuser, dbrouser): - e.execute( - "REVOKE ALL PRIVILEGES ON {0}.* FROM {1!r}@{2!r}".format( - dbname, dbrwuser, dbhost - ) +def db_revoke_host_users(ctx, c, dbname, dbhost, dbrwuser, dbrouser): + sqlquery = "REVOKE ALL PRIVILEGES ON {0}.* FROM {1!r}@{2!r}".format( + dbname, dbrwuser, dbhost ) - e.execute("DROP USER {0!r}@{1!r}".format(dbrwuser, dbhost)) - e.execute( - "REVOKE SELECT ON {0}.* FROM {1!r}@{2!r}".format( - dbname, dbrouser, dbhost - ) + c.execute(text(sqlquery)) + c.execute(text("DROP USER {0!r}@{1!r}".format(dbrwuser, dbhost))) + sqlquery = "REVOKE SELECT ON {0}.* FROM {1!r}@{2!r}".format( + dbname, dbrouser, dbhost ) - e.execute("DROP USER {0!r}@{1!r}".format(dbrouser, dbhost)) + c.execute(text(sqlquery)) + c.execute(text("DROP USER {0!r}@{1!r}".format(dbrouser, dbhost))) -def db_revoke_users(ctx, e, dbname): +def db_revoke_users(ctx, c, dbname): dbhost = ctx.gconfig.get("db", "host") dbrwuser = ctx.gconfig.get("db", "rwuser") dbrouser = ctx.gconfig.get("db", "rouser") dbaccesshost = ctx.gconfig.get("db", "accesshost") - db_revoke_host_users(ctx, e, dbname, dbhost, dbrwuser, dbrouser) + db_revoke_host_users(ctx, c, dbname, dbhost, dbrwuser, dbrouser) if dbhost != "localhost": db_revoke_host_users( ctx, - e, + c, dbname, "localhost", dbrwuser, @@ -923,7 +942,7 @@ def db_revoke_users(ctx, e, dbname): if len(dbaccesshost) > 0: db_revoke_host_users( ctx, - e, + c, dbname, dbaccesshost, dbrwuser, @@ -954,7 +973,9 @@ def db_revoke(ctx, dbname): ldbname = dbname ctx.vlog("Revoke access to database [%s]", ldbname) e = create_engine(ctx.gconfig.get("db", "adminurl")) - db_revoke_users(ctx, e, ldbname) + with e.connect() as c: + db_revoke_users(ctx, c, ldbname) + c.commit() @cli.command( @@ -978,19 +999,19 @@ def db_version_set(ctx, vertable, table, version): - Version number """ e = create_engine(ctx.gconfig.get("db", "rwurl")) - e.execute( - "delete from {0} where table_name={1!r}".format( + with e.connect() as c: + sqlquery = "delete from {0} where table_name={1!r}".format( vertable.encode("ascii", "ignore").decode(), table.encode("ascii", "ignore").decode(), ) - ) - e.execute( - "insert into {0} (table_name, table_version) values ({1!r}, {2})".format( + c.execute(text(sqlquery)) + sqlquery = "insert into {0} (table_name, table_version) values ({1!r}, {2})".format( vertable.encode("ascii", "ignore").decode(), table.encode("ascii", "ignore").decode(), version, ) - ) + c.execute(text(sqlquery)) + c.commit() @cli.command( @@ -1027,10 +1048,10 @@ def db_version_get(ctx, vertable, oformat, ostyle, table): - Name of the table to get the version for """ e = create_engine(ctx.gconfig.get("db", "rwurl")) - res = e.execute( - "select * from {0} where table_name={1!r}".format( - vertable.encode("ascii", "ignore").decode(), - table.encode("ascii", "ignore").decode(), - ) + sqlquery = "select * from {0} where table_name={1!r}".format( + vertable.encode("ascii", "ignore").decode(), + table.encode("ascii", "ignore").decode(), ) + with e.connect() as c: + res = c.execute(text(sqlquery)) ioutils_dbres_print(ctx, oformat, ostyle, res) diff --git a/kamcli/dbutils.py b/kamcli/dbutils.py index 6e9fe9a..f2bd7ae 100644 --- a/kamcli/dbutils.py +++ b/kamcli/dbutils.py @@ -10,7 +10,7 @@ ] -def dbutils_exec_sqlfile(ctx, sqlengine, fname): +def dbutils_exec_sqlfile(ctx, conn, fname): if not os.path.exists(fname): for i in KDB_IGNORE_MISSING: if i in fname: @@ -27,7 +27,7 @@ def dbutils_exec_sqlfile(ctx, sqlengine, fname): sql_command += line.strip("\n") if sql_command.endswith(";"): try: - sqlengine.execute(text(sql_command)) + conn.execute(text(sql_command)) except SQLAlchemyError: ctx.log( "failed to execute sql statement [%s] from file [%s]", From c3add045c6c94da0935642e7b7c7dd3368663104 Mon Sep 17 00:00:00 2001 From: Victor Seva Date: Thu, 5 Mar 2026 12:00:27 +0100 Subject: [PATCH 07/20] cmd_dialog: remove implicit execution --- kamcli/commands/cmd_dialog.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kamcli/commands/cmd_dialog.py b/kamcli/commands/cmd_dialog.py index a437853..0e8a7d4 100644 --- a/kamcli/commands/cmd_dialog.py +++ b/kamcli/commands/cmd_dialog.py @@ -1,5 +1,6 @@ import click from sqlalchemy import create_engine +from sqlalchemy import text from kamcli.ioutils import ioutils_dbres_print from kamcli.cli import pass_context from kamcli.iorpc import command_ctl @@ -39,7 +40,8 @@ def dialog_showdb(ctx, oformat, ostyle): """ e = create_engine(ctx.gconfig.get("db", "rwurl")) ctx.vlog("Showing all dialog records") - res = e.execute("select * from dialog") + with e.connect() as c: + res = c.execute(text("select * from dialog")) ioutils_dbres_print(ctx, oformat, ostyle, res) From 0b03b3492e8ef7ab403565321e820350624e8136 Mon Sep 17 00:00:00 2001 From: Victor Seva Date: Thu, 5 Mar 2026 12:47:36 +0100 Subject: [PATCH 08/20] cmd_dialplan: remove implicit execution --- kamcli/commands/cmd_dialplan.py | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/kamcli/commands/cmd_dialplan.py b/kamcli/commands/cmd_dialplan.py index 5fe02f5..a91d2f5 100644 --- a/kamcli/commands/cmd_dialplan.py +++ b/kamcli/commands/cmd_dialplan.py @@ -1,5 +1,6 @@ import click from sqlalchemy import create_engine +from sqlalchemy import text from kamcli.ioutils import ioutils_dbres_print from kamcli.cli import pass_context from kamcli.iorpc import command_ctl @@ -72,7 +73,7 @@ def dialplan_add( matchexp, ) e = create_engine(ctx.gconfig.get("db", "rwurl")) - e.execute( + sqlquery = ( "insert into dialplan (dpid, pr, match_op, match_exp, match_len, " "subst_exp, repl_exp, attrs) values " "({0}, {1}, {2}, {3!r}, {4}, {5!r}, {6!r}, {7!r})".format( @@ -86,6 +87,9 @@ def dialplan_add( attrs.encode("ascii", "ignore").decode(), ) ) + with e.connect() as c: + c.execute(text(sqlquery)) + c.commit() @cli.command("rm", short_help="Remove records from dialplan") @@ -101,16 +105,16 @@ def dialplan_rm(ctx, dpid, matchexp): [] - match expression """ e = create_engine(ctx.gconfig.get("db", "rwurl")) - if not matchexp: - e.execute("delete from dialplan where dpid={0}".format(dpid)) - else: - for m in matchexp: - e.execute( - "delete from dialplan where " - "dpid={0} and match_exp={1!r}".format( + with e.connect() as c: + if not matchexp: + c.execute(text("delete from dialplan where dpid={0}".format(dpid))) + else: + for m in matchexp: + sqlquery = "delete from dialplan where dpid={0} and match_exp={1!r}".format( dpid, matchexp.encode("ascii", "ignore").decode() ) - ) + c.execute(text(sqlquery)) + c.commit() @cli.command("showdb", short_help="Show dialplan records in database") @@ -141,12 +145,15 @@ def dialplan_showdb(ctx, oformat, ostyle, dpid): e = create_engine(ctx.gconfig.get("db", "rwurl")) if not dpid: ctx.vlog("Showing all dialplan records") - res = e.execute("select * from dialplan") + res = e.execute(text("select * from dialplan")) ioutils_dbres_print(ctx, oformat, ostyle, res) else: for d in dpid: ctx.vlog("Showing dialplan records for set id: " + d) - res = e.execute("select * from dialplan where dpid={0}".format(d)) + with e.connect() as c: + res = c.execute( + text("select * from dialplan where dpid={0}".format(d)) + ) ioutils_dbres_print(ctx, oformat, ostyle, res) @@ -168,8 +175,7 @@ def dialplan_list(ctx, dpid): ) @pass_context def dialplan_reload(ctx): - """Reload dialplan records from database into memory - """ + """Reload dialplan records from database into memory""" command_ctl(ctx, "dialplan.reload", []) From 03ac6a939c5502c7634a747fc5f2accba6ea3a2e Mon Sep 17 00:00:00 2001 From: Victor Seva Date: Thu, 5 Mar 2026 12:50:22 +0100 Subject: [PATCH 09/20] cmd_dispatcher: remove implicit execution --- kamcli/commands/cmd_dispatcher.py | 32 +++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/kamcli/commands/cmd_dispatcher.py b/kamcli/commands/cmd_dispatcher.py index 41fbafd..bb94885 100644 --- a/kamcli/commands/cmd_dispatcher.py +++ b/kamcli/commands/cmd_dispatcher.py @@ -1,5 +1,6 @@ import click from sqlalchemy import create_engine +from sqlalchemy import text from kamcli.ioutils import ioutils_dbres_print from kamcli.cli import pass_context from kamcli.iorpc import command_ctl @@ -39,7 +40,7 @@ def dispatcher_add( """ ctx.vlog("Adding to setid [%d] destination [%s]", setid, destination) e = create_engine(ctx.gconfig.get("db", "rwurl")) - e.execute( + sqlquery = ( "insert into dispatcher " "(setid, destination, flags, priority, attrs, description) " "values ({0}, {1!r}, {2}, {3}, {4!r}, {5!r})".format( @@ -51,6 +52,9 @@ def dispatcher_add( description.encode("ascii", "ignore").decode(), ) ) + with e.connect() as c: + c.execute(text(sqlquery)) + c.commit() @cli.command("rm", short_help="Remove a destination from dispatcher table") @@ -66,11 +70,14 @@ def dispatcher_rm(ctx, setid, destination): - SIP URI for destination """ e = create_engine(ctx.gconfig.get("db", "rwurl")) - e.execute( + sqlquery = ( "delete from dispatcher where setid={0} and destination={1!r}".format( setid, destination.encode("ascii", "ignore").decode() ) ) + with e.connect() as c: + c.execute(text(sqlquery)) + c.commit() @cli.command("showdb", short_help="Show dispatcher records in database") @@ -99,17 +106,18 @@ def dispatcher_showdb(ctx, oformat, ostyle, setid): [] - dispatching set id """ e = create_engine(ctx.gconfig.get("db", "rwurl")) - if not setid: - ctx.vlog("Showing all dispatcher records") - res = e.execute("select * from dispatcher") - ioutils_dbres_print(ctx, oformat, ostyle, res) - else: - for s in setid: - ctx.vlog("Showing dispatcher records for set id") - res = e.execute( - "select * from dispatcher where setid={0}".format(s) - ) + with e.connect() as c: + if not setid: + ctx.vlog("Showing all dispatcher records") + res = c.execute(text("select * from dispatcher")) ioutils_dbres_print(ctx, oformat, ostyle, res) + else: + for s in setid: + ctx.vlog("Showing dispatcher records for set id") + res = c.execute( + text("select * from dispatcher where setid={0}".format(s)) + ) + ioutils_dbres_print(ctx, oformat, ostyle, res) @cli.command( From c6179f4b43e15907cdf847416e1133720857de58 Mon Sep 17 00:00:00 2001 From: Victor Seva Date: Thu, 5 Mar 2026 12:52:10 +0100 Subject: [PATCH 10/20] cmd_domain: remove implicit execution --- kamcli/commands/cmd_domain.py | 41 ++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/kamcli/commands/cmd_domain.py b/kamcli/commands/cmd_domain.py index 6d0df05..c524eee 100644 --- a/kamcli/commands/cmd_domain.py +++ b/kamcli/commands/cmd_domain.py @@ -1,5 +1,6 @@ import click from sqlalchemy import create_engine +from sqlalchemy import text from kamcli.ioutils import ioutils_dbres_print from kamcli.cli import pass_context from kamcli.iorpc import command_ctl @@ -27,11 +28,12 @@ def domain_add(ctx, domain): """ ctx.vlog("Adding a new domain [%s]", domain) e = create_engine(ctx.gconfig.get("db", "rwurl")) - e.execute( - "insert into domain (domain) values ({0!r})".format( - domain.encode("ascii", "ignore").decode() - ) + sqlquery = "insert into domain (domain) values ({0!r})".format( + domain.encode("ascii", "ignore").decode() ) + with e.connect() as c: + c.execute(text(sqlquery)) + c.commit() @cli.command("rm", short_help="Remove a record from domain table") @@ -45,11 +47,12 @@ def domain_rm(ctx, domain): - domain value """ e = create_engine(ctx.gconfig.get("db", "rwurl")) - e.execute( - "delete from domain where domain={0!r}".format( - domain.encode("ascii", "ignore").decode() - ) + sqlquery = "delete from domain where domain={0!r}".format( + domain.encode("ascii", "ignore").decode() ) + with e.connect() as c: + c.execute(text(sqlquery)) + c.commit() ## @@ -81,17 +84,19 @@ def domain_showdb(ctx, oformat, ostyle, domain): [] - domain value (optional) """ e = create_engine(ctx.gconfig.get("db", "rwurl")) - if not domain: - ctx.vlog("Showing all domain records") - res = e.execute("select * from domain") - ioutils_dbres_print(ctx, oformat, ostyle, res) - else: - for d in domain: - ctx.vlog("Showing a specific domain record") - res = e.execute( - "select * from domain where domain={0!r}".format(d) - ) + with e.connect() as c: + if not domain: + ctx.vlog("Showing all domain records") + res = c.execute(text("select * from domain")) ioutils_dbres_print(ctx, oformat, ostyle, res) + else: + for d in domain: + ctx.vlog("Showing a specific domain record") + res = c.execute( + text("select * from domain where domain={0!r}".format(d)) + ) + ioutils_dbres_print(ctx, oformat, ostyle, res) + c.commit() @cli.command("list", short_help="Show details for domain records in memory") From a9346706c40786776d0dab18dceed56a14fb4958 Mon Sep 17 00:00:00 2001 From: Victor Seva Date: Thu, 5 Mar 2026 12:54:29 +0100 Subject: [PATCH 11/20] cmd_group: remove implicit execution --- kamcli/commands/cmd_group.py | 42 ++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/kamcli/commands/cmd_group.py b/kamcli/commands/cmd_group.py index ca36c32..21ebd34 100644 --- a/kamcli/commands/cmd_group.py +++ b/kamcli/commands/cmd_group.py @@ -1,5 +1,6 @@ import click from sqlalchemy import create_engine +from sqlalchemy import text from kamcli.ioutils import ioutils_dbres_print from kamcli.cli import pass_context from kamcli.cli import parse_user_spec @@ -35,11 +36,14 @@ def group_grant(ctx, userid, groupid): groupid, ) e = create_engine(ctx.gconfig.get("db", "rwurl")) - e.execute( - "insert into grp (username, domain, grp) values ({0!r}, {1!r}, {2!r})".format( - udata["username"], udata["domain"], groupid, - ) + sqlquery = "insert into grp (username, domain, grp) values ({0!r}, {1!r}, {2!r})".format( + udata["username"], + udata["domain"], + groupid, ) + with e.connect() as c: + c.execute(text(sqlquery)) + c.commit() @cli.command("revoke", short_help="Remove a user from groups") @@ -60,17 +64,21 @@ def group_revoke(ctx, userid, groupid): ) e = create_engine(ctx.gconfig.get("db", "rwurl")) if not groupid: - e.execute( + sqlquery = ( "delete from grp where username={0!r} and domain={1!r}".format( - udata["username"], udata["domain"], + udata["username"], + udata["domain"], ) ) else: - e.execute( - "delete from grp where username={0!r} and domain={1!r} and grp={2!r}".format( - udata["username"], udata["domain"], groupid, - ) + sqlquery = "delete from grp where username={0!r} and domain={1!r} and grp={2!r}".format( + udata["username"], + udata["domain"], + groupid, ) + with e.connect() as c: + c.execute(text(sqlquery)) + c.commit() @cli.command("show", short_help="Show group membership details") @@ -103,8 +111,9 @@ def group_show(ctx, oformat, ostyle, userid): if not userid: ctx.vlog("Showing all records") e = create_engine(ctx.gconfig.get("db", "rwurl")) - res = e.execute("select * from grp") - ioutils_dbres_print(ctx, oformat, ostyle, res) + with e.connect() as c: + res = c.execute(text("select * from grp")) + ioutils_dbres_print(ctx, oformat, ostyle, res) else: for u in userid: udata = parse_user_spec(ctx, u) @@ -114,9 +123,10 @@ def group_show(ctx, oformat, ostyle, userid): udata["domain"], ) e = create_engine(ctx.gconfig.get("db", "rwurl")) - res = e.execute( - "select * from grp where username={0!r} and domain={1!r}".format( - udata["username"], udata["domain"], - ) + sqlquery = "select * from grp where username={0!r} and domain={1!r}".format( + udata["username"], + udata["domain"], ) + with e.connect() as c: + res = c.execute(text(sqlquery)) ioutils_dbres_print(ctx, oformat, ostyle, res) From dfddc3e115646b0e170e6f6aab24366745ae690d Mon Sep 17 00:00:00 2001 From: Victor Seva Date: Thu, 5 Mar 2026 12:57:47 +0100 Subject: [PATCH 12/20] cmd_htable: remove implicit execution --- kamcli/commands/cmd_htable.py | 77 ++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 33 deletions(-) diff --git a/kamcli/commands/cmd_htable.py b/kamcli/commands/cmd_htable.py index 3b2b695..d57b4f6 100644 --- a/kamcli/commands/cmd_htable.py +++ b/kamcli/commands/cmd_htable.py @@ -1,5 +1,6 @@ import click from sqlalchemy import create_engine +from sqlalchemy import text from kamcli.ioutils import ioutils_dbres_print from kamcli.cli import pass_context from kamcli.iorpc import command_ctl @@ -142,7 +143,11 @@ def htable_setxi(ctx, htname, itname, ival, exp): @cli.command("rm", short_help="Remove the item $sht(htname=>itname)") @click.option( - "yes", "--yes", "-y", is_flag=True, help="Do not ask for confirmation", + "yes", + "--yes", + "-y", + is_flag=True, + help="Do not ask for confirmation", ) @click.argument("htname", metavar="") @click.argument("itname", metavar="") @@ -212,7 +217,8 @@ def htable_store(ctx, htname): @cli.command( - "list-tables", short_help="List the hash tables", + "list-tables", + short_help="List the hash tables", ) @pass_context def htable_list_tables(ctx): @@ -224,7 +230,8 @@ def htable_list_tables(ctx): @cli.command( - "stats", short_help="Print statistics for hash tables", + "stats", + short_help="Print statistics for hash tables", ) @pass_context def htable_stats(ctx): @@ -295,23 +302,22 @@ def htable_dbadd( kvalue = keyvalue.encode("ascii", "ignore").decode() if valtype == 0: - e.execute( - "insert into {0} ({1}, {2}) values ({3!r}, {4!r})".format( - dbname, col_kname, col_kvalue, kname, kvalue - ) + sqltext = "insert into {0} ({1}, {2}) values ({3!r}, {4!r})".format( + dbname, col_kname, col_kvalue, kname, kvalue ) else: - e.execute( - "insert into {0} ({1}, {2}, {3}) values ({4!r}, {5}, {6!r})".format( - dbname, - col_kname, - col_valtype, - col_kvalue, - kname, - valtype, - kvalue, - ) + sqltext = "insert into {0} ({1}, {2}, {3}) values ({4!r}, {5}, {6!r})".format( + dbname, + col_kname, + col_valtype, + col_kvalue, + kname, + valtype, + kvalue, ) + with e.connect() as c: + c.execute(text(sqltext)) + c.commit() @cli.command("db-rm", short_help="Remove a record from htable database") @@ -328,7 +334,11 @@ def htable_dbadd( help='Column name for key name (default: "key_name")', ) @click.option( - "yes", "--yes", "-y", is_flag=True, help="Do not ask for confirmation", + "yes", + "--yes", + "-y", + is_flag=True, + help="Do not ask for confirmation", ) @click.argument("keyname", metavar="") @pass_context @@ -346,13 +356,14 @@ def htable_dbrm(ctx, dbtname, colkeyname, yes, keyname): ctx.vlog("Skip removing item [%s]", keyname) return e = create_engine(ctx.gconfig.get("db", "rwurl")) - e.execute( - "delete from {0} where {1}={2!r}".format( - dbtname.encode("ascii", "ignore").decode(), - colkeyname.encode("ascii", "ignore").decode(), - keyname.encode("ascii", "ignore").decode(), - ) + sqltext = "delete from {0} where {1}={2!r}".format( + dbtname.encode("ascii", "ignore").decode(), + colkeyname.encode("ascii", "ignore").decode(), + keyname.encode("ascii", "ignore").decode(), ) + with e.connect() as c: + c.execute(text(sqltext)) + c.commit() @cli.command("db-show", short_help="Show htable records in database") @@ -395,20 +406,20 @@ def htable_dbshow(ctx, oformat, ostyle, dbtname, colkeyname, keyname): e = create_engine(ctx.gconfig.get("db", "rwurl")) if not keyname: ctx.vlog("Showing all htable database records") - res = e.execute( - "select * from {0}".format( - dbtname.encode("ascii", "ignore").decode() - ) + sqlquery = "select * from {0}".format( + dbtname.encode("ascii", "ignore").decode() ) + with e.connect() as c: + res = c.execute(text(sqlquery)) ioutils_dbres_print(ctx, oformat, ostyle, res) else: ctx.vlog("Showing htable database records for key name") - for k in keyname: - res = e.execute( - "select * from {0} where {1}={2!r}".format( + with e.connect() as c: + for k in keyname: + sqlquery = "select * from {0} where {1}={2!r}".format( dbtname.encode("ascii", "ignore").decode(), colkeyname.encode("ascii", "ignore").decode(), k.encode("ascii", "ignore").decode(), ) - ) - ioutils_dbres_print(ctx, oformat, ostyle, res) + res = c.execute(text(sqlquery)) + ioutils_dbres_print(ctx, oformat, ostyle, res) From 2a69dbedd4793558c843d289e1596ab50a29fd39 Mon Sep 17 00:00:00 2001 From: Victor Seva Date: Thu, 5 Mar 2026 13:01:30 +0100 Subject: [PATCH 13/20] cmd_mtree: remove implicit execution --- kamcli/commands/cmd_mtree.py | 53 +++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/kamcli/commands/cmd_mtree.py b/kamcli/commands/cmd_mtree.py index e2a1d09..fc6d51f 100644 --- a/kamcli/commands/cmd_mtree.py +++ b/kamcli/commands/cmd_mtree.py @@ -1,5 +1,6 @@ import click from sqlalchemy import create_engine +from sqlalchemy import text from kamcli.ioutils import ioutils_dbres_print from kamcli.cli import pass_context from kamcli.iorpc import command_ctl @@ -57,13 +58,11 @@ def mtree_dbadd(ctx, tname, coltprefix, coltvalue, dbtname, tprefix, tvalue): prefix = tprefix.encode("ascii", "ignore").decode() val = tvalue.encode("ascii", "ignore").decode() if not tname: - e.execute( - "insert into {0} ({1}, {2}) values ({3!r}, {4!r})".format( - dbname, col_pref, col_val, prefix, val - ) + sqlquery = "insert into {0} ({1}, {2}) values ({3!r}, {4!r})".format( + dbname, col_pref, col_val, prefix, val ) else: - e.execute( + sqlquery = ( "insert into {0} (tname, {1}, {2}) values " "({3!r}, {4!r}, {5!r})".format( dbname, @@ -74,6 +73,9 @@ def mtree_dbadd(ctx, tname, coltprefix, coltvalue, dbtname, tprefix, tvalue): val, ) ) + with e.connect() as c: + c.execute(text(sqlquery)) + c.commit() @cli.command("db-rm", short_help="Remove a record from mtree table") @@ -84,7 +86,11 @@ def mtree_dbadd(ctx, tname, coltprefix, coltvalue, dbtname, tprefix, tvalue): help='Column name for prefix (default: "tprefix")', ) @click.option( - "yes", "--yes", "-y", is_flag=True, help="Do not ask for confirmation", + "yes", + "--yes", + "-y", + is_flag=True, + help="Do not ask for confirmation", ) @click.argument("dbtname", metavar="") @click.argument("tprefix", metavar="") @@ -104,13 +110,14 @@ def mtree_dbrm(ctx, coltprefix, yes, dbtname, tprefix): ctx.vlog("Skip removing prefix [%s]", tprefix) return e = create_engine(ctx.gconfig.get("db", "rwurl")) - e.execute( - "delete from {0} where {1}={2!r}".format( - dbtname.encode("ascii", "ignore").decode(), - coltprefix.encode("ascii", "ignore").decode(), - tprefix.encode("ascii", "ignore").decode(), - ) + sqlquery = "delete from {0} where {1}={2!r}".format( + dbtname.encode("ascii", "ignore").decode(), + coltprefix.encode("ascii", "ignore").decode(), + tprefix.encode("ascii", "ignore").decode(), ) + with e.connect() as c: + c.execute(text(sqlquery)) + c.commit() @cli.command("db-show", short_help="Show mtree records in database") @@ -149,23 +156,25 @@ def mtree_dbshow(ctx, oformat, ostyle, coltprefix, dbtname, tprefix): e = create_engine(ctx.gconfig.get("db", "rwurl")) if not tprefix: ctx.vlog("Showing all tree database records") - res = e.execute( - "select * from {0}".format( - dbtname.encode("ascii", "ignore").decode() - ) + sqlquery = "select * from {0}".format( + dbtname.encode("ascii", "ignore").decode() ) + with e.connect() as c: + res = c.execute(text(sqlquery)) ioutils_dbres_print(ctx, oformat, ostyle, res) else: - for tp in tprefix: - ctx.vlog("Showing tree database records for prefix {0}".format(tp)) - res = e.execute( - "select * from {0} where {1}={2!r}".format( + with e.connect() as c: + for tp in tprefix: + ctx.vlog( + "Showing tree database records for prefix {0}".format(tp) + ) + sqlquery = "select * from {0} where {1}={2!r}".format( dbtname.encode("ascii", "ignore").decode(), coltprefix.encode("ascii", "ignore").decode(), tp.encode("ascii", "ignore").decode(), ) - ) - ioutils_dbres_print(ctx, oformat, ostyle, res) + res = c.execute(text(sqlquery)) + ioutils_dbres_print(ctx, oformat, ostyle, res) @cli.command("list", short_help="Show the records in memory tree") From be48037f238b3bea11ee6e6b96c4883fb4654d2c Mon Sep 17 00:00:00 2001 From: Victor Seva Date: Thu, 5 Mar 2026 13:03:40 +0100 Subject: [PATCH 14/20] cmd_pipelimit: remove implicit execution --- kamcli/commands/cmd_pipelimit.py | 49 +++++++++++++++++++------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/kamcli/commands/cmd_pipelimit.py b/kamcli/commands/cmd_pipelimit.py index 6a1ee0f..ede372f 100644 --- a/kamcli/commands/cmd_pipelimit.py +++ b/kamcli/commands/cmd_pipelimit.py @@ -1,5 +1,6 @@ import click from sqlalchemy import create_engine +from sqlalchemy import text from kamcli.ioutils import ioutils_dbres_print from kamcli.cli import pass_context from kamcli.iorpc import command_ctl @@ -50,11 +51,12 @@ def pipelimit_dbadd(ctx, dbtname, alg, pipeid, limit): dbtval = dbtname.encode("ascii", "ignore").decode() pidval = pipeid.encode("ascii", "ignore").decode() algval = alg.encode("ascii", "ignore").decode() - e.execute( - "insert into {0} (pipeid, algorithm, plimit) values ({1!r}, {2!r}, {3})".format( - dbtval, pidval, algval, limit - ) + sqlquery = "insert into {0} (pipeid, algorithm, plimit) values ({1!r}, {2!r}, {3})".format( + dbtval, pidval, algval, limit ) + with e.connect() as c: + c.execute(text(sqlquery)) + c.commit() @cli.command("db-show", short_help="Show pipelimit records in database") @@ -91,24 +93,26 @@ def pipelimit_dbshow(ctx, oformat, ostyle, dbtname, pipeid): e = create_engine(ctx.gconfig.get("db", "rwurl")) if not pipeid: ctx.vlog("Showing all pipelimit database records") - res = e.execute( - "select * from {0}".format( - dbtname.encode("ascii", "ignore").decode() - ) + sqlquery = "select * from {0}".format( + dbtname.encode("ascii", "ignore").decode() ) + with e.connect() as c: + res = c.execute(text(sqlquery)) ioutils_dbres_print(ctx, oformat, ostyle, res) else: - for p in pipeid: - ctx.vlog( - "Showing pipelimit database records for pipeid {0}".format(p) - ) - res = e.execute( - "select * from {0} where pipeid={1!r}".format( + with e.connect() as c: + for p in pipeid: + ctx.vlog( + "Showing pipelimit database records for pipeid {0}".format( + p + ) + ) + sqlquery = "select * from {0} where pipeid={1!r}".format( dbtname.encode("ascii", "ignore").decode(), p.encode("ascii", "ignore").decode(), ) - ) - ioutils_dbres_print(ctx, oformat, ostyle, res) + res = c.execute(text(sqlquery)) + ioutils_dbres_print(ctx, oformat, ostyle, res) @cli.command("db-rm", short_help="Remove a record from pipelimit table") @@ -119,7 +123,11 @@ def pipelimit_dbshow(ctx, oformat, ostyle, dbtname, pipeid): help='The name of database table (default: "pl_pipes")', ) @click.option( - "yes", "--yes", "-y", is_flag=True, help="Do not ask for confirmation", + "yes", + "--yes", + "-y", + is_flag=True, + help="Do not ask for confirmation", ) @click.argument("pipeid", metavar="") @pass_context @@ -137,12 +145,13 @@ def pipelimit_dbrm(ctx, dbtname, yes, pipeid): ctx.vlog("Skip removing pipe [%s]", pipeid) return e = create_engine(ctx.gconfig.get("db", "rwurl")) - e.execute( - "delete from {0} where pipeid={1!r}".format( + with e.connect() as c: + sqlquery = "delete from {0} where pipeid={1!r}".format( dbtname.encode("ascii", "ignore").decode(), pipeid.encode("ascii", "ignore").decode(), ) - ) + c.execute(text(sqlquery)) + c.commit() @cli.command("list", short_help="List the details of one or all pipes") From f8646d5ae0d7baa8f5cfd7a8a07028f01c2e47da Mon Sep 17 00:00:00 2001 From: Victor Seva Date: Thu, 5 Mar 2026 13:04:46 +0100 Subject: [PATCH 15/20] cmd_rtpengine: remove implicit execution --- kamcli/commands/cmd_rtpengine.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kamcli/commands/cmd_rtpengine.py b/kamcli/commands/cmd_rtpengine.py index 1e44a80..e5bdeca 100644 --- a/kamcli/commands/cmd_rtpengine.py +++ b/kamcli/commands/cmd_rtpengine.py @@ -1,5 +1,6 @@ import click from sqlalchemy import create_engine +from sqlalchemy import text from kamcli.ioutils import ioutils_dbres_print from kamcli.cli import pass_context from kamcli.iorpc import command_ctl @@ -41,7 +42,8 @@ def rtpengine_showdb(ctx, oformat, ostyle): """ e = create_engine(ctx.gconfig.get("db", "rwurl")) ctx.vlog("Showing all rtpengine database records") - res = e.execute("select * from rtpengine") + with e.connect() as c: + res = c.execute(text("select * from rtpengine")) ioutils_dbres_print(ctx, oformat, ostyle, res) From 2aecfbcf11f54c14928cacde27fdc1816ff9b159 Mon Sep 17 00:00:00 2001 From: Victor Seva Date: Thu, 5 Mar 2026 13:07:59 +0100 Subject: [PATCH 16/20] cmd_speeddial: remove implicit execution --- kamcli/commands/cmd_speeddial.py | 80 ++++++++++++++++++++------------ 1 file changed, 50 insertions(+), 30 deletions(-) diff --git a/kamcli/commands/cmd_speeddial.py b/kamcli/commands/cmd_speeddial.py index 5a47856..17b41ac 100644 --- a/kamcli/commands/cmd_speeddial.py +++ b/kamcli/commands/cmd_speeddial.py @@ -1,5 +1,6 @@ import click from sqlalchemy import create_engine +from sqlalchemy import text from kamcli.ioutils import ioutils_dbres_print from kamcli.cli import pass_context from kamcli.cli import parse_user_spec @@ -39,7 +40,9 @@ def cli(ctx): @click.argument("targeturi", metavar="") @click.argument("desc", metavar="", nargs=-1) @pass_context -def speeddial_add(ctx, table, fname, lname, userid, shortdial, targeturi, desc): +def speeddial_add( + ctx, table, fname, lname, userid, shortdial, targeturi, desc +): """Add a speed dial record \b @@ -64,7 +67,7 @@ def speeddial_add(ctx, table, fname, lname, userid, shortdial, targeturi, desc): e = create_engine(ctx.gconfig.get("db", "rwurl")) uri = "sip:{}@{}".format(tdata["username"], tdata["domain"]) if not desc: - e.execute( + sqlquery = ( "insert into {0} (username, domain, sd_username, " "sd_domain, new_uri, fname, lname) values ({1!r}, {2!r}, {3!r}, " "{4!r}, {5!r}, {6!r}, {7!r})".format( @@ -79,7 +82,7 @@ def speeddial_add(ctx, table, fname, lname, userid, shortdial, targeturi, desc): ) ) else: - e.execute( + sqlquery = ( "insert into {0} (username, domain, sd_username, " "sd_domain, new_uri, fname, lname, description) values ({1!r}, {2!r}, {3!r}, " "{4!r}, {5!r}, {6!r}, {7!r}, {8!r})".format( @@ -94,6 +97,9 @@ def speeddial_add(ctx, table, fname, lname, userid, shortdial, targeturi, desc): desc, ) ) + with e.connect() as c: + c.execute(text(sqlquery)) + c.commit() @cli.command("rm", short_help="Remove speed dial records") @@ -122,24 +128,32 @@ def speeddial_rm(ctx, table, userid, shortdial): ) e = create_engine(ctx.gconfig.get("db", "rwurl")) if not shortdial: - e.execute( + sqlquery = ( "delete from {0} where username={1!r} and domain={2!r}".format( - table, udata["username"], udata["domain"], + table, + udata["username"], + udata["domain"], ) ) + with e.connect() as c: + c.execute(text(sqlquery)) + c.commit() else: - for s in shortdial: - sdata = parse_user_spec(ctx, s) - e.execute( - "delete from {0} where username={1!r} and domain={2!r} " - "and sd_username={3!r} and sd_domain={4!r}".format( - table, - udata["username"], - udata["domain"], - sdata["username"], - sdata["domain"], + with e.connect() as c: + for s in shortdial: + sdata = parse_user_spec(ctx, s) + sqlquery = ( + "delete from {0} where username={1!r} and domain={2!r} " + "and sd_username={3!r} and sd_domain={4!r}".format( + table, + udata["username"], + udata["domain"], + sdata["username"], + sdata["domain"], + ) ) - ) + c.execute(text(sqlquery)) + c.commit() @cli.command("show", short_help="Show speed dial records") @@ -184,23 +198,29 @@ def speeddial_show(ctx, oformat, ostyle, table, userid, shortdial): udata["domain"], ) if not shortdial: - res = e.execute( + sqlquery = ( "select * from {0} where username={1!r} and domain={2!r}".format( - table, udata["username"], udata["domain"], + table, + udata["username"], + udata["domain"], ) ) + with e.connect() as c: + res = c.execute(text(sqlquery)) ioutils_dbres_print(ctx, oformat, ostyle, res) else: - for s in shortdial: - sdata = parse_user_spec(ctx, s) - res = e.execute( - "select * from {0} where username={1!r} and domain={2!r} " - "and sd_username={3!r} and sd_domain={4!r}".format( - table, - udata["username"], - udata["domain"], - sdata["username"], - sdata["domain"], + with e.connect() as c: + for s in shortdial: + sdata = parse_user_spec(ctx, s) + sqlquery = ( + "select * from {0} where username={1!r} and domain={2!r} " + "and sd_username={3!r} and sd_domain={4!r}".format( + table, + udata["username"], + udata["domain"], + sdata["username"], + sdata["domain"], + ) ) - ) - ioutils_dbres_print(ctx, oformat, ostyle, res) + res = c.execute(text(sqlquery)) + ioutils_dbres_print(ctx, oformat, ostyle, res) From f3e1a2f7b6e941a5f10b20504c306af28e7cbe2f Mon Sep 17 00:00:00 2001 From: Victor Seva Date: Thu, 5 Mar 2026 13:13:44 +0100 Subject: [PATCH 17/20] cmd_subscriber: remove implicit execution --- kamcli/commands/cmd_subscriber.py | 76 +++++++++++++++++++------------ 1 file changed, 46 insertions(+), 30 deletions(-) diff --git a/kamcli/commands/cmd_subscriber.py b/kamcli/commands/cmd_subscriber.py index c87d122..d132944 100644 --- a/kamcli/commands/cmd_subscriber.py +++ b/kamcli/commands/cmd_subscriber.py @@ -1,6 +1,7 @@ import click import hashlib from sqlalchemy import create_engine +from sqlalchemy import text from kamcli.ioutils import ioutils_dbres_print from kamcli.cli import pass_context from kamcli.cli import parse_user_spec @@ -58,7 +59,7 @@ def subscriber_add(ctx, dbtname, pwtext, userid, password): ha1b = hashlib.md5(dig.encode()).hexdigest() e = create_engine(ctx.gconfig.get("db", "rwurl")) if pwtext == "yes": - e.execute( + sqlquery = ( "insert into {0} (username, domain, password, ha1, ha1b) " "values ({1!r}, {2!r}, {3!r}, {4!r}, {5!r})".format( dbtname.encode("ascii", "ignore").decode(), @@ -70,7 +71,7 @@ def subscriber_add(ctx, dbtname, pwtext, userid, password): ) ) else: - e.execute( + sqlquery = ( "insert into {0} (username, domain, ha1, ha1b) values " "({1!r}, {2!r}, {3!r}, {4!r})".format( dbtname.encode("ascii", "ignore").decode(), @@ -80,6 +81,9 @@ def subscriber_add(ctx, dbtname, pwtext, userid, password): ha1b, ) ) + with e.connect() as c: + c.execute(text(sqlquery)) + c.commit() @cli.command("rm", short_help="Remove an existing subscriber") @@ -115,13 +119,14 @@ def subscriber_rm(ctx, dbtname, yes, userid): udata = parse_user_spec(ctx, userid) ctx.log("Removing subscriber [%s@%s]", udata["username"], udata["domain"]) e = create_engine(ctx.gconfig.get("db", "rwurl")) - e.execute( - "delete from {0} where username={1!r} and domain={2!r}".format( - dbtname.encode("ascii", "ignore").decode(), - udata["username"], - udata["domain"], - ) + sqlquery = "delete from {0} where username={1!r} and domain={2!r}".format( + dbtname.encode("ascii", "ignore").decode(), + udata["username"], + udata["domain"], ) + with e.connect() as c: + c.execute(text(sqlquery)) + c.commit() @cli.command("passwd", short_help="Update the password for a subscriber") @@ -166,7 +171,7 @@ def subscriber_passwd(ctx, dbtname, pwtext, userid, password): ha1b = hashlib.md5(dig.encode()).hexdigest() e = create_engine(ctx.gconfig.get("db", "rwurl")) if pwtext == "yes": - e.execute( + sqlquery = ( "update {0} set password={1!r}, ha1={2!r}, ha1b={3!r} where " "username={4!r} and domain={5!r}".format( dbtname.encode("ascii", "ignore").decode(), @@ -178,7 +183,7 @@ def subscriber_passwd(ctx, dbtname, pwtext, userid, password): ) ) else: - e.execute( + sqlquery = ( "update {0} set ha1={1!r}, ha1b={2!r} where " "username={3!r} and domain={4!r}".format( dbtname, @@ -188,6 +193,9 @@ def subscriber_passwd(ctx, dbtname, pwtext, userid, password): udata["domain"], ) ) + with e.connect() as c: + c.execute(text(sqlquery)) + c.commit() @cli.command("show", short_help="Show details for subscribers") @@ -224,32 +232,31 @@ def subscriber_show(ctx, dbtname, oformat, ostyle, userid): - it can be a list of userids - if not provided then all subscribers are shown """ + e = create_engine(ctx.gconfig.get("db", "rwurl")) if not userid: ctx.vlog("Showing all subscribers") - e = create_engine(ctx.gconfig.get("db", "rwurl")) - res = e.execute( - "select * from {0}".format( - dbtname.encode("ascii", "ignore").decode() - ) + sqlquery = "select * from {0}".format( + dbtname.encode("ascii", "ignore").decode() ) + with e.connect() as c: + res = c.execute(text(sqlquery)) ioutils_dbres_print(ctx, oformat, ostyle, res) else: - for u in userid: - udata = parse_user_spec(ctx, u) - ctx.vlog( - "Showing subscriber [%s@%s]", - udata["username"], - udata["domain"], - ) - e = create_engine(ctx.gconfig.get("db", "rwurl")) - res = e.execute( - "select * from {0} where username={1!r} and domain={2!r}".format( + with e.connect() as c: + for u in userid: + udata = parse_user_spec(ctx, u) + ctx.vlog( + "Showing subscriber [%s@%s]", + udata["username"], + udata["domain"], + ) + sqlquery = "select * from {0} where username={1!r} and domain={2!r}".format( dbtname.encode("ascii", "ignore").decode(), udata["username"], udata["domain"], ) - ) - ioutils_dbres_print(ctx, oformat, ostyle, res) + res = c.execute(text(sqlquery)) + ioutils_dbres_print(ctx, oformat, ostyle, res) @cli.command("setattrs", short_help="Set a string attribute for a subscriber") @@ -283,7 +290,7 @@ def subscriber_setattrs(ctx, dbtname, userid, attr, val): val, ) e = create_engine(ctx.gconfig.get("db", "rwurl")) - e.execute( + sqlquery = ( "update {0} set {1}={2!r} where username={3!r} and " "domain={4!r}".format( dbtname.encode("ascii", "ignore").decode(), @@ -293,6 +300,9 @@ def subscriber_setattrs(ctx, dbtname, userid, attr, val): udata["domain"], ) ) + with e.connect() as c: + c.execute(text(sqlquery)) + c.commit() @cli.command( @@ -328,7 +338,7 @@ def subscriber_setattri(ctx, dbtname, userid, attr, val): val, ) e = create_engine(ctx.gconfig.get("db", "rwurl")) - e.execute( + sqlquery = ( "update {0} set {1}={2} where username={3!r} and " "domain={4!r}".format( dbtname.encode("ascii", "ignore").decode(), @@ -338,6 +348,9 @@ def subscriber_setattri(ctx, dbtname, userid, attr, val): udata["domain"], ) ) + with e.connect() as c: + c.execute(text(sqlquery)) + c.commit() @cli.command( @@ -370,7 +383,7 @@ def subscriber_setattrnull(ctx, dbtname, userid, attr): attr, ) e = create_engine(ctx.gconfig.get("db", "rwurl")) - e.execute( + sqlquery = ( "update {0} set {1}=NULL where username={2!r} and " "domain={3!r}".format( dbtname.encode("ascii", "ignore").decode(), @@ -379,3 +392,6 @@ def subscriber_setattrnull(ctx, dbtname, userid, attr): udata["domain"], ) ) + with e.connect() as c: + c.execute(text(sqlquery)) + c.commit() From 303a36fded34b3795135b043ed756f625cc936ec Mon Sep 17 00:00:00 2001 From: Victor Seva Date: Thu, 5 Mar 2026 23:24:53 +0100 Subject: [PATCH 18/20] cmd_tls: remove implicit execution --- kamcli/commands/cmd_tls.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/kamcli/commands/cmd_tls.py b/kamcli/commands/cmd_tls.py index dcdb2e4..df85eb7 100644 --- a/kamcli/commands/cmd_tls.py +++ b/kamcli/commands/cmd_tls.py @@ -2,6 +2,7 @@ import os import click from sqlalchemy import create_engine +from sqlalchemy import text from kamcli.ioutils import ioutils_dbres_print from kamcli.cli import pass_context from kamcli.iorpc import command_ctl @@ -37,7 +38,8 @@ def tls_showdb(ctx, oformat, ostyle): """ e = create_engine(ctx.gconfig.get("db", "rwurl")) ctx.vlog("Showing all tlscfg records") - res = e.execute("select * from tlscfg") + with e.connect() as c: + res = c.execute(text("select * from tlscfg")) ioutils_dbres_print(ctx, oformat, ostyle, res) @@ -61,7 +63,8 @@ def tls_cfgprint(ctx, odir, cfgpath): """ e = create_engine(ctx.gconfig.get("db", "rwurl")) ctx.vlog("Generating TLS config from database records") - res = e.execute("select * from tlscfg") + with e.connect() as c: + res = c.execute(text("select * from tlscfg")) if cfgpath: cfgpath = cfgpath[0] @@ -76,7 +79,7 @@ def tls_cfgprint(ctx, odir, cfgpath): sys.stdout = cfgsock pcount = 0 - for row in res: + for row in res.mappings(): if pcount > 0: print("\n") @@ -245,7 +248,11 @@ def tls_sqlprint(ctx): short_help="Generate self signed certificates in current directory", ) @click.option( - "domain", "--domain", "-d", default=None, help="Domain of the certificate", + "domain", + "--domain", + "-d", + default=None, + help="Domain of the certificate", ) @click.option( "expiredays", From cd1b227e2ed05e86063d4e47eb0d3629f434dff9 Mon Sep 17 00:00:00 2001 From: Victor Seva Date: Thu, 5 Mar 2026 23:32:38 +0100 Subject: [PATCH 19/20] cmd_uacreg: remove implicit execution --- kamcli/commands/cmd_uacreg.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/kamcli/commands/cmd_uacreg.py b/kamcli/commands/cmd_uacreg.py index 2b27271..ee4992e 100644 --- a/kamcli/commands/cmd_uacreg.py +++ b/kamcli/commands/cmd_uacreg.py @@ -1,5 +1,6 @@ import click from sqlalchemy import create_engine +from sqlalchemy import text from kamcli.ioutils import ioutils_dbres_print from kamcli.cli import pass_context from kamcli.iorpc import command_ctl @@ -85,7 +86,7 @@ def uacreg_add( else: pwval = auth_password e = create_engine(ctx.gconfig.get("db", "rwurl")) - e.execute( + sqlquery = ( "insert into uacreg (l_uuid, l_username, l_domain, r_username, " "r_domain, realm, auth_username, auth_password, auth_ha1, auth_proxy, " "expires, flags, reg_delay, socket) values " @@ -108,6 +109,9 @@ def uacreg_add( socket.encode("ascii", "ignore").decode(), ) ) + with e.connect() as c: + c.execute(text(sqlquery)) + c.commit() @cli.command( @@ -138,7 +142,7 @@ def uacreg_passwd(ctx, realm, authha1, l_uuid, auth_password): else: pwval = auth_password e = create_engine(ctx.gconfig.get("db", "rwurl")) - e.execute( + sqlquery = ( "update uacreg set auth_password={0!r}, auth_ha1={1!r} " "where l_uuid={2!r}".format( pwval.encode("ascii", "ignore").decode(), @@ -146,6 +150,9 @@ def uacreg_passwd(ctx, realm, authha1, l_uuid, auth_password): l_uuid.encode("ascii", "ignore").decode(), ) ) + with e.connect() as c: + c.execute(text(sqlquery)) + c.commit() @cli.command("showdb", short_help="Show dialplan records in database") @@ -176,14 +183,15 @@ def uacreg_showdb(ctx, oformat, ostyle, l_uuid): e = create_engine(ctx.gconfig.get("db", "rwurl")) if not l_uuid: ctx.vlog("Showing all uacreg records") - res = e.execute("select * from uacreg") + with e.connect() as c: + res = c.execute(text("select * from uacreg")) ioutils_dbres_print(ctx, oformat, ostyle, res) else: for record in l_uuid: ctx.vlog("Showing uacreg records for l_uuid: " + record) - res = e.execute( - "select * from uacreg where l_uuid={0!r}".format(record) - ) + sqlquery = "select * from uacreg where l_uuid={0!r}".format(record) + with e.connect() as c: + res = c.execute(text(sqlquery)) ioutils_dbres_print(ctx, oformat, ostyle, res) @@ -205,6 +213,5 @@ def uacreg_list(ctx): ) @pass_context def uacreg_reload(ctx): - """Reload remote registration records from database into memory - """ + """Reload remote registration records from database into memory""" command_ctl(ctx, "uac.reg_reload", []) From 5651f4c061327b549c8792346ad8f9b771a25add Mon Sep 17 00:00:00 2001 From: Victor Seva Date: Thu, 5 Mar 2026 23:37:49 +0100 Subject: [PATCH 20/20] cmd_ul: remove implicit execution --- kamcli/commands/cmd_ul.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/kamcli/commands/cmd_ul.py b/kamcli/commands/cmd_ul.py index f6831a8..2e05c9d 100644 --- a/kamcli/commands/cmd_ul.py +++ b/kamcli/commands/cmd_ul.py @@ -1,5 +1,6 @@ import click from sqlalchemy import create_engine +from sqlalchemy import text from kamcli.ioutils import ioutils_dbres_print from kamcli.ioutils import ioutils_formats_list from kamcli.cli import pass_context @@ -156,10 +157,11 @@ def ul_showdb(ctx, oformat, ostyle, userid): - it can be a list of userids - if not provided then all records are shown """ + e = create_engine(ctx.gconfig.get("db", "rwurl")) if not userid: ctx.vlog("Showing all records") - e = create_engine(ctx.gconfig.get("db", "rwurl")) - res = e.execute("select * from location") + with e.connect() as c: + res = c.execute(text("select * from location")) ioutils_dbres_print(ctx, oformat, ostyle, res) else: for u in userid: @@ -169,10 +171,10 @@ def ul_showdb(ctx, oformat, ostyle, userid): udata["username"], udata["domain"], ) - e = create_engine(ctx.gconfig.get("db", "rwurl")) - res = e.execute( - "select * from location where username={0!r} and domain={1!r}".format( - udata["username"], udata["domain"], - ) + sqlquery = "select * from location where username={0!r} and domain={1!r}".format( + udata["username"], + udata["domain"], ) + with e.connect() as c: + res = c.execute(text(sqlquery)) ioutils_dbres_print(ctx, oformat, ostyle, res)