{"sha": "186c513a61a091b9f83d788e25b08f41a84ed9a3", "message": "Support parameterized SQL and block potentially harmful queries\n\nYou can now call arbitrary SQL like this:\n\n /flights?sql=select%20*%20from%20airports%20where%20country%20like%20:c&c=iceland\n\nUnescaped, those querystring params look like this:\n\n sql = select * from airports where country like :c\n c = iceland\n\nSo SQL can be constructed with named parameters embedded in it, which will\nthen be read from the querystring and correctly escaped.\n\nThis means we can aggressively filter the SQL parameter for potentially\ndangerous syntax. For the moment we enforce that it starts with a SELECT\nstatement and we ban the sequence \"pragma\" from it entirely.\n\nIf you need to use pragma in a query, you can use the new named parameter\nmechanism.\n\nFixes #39", "author_date": "2017-11-05T02:49:18Z", "committer_date": "2017-11-05T02:49:18Z", "raw_author": {"value": "2946d096d0cdefdc017559e6b57e87658736e843", "label": "Simon Willison"}, "raw_committer": {"value": "2946d096d0cdefdc017559e6b57e87658736e843", "label": "Simon Willison"}, "repo": {"value": 107914493, "label": "datasette"}, "author": {"value": 9599, "label": "simonw"}, "committer": {"value": 9599, "label": "simonw"}} {"sha": "1fc75809a6aa17860944b4cc3a4d7175cd53b1f4", "message": "Refactored everything into a factory function\n\nI now call a factory function to construct the Sanic app:\n\n app = app_factory(files)\n\nThis allows me to pass additional arguments to it, e.g. the files to serve.\n\nAlso refactored my class-based views to accept jinja as an argument, e.g:\n\n app.add_route(\n TableView.as_view(jinja),\n '//'\n )", "author_date": "2017-11-05T02:13:44Z", "committer_date": "2017-11-05T02:13:44Z", "raw_author": {"value": "2946d096d0cdefdc017559e6b57e87658736e843", "label": "Simon Willison"}, "raw_committer": {"value": "2946d096d0cdefdc017559e6b57e87658736e843", "label": "Simon Willison"}, "repo": {"value": 107914493, "label": "datasette"}, "author": {"value": 9599, "label": "simonw"}, "committer": {"value": 9599, "label": "simonw"}} {"sha": "31b21f5c5e15fc3acab7fabb170c1da71dc3c98c", "message": "Moved all SQLite queries to threads\n\nSQLite operations are blocking, but we're running everything in Sanic, an\nasyncio web framework, so blocking operations are bad - a long-running DB\noperation could hold up the entire server.\n\nInstead, I've moved all SQLite operations into threads. These are managed by a\nconcurrent.futures ThreadPoolExecutor. This means I can run up to X queries in\nparallel, and I can continue to queue up additional incoming HTTP traffic\nwhile the threadpool is busy.\n\nEach thread is responsible for managing its own SQLite connections - one per\ndatabase. These are cached in a threadlocal.\n\nSince we are working with immutable, read-only SQLite databases it should be\nsafe to share SQLite objects across threads. On this assumption I'm using the\ncheck_same_thread=False option. Opening a database connection looks like this:\n\n conn = sqlite3.connect(\n 'file:filename.db?immutable=1',\n uri=True,\n check_same_thread=False,\n )\n\nThe following articles were helpful in figuring this out:\n\n* https://pymotw.com/3/asyncio/executors.html\n* https://marlinux.wordpress.com/2017/05/19/python-3-6-asyncio-sqlalchemy/\n\nCloses #45. Refs #38.", "author_date": "2017-11-05T02:21:44Z", "committer_date": "2017-11-05T02:21:44Z", "raw_author": {"value": "2946d096d0cdefdc017559e6b57e87658736e843", "label": "Simon Willison"}, "raw_committer": {"value": "2946d096d0cdefdc017559e6b57e87658736e843", "label": "Simon Willison"}, "repo": {"value": 107914493, "label": "datasette"}, "author": {"value": 9599, "label": "simonw"}, "committer": {"value": 9599, "label": "simonw"}}