diff -r 0df85a2ebe4d Doc/library/sqlite3.rst --- a/Doc/library/sqlite3.rst Wed Jan 11 03:32:43 2012 -0500 +++ b/Doc/library/sqlite3.rst Wed Jan 11 21:22:19 2012 +0100 @@ -136,7 +136,7 @@ first blank for the column name: the column name would simply be "x". -.. function:: connect(database[, timeout, detect_types, isolation_level, check_same_thread, factory, cached_statements]) +.. function:: connect(database[, timeout, detect_types, isolation_level, check_same_thread, factory, cached_statements, uri]) Opens a connection to the SQLite database file *database*. You can use ``":memory:"`` to open a database connection to a database that resides in RAM @@ -172,6 +172,12 @@ for the connection, you can set the *cached_statements* parameter. The currently implemented default is to cache 100 statements. + If *uri* is :const:`True`, *database* is interpreted as a URI. This allows you + to specify options. For example, to open a database in read-only mode you can + use:: + + db = sqlite3.connect('file:path/to/database?mode=ro', uri=True) + .. function:: register_converter(typename, callable) diff -r 0df85a2ebe4d Lib/sqlite3/test/dbapi.py --- a/Lib/sqlite3/test/dbapi.py Wed Jan 11 03:32:43 2012 -0500 +++ b/Lib/sqlite3/test/dbapi.py Wed Jan 11 21:22:19 2012 +0100 @@ -22,6 +22,8 @@ # 3. This notice may not be removed or altered from any source distribution. import unittest +import tempfile +import os import sqlite3 as sqlite try: import threading @@ -162,6 +164,21 @@ def CheckInTransactionRO(self): with self.assertRaises(AttributeError): self.cx.in_transaction = True + + def CheckOpenUri(self): + fd, fn = tempfile.mkstemp(prefix="test_sqlite3-") + os.close(fd) + try: + with sqlite.connect(fn) as cx: + cx.execute('create table test(id integer)') + with sqlite.connect('file:' + fn, uri=True) as cx: + cx.execute('insert into test(id) values(0)') + with sqlite.connect('file:' + fn + '?mode=ro', uri=True) as cx: + with self.assertRaises(sqlite.OperationalError): + cx.execute('insert into test(id) values(1)') + finally: + os.remove(fn) + class CursorTests(unittest.TestCase): def setUp(self): diff -r 0df85a2ebe4d Modules/_sqlite/connection.c --- a/Modules/_sqlite/connection.c Wed Jan 11 03:32:43 2012 -0500 +++ b/Modules/_sqlite/connection.c Wed Jan 11 21:22:19 2012 +0100 @@ -60,7 +60,11 @@ int pysqlite_connection_init(pysqlite_Connection* self, PyObject* args, PyObject* kwargs) { - static char *kwlist[] = {"database", "timeout", "detect_types", "isolation_level", "check_same_thread", "factory", "cached_statements", NULL, NULL}; + static char *kwlist[] = {"database", "timeout", "detect_types", "isolation_level", "check_same_thread", "factory", "cached_statements", +#ifdef SQLITE_OPEN_URI + "uri", +#endif + NULL, NULL}; char* database; int detect_types = 0; @@ -68,11 +72,24 @@ PyObject* factory = NULL; int check_same_thread = 1; int cached_statements = 100; +#ifdef SQLITE_OPEN_URI + int uri = 0; +#endif double timeout = 5.0; int rc; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|diOiOi", kwlist, - &database, &timeout, &detect_types, &isolation_level, &check_same_thread, &factory, &cached_statements)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, +#ifdef SQLITE_OPEN_URI + "s|diOiOii", +#else + "s|diOiOi", +#endif + kwlist, + &database, &timeout, &detect_types, &isolation_level, &check_same_thread, &factory, &cached_statements +#ifdef SQLITE_OPEN_URI + , &uri +#endif + )) { return -1; } @@ -92,7 +109,11 @@ self->text_factory = (PyObject*)&PyUnicode_Type; Py_BEGIN_ALLOW_THREADS +#ifdef SQLITE_OPEN_URI + rc = sqlite3_open_v2(database, &self->db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | (uri ? SQLITE_OPEN_URI : 0), NULL); +#else rc = sqlite3_open(database, &self->db); +#endif Py_END_ALLOW_THREADS if (rc != SQLITE_OK) { diff -r 0df85a2ebe4d Modules/_sqlite/module.c --- a/Modules/_sqlite/module.c Wed Jan 11 03:32:43 2012 -0500 +++ b/Modules/_sqlite/module.c Wed Jan 11 21:22:19 2012 +0100 @@ -46,28 +46,14 @@ static PyObject* module_connect(PyObject* self, PyObject* args, PyObject* kwargs) { - /* Python seems to have no way of extracting a single keyword-arg at - * C-level, so this code is redundant with the one in connection_init in - * connection.c and must always be copied from there ... */ - - static char *kwlist[] = {"database", "timeout", "detect_types", "isolation_level", "check_same_thread", "factory", "cached_statements", NULL, NULL}; - char* database; - int detect_types = 0; - PyObject* isolation_level; PyObject* factory = NULL; - int check_same_thread = 1; - int cached_statements; - double timeout = 5.0; - PyObject* result; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|diOiOi", kwlist, - &database, &timeout, &detect_types, &isolation_level, &check_same_thread, &factory, &cached_statements)) - { - return NULL; + if (kwargs) { + factory = PyDict_GetItemString(kwargs, "factory"); } - if (factory == NULL) { + if (factory == NULL || factory == Py_None) { factory = (PyObject*)&pysqlite_ConnectionType; } @@ -77,7 +63,7 @@ } PyDoc_STRVAR(module_connect_doc, -"connect(database[, timeout, isolation_level, detect_types, factory])\n\ +"connect(database[, timeout, detect_types, isolation_level, check_same_thread, factory, cached_statements, uri])\n\ \n\ Opens a connection to the SQLite database file *database*. You can use\n\ \":memory:\" to open a database connection to a database that resides in\n\