db.execute_write(executescript=True) option, closes #1569

pull/1589/head
Simon Willison 2021-12-18 10:28:25 -08:00
rodzic 85c22f4fbc
commit 9e094b7c9d
3 zmienionych plików z 31 dodań i 4 usunięć

Wyświetl plik

@ -94,10 +94,14 @@ class Database:
f"file:{self.path}{qs}", uri=True, check_same_thread=False f"file:{self.path}{qs}", uri=True, check_same_thread=False
) )
async def execute_write(self, sql, params=None, block=False): async def execute_write(self, sql, params=None, executescript=False, block=False):
assert not (executescript and params), "Cannot use params with executescript=True"
def _inner(conn): def _inner(conn):
with conn: with conn:
return conn.execute(sql, params or []) if executescript:
return conn.executescript(sql)
else:
return conn.execute(sql, params or [])
with trace("sql", database=self.name, sql=sql.strip(), params=params): with trace("sql", database=self.name, sql=sql.strip(), params=params):
results = await self.execute_write_fn(_inner, block=block) results = await self.execute_write_fn(_inner, block=block)

Wyświetl plik

@ -663,8 +663,8 @@ Example usage:
.. _database_execute_write: .. _database_execute_write:
await db.execute_write(sql, params=None, block=False) await db.execute_write(sql, params=None, executescript=False, block=False)
----------------------------------------------------- --------------------------------------------------------------------------
SQLite only allows one database connection to write at a time. Datasette handles this for you by maintaining a queue of writes to be executed against a given database. Plugins can submit write operations to this queue and they will be executed in the order in which they are received. SQLite only allows one database connection to write at a time. Datasette handles this for you by maintaining a queue of writes to be executed against a given database. Plugins can submit write operations to this queue and they will be executed in the order in which they are received.
@ -676,6 +676,8 @@ By default queries are considered to be "fire and forget" - they will be added t
If you pass ``block=True`` this behaviour changes: the method will block until the write operation has completed, and the return value will be the return from calling ``conn.execute(...)`` using the underlying ``sqlite3`` Python library. If you pass ``block=True`` this behaviour changes: the method will block until the write operation has completed, and the return value will be the return from calling ``conn.execute(...)`` using the underlying ``sqlite3`` Python library.
If you pass ``executescript=True`` your SQL will be executed using the ``sqlite3`` `conn.executescript() <https://docs.python.org/3/library/sqlite3.html#sqlite3.Cursor.executescript>`__ method. This allows multiple SQL statements to be separated by semicolons, but cannot be used with the ``params=`` option.
.. _database_execute_write_fn: .. _database_execute_write_fn:
await db.execute_write_fn(fn, block=False) await db.execute_write_fn(fn, block=False)

Wyświetl plik

@ -396,6 +396,27 @@ async def test_execute_write_block_false(db):
assert "Mystery!" == rows.rows[0][0] assert "Mystery!" == rows.rows[0][0]
@pytest.mark.asyncio
async def test_execute_write_executescript(db):
await db.execute_write(
"create table foo (id integer primary key); create table bar (id integer primary key); ",
executescript=True,
block=True
)
table_names = await db.table_names()
assert {"foo", "bar"}.issubset(table_names)
@pytest.mark.asyncio
async def test_execute_write_executescript_not_allowed_with_params(db):
with pytest.raises(AssertionError):
await db.execute_write(
"update roadside_attractions set name = ? where pk = ?",
["Mystery!", 1],
executescript=True
)
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_execute_write_has_correctly_prepared_connection(db): async def test_execute_write_has_correctly_prepared_connection(db):
# The sleep() function is only available if ds._prepare_connection() was called # The sleep() function is only available if ds._prepare_connection() was called