def execute(*sql, connection=None, script=None, sep='; ', args=None):
"""
Thread-safe SQL execution.
- SELECTs and other read queries run immediately (parallel safe).
- DML/DDL statements (INSERT/UPDATE/DELETE/etc) are funneled into the write queue.
- Multi-statement scripts are supported via executescript.
- All write queue items are always 5-tuple: (sql, args, conn, result_q, is_script)
"""
assert connection, "Pass connection= from gw.sql.open_connection()"
if script:
script_text = gw.resource(script, text=True)
# Recursively call as a multi-statement script
return execute(script_text, connection=connection)
if sql:
sql = sep.join(sql)
else:
raise ValueError("SQL statement required")
# Detect if this is a multi-statement script (very basic: contains semicolon)
# Note: More robust SQL parsing is possible but out of scope here.
stripped_sql = sql.strip().rstrip(";")
is_script = ";" in stripped_sql
# If it is a read-only statement and not a script, execute directly
if not _is_write_query(sql) and not is_script:
cursor = connection.cursor()
try:
cursor.execute(sql, args or ())
return cursor.fetchall() if cursor.description else None
finally:
cursor.close()
else:
# All writes or scripts are serialized via the queue.
result_q = queue.Queue()
# Always enqueue a 5-item tuple: (sql, args, conn, result_q, is_script)
_write_queue.put((sql, args, connection._connection, result_q, is_script))
rows, error = result_q.get()
if error:
raise error
return rows