Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

- Updated fetch benchmark for sqlite.Row

- Each c file has now a function for setting up the type(s) it implements
- Implemented a function that resets all statements in the connection's
  statement cache. After calling this function it is always possible to
  rollback a transaction or close a connection.
- new shortcuts: the connection object gets execute() and executemany() methods
- many bugfixes for statement caching and the new Row type
- moved the handling of row_factory to a different place
- use SQLite's standard busy timeout handler instead of our own solution to
  hopefully improve behaviour for concurrent database access
  • Loading branch information...
commit 5a009ed6fb2e90b952438f5786f93cd1e8ac8722 1 parent 8069c5d
gh authored
View
8 benchmarks/fetch.py
@@ -8,6 +8,7 @@ def yesno(question):
if use_pysqlite2:
use_custom_types = yesno("Use custom types?")
use_dictcursor = yesno("Use dict cursor?")
+ use_rowcursor = yesno("Use row cursor?")
else:
use_tuple = yesno("Use rowclass=tuple?")
@@ -34,6 +35,11 @@ def __init__(self, *args, **kwargs):
sqlite.Cursor.__init__(self, *args, **kwargs)
self.row_factory = dict_factory
+ class RowCursor(sqlite.Cursor):
+ def __init__(self, *args, **kwargs):
+ sqlite.Cursor.__init__(self, *args, **kwargs)
+ self.row_factory = sqlite.Row
+
def create_db():
if sqlite.version_info > (2, 0):
if use_custom_types:
@@ -43,6 +49,8 @@ def create_db():
con = sqlite.connect(":memory:")
if use_dictcursor:
cur = con.cursor(factory=DictCursor)
+ elif use_rowcursor:
+ cur = con.cursor(factory=RowCursor)
else:
cur = con.cursor()
else:
View
17 src/cache.c
@@ -50,6 +50,7 @@ void node_dealloc(Node* self)
{
Py_DECREF(self->key);
Py_DECREF(self->data);
+
self->ob_type->tp_free((PyObject*)self);
}
@@ -324,3 +325,19 @@ PyTypeObject CacheType = {
0, /* tp_new */
0 /* tp_free */
};
+
+extern int cache_setup_types(void)
+{
+ int rc;
+
+ NodeType.tp_new = PyType_GenericNew;
+ CacheType.tp_new = PyType_GenericNew;
+
+ rc = PyType_Ready(&NodeType);
+ if (rc < 0) {
+ return rc;
+ }
+
+ rc = PyType_Ready(&CacheType);
+ return rc;
+}
View
2  src/cache.h
@@ -56,4 +56,6 @@ int cache_init(Cache* self, PyObject* args, PyObject* kwargs);
void cache_dealloc(Cache* self);
PyObject* cache_get(Cache* self, PyObject* args);
+int cache_setup_types(void);
+
#endif
View
92 src/connection.c
@@ -35,6 +35,7 @@ static int connection_set_isolation_level(Connection* self, PyObject* isolation_
int connection_init(Connection* self, PyObject* args, PyObject* kwargs)
{
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 = NULL;
@@ -88,6 +89,8 @@ int connection_init(Connection* self, PyObject* args, PyObject* kwargs)
self->inTransaction = 0;
self->detect_types = detect_types;
self->timeout = timeout;
+ (void)sqlite3_busy_timeout(self->db, (int)(timeout*1000));
+
self->thread_ident = PyThread_get_thread_ident();
self->check_same_thread = check_same_thread;
@@ -126,6 +129,20 @@ void flush_statement_cache(Connection* self)
self->statement_cache->decref_factory = 0;
}
+void reset_all_statements(Connection* self)
+{
+ Node* node;
+ Statement* statement;
+
+ node = self->statement_cache->first;
+
+ while (node) {
+ statement = (Statement*)(node->data);
+ (void)statement_reset(statement);
+ node = node->next;
+ }
+}
+
void connection_dealloc(Connection* self)
{
/* Clean up if user has not called .close() explicitly. */
@@ -307,6 +324,8 @@ PyObject* connection_rollback(Connection* self, PyObject* args)
}
if (self->inTransaction) {
+ reset_all_statements(self);
+
Py_BEGIN_ALLOW_THREADS
rc = sqlite3_prepare(self->db, "ROLLBACK", -1, &statement, &tail);
Py_END_ALLOW_THREADS
@@ -719,6 +738,68 @@ PyObject* connection_call(Connection* self, PyObject* args, PyObject* kwargs)
return (PyObject*)statement;
}
+PyObject* connection_execute(Connection* self, PyObject* args, PyObject* kwargs)
+{
+ PyObject* cursor = 0;
+ PyObject* result = 0;
+ PyObject* method = 0;
+
+ cursor = PyObject_CallMethod((PyObject*)self, "cursor", "");
+ if (!cursor) {
+ goto error;
+ }
+
+ method = PyObject_GetAttrString(cursor, "execute");
+ if (!method) {
+ Py_DECREF(cursor);
+ cursor = 0;
+ goto error;
+ }
+
+ result = PyObject_Call(method, args, kwargs);
+ if (!result) {
+ Py_DECREF(cursor);
+ cursor = 0;
+ }
+
+error:
+ Py_XDECREF(result);
+ Py_XDECREF(method);
+
+ return cursor;
+}
+
+PyObject* connection_executemany(Connection* self, PyObject* args, PyObject* kwargs)
+{
+ PyObject* cursor = 0;
+ PyObject* result = 0;
+ PyObject* method = 0;
+
+ cursor = PyObject_CallMethod((PyObject*)self, "cursor", "");
+ if (!cursor) {
+ goto error;
+ }
+
+ method = PyObject_GetAttrString(cursor, "executemany");
+ if (!method) {
+ Py_DECREF(cursor);
+ cursor = 0;
+ goto error;
+ }
+
+ result = PyObject_Call(method, args, kwargs);
+ if (!result) {
+ Py_DECREF(cursor);
+ cursor = 0;
+ }
+
+error:
+ Py_XDECREF(result);
+ Py_XDECREF(method);
+
+ return cursor;
+}
+
static char connection_doc[] =
PyDoc_STR("<missing docstring>");
@@ -740,6 +821,10 @@ static PyMethodDef connection_methods[] = {
PyDoc_STR("Creates a new function.")},
{"create_aggregate", (PyCFunction)connection_create_aggregate, METH_VARARGS|METH_KEYWORDS,
PyDoc_STR("Creates a new aggregate.")},
+ {"execute", (PyCFunction)connection_execute, METH_VARARGS,
+ PyDoc_STR("Executes a SQL statement.")},
+ {"executemany", (PyCFunction)connection_executemany, METH_VARARGS,
+ PyDoc_STR("Repeatedly executes a SQL statement.")},
{NULL, NULL}
};
@@ -800,4 +885,9 @@ PyTypeObject ConnectionType = {
0, /* tp_new */
0 /* tp_free */
};
-
+
+extern int connection_setup_types(void)
+{
+ ConnectionType.tp_new = PyType_GenericNew;
+ return PyType_Ready(&ConnectionType);
+}
View
3  src/connection.h
@@ -36,6 +36,7 @@ typedef struct
{
PyObject_HEAD
sqlite3* db;
+
int inTransaction;
int detect_types;
@@ -93,4 +94,6 @@ int connection_init(Connection* self, PyObject* args, PyObject* kwargs);
int check_thread(Connection* self);
int check_connection(Connection* con);
+int connection_setup_types(void);
+
#endif
View
46 src/cursor.c
@@ -114,6 +114,7 @@ void cursor_dealloc(Cursor* self)
/* Reset the statement if the user has not closed the cursor */
if (self->statement) {
rc = statement_reset(self->statement);
+ Py_DECREF(self->statement);
}
Py_XDECREF(self->connection);
@@ -123,7 +124,6 @@ void cursor_dealloc(Cursor* self)
Py_XDECREF(self->rowcount);
Py_XDECREF(self->row_factory);
Py_XDECREF(self->next_row);
- Py_XDECREF(self->statement);
self->ob_type->tp_free((PyObject*)self);
}
@@ -257,7 +257,6 @@ PyObject* _fetch_one_row(Cursor* self)
{
int i, numcols;
PyObject* row;
- PyObject* converted_row;
PyObject* item = NULL;
int coltype;
PY_LONG_LONG intval;
@@ -340,14 +339,7 @@ PyObject* _fetch_one_row(Cursor* self)
if (PyErr_Occurred()) {
return NULL;
} else {
- if (self->row_factory != Py_None) {
- converted_row = PyObject_CallFunction(self->row_factory, "OO", self, row);
- Py_DECREF(row);
- } else {
- converted_row = row;
- }
-
- return converted_row;
+ return row;
}
}
@@ -496,7 +488,12 @@ PyObject* _query_execute(Cursor* self, int multiple, PyObject* args)
func_args = PyTuple_New(1);
Py_INCREF(operation);
PyTuple_SetItem(func_args, 0, operation);
- Py_XDECREF(self->statement);
+
+ if (self->statement) {
+ (void)statement_reset(self->statement);
+ Py_DECREF(self->statement);
+ }
+
self->statement = (Statement*)cache_get(self->connection->statement_cache, func_args);
Py_DECREF(func_args);
@@ -619,6 +616,10 @@ PyObject* _query_execute(Cursor* self, int multiple, PyObject* args)
}
self->next_row = _fetch_one_row(self);
+ } else if (rc == SQLITE_DONE && !multiple) {
+ statement_reset(self->statement);
+ Py_DECREF(self->statement);
+ self->statement = 0;
}
switch (statement_type) {
@@ -764,6 +765,7 @@ PyObject* cursor_getiter(Cursor *self)
PyObject* cursor_iternext(Cursor *self)
{
+ PyObject* next_row_tuple;
PyObject* next_row;
int rc;
@@ -780,9 +782,16 @@ PyObject* cursor_iternext(Cursor *self)
return NULL;
}
- next_row = self->next_row;
+ next_row_tuple = self->next_row;
self->next_row = NULL;
+ if (self->row_factory != Py_None) {
+ next_row = PyObject_CallFunction(self->row_factory, "OO", self, next_row_tuple);
+ Py_DECREF(next_row_tuple);
+ } else {
+ next_row = next_row_tuple;
+ }
+
rc = _sqlite_step_with_busyhandler(self->statement->st, self->connection);
if (rc != SQLITE_DONE && rc != SQLITE_ROW) {
Py_DECREF(next_row);
@@ -887,8 +896,11 @@ PyObject* cursor_close(Cursor* self, PyObject* args)
return NULL;
}
- Py_XDECREF(self->statement);
- self->statement = 0;
+ if (self->statement) {
+ (void)statement_reset(self->statement);
+ Py_DECREF(self->statement);
+ self->statement = 0;
+ }
Py_INCREF(Py_None);
return Py_None;
@@ -969,3 +981,9 @@ PyTypeObject CursorType = {
0, /* tp_new */
0 /* tp_free */
};
+
+extern int cursor_setup_types(void)
+{
+ CursorType.tp_new = PyType_GenericNew;
+ return PyType_Ready(&CursorType);
+}
View
2  src/cursor.h
@@ -65,5 +65,7 @@ PyObject* cursor_fetchall(Cursor* self, PyObject* args);
PyObject* pysqlite_noop(Connection* self, PyObject* args);
PyObject* cursor_close(Cursor* self, PyObject* args);
+int cursor_setup_types(void);
+
#define UNKNOWN (-1)
#endif
View
37 src/module.c
@@ -152,34 +152,14 @@ PyMODINIT_FUNC init_sqlite(void)
module = Py_InitModule("pysqlite2._sqlite", module_methods);
- ConnectionType.tp_new = PyType_GenericNew;
- CursorType.tp_new = PyType_GenericNew;
- NodeType.tp_new = PyType_GenericNew;
- CacheType.tp_new = PyType_GenericNew;
- StatementType.tp_new = PyType_GenericNew;
- row_initmodule();
- RowType.tp_new = PyType_GenericNew;
- SQLitePrepareProtocolType.tp_new = PyType_GenericNew;
- SQLitePrepareProtocolType.ob_type = &PyType_Type;
- if (PyType_Ready(&NodeType) < 0) {
- return;
- }
- if (PyType_Ready(&CacheType) < 0) {
- return;
- }
- if (PyType_Ready(&ConnectionType) < 0) {
- return;
- }
- if (PyType_Ready(&CursorType) < 0) {
- return;
- }
- if (PyType_Ready(&SQLitePrepareProtocolType) == -1) {
- return;
- }
- if (PyType_Ready(&StatementType) == -1) {
- return;
- }
- if (PyType_Ready(&RowType) == -1) {
+ if (
+ (row_setup_types() < 0) ||
+ (cursor_setup_types() < 0) ||
+ (connection_setup_types() < 0) ||
+ (cache_setup_types() < 0) ||
+ (statement_setup_types() < 0) ||
+ (prepare_protocol_setup_types() < 0)
+ ) {
return;
}
@@ -274,4 +254,3 @@ PyMODINIT_FUNC init_sqlite(void)
PyErr_SetString(PyExc_ImportError, "pysqlite2._sqlite: init failed");
}
}
-
View
7 src/prepare_protocol.c
@@ -75,3 +75,10 @@ PyTypeObject SQLitePrepareProtocolType= {
0, /* tp_new */
0 /* tp_free */
};
+
+extern int prepare_protocol_setup_types(void)
+{
+ SQLitePrepareProtocolType.tp_new = PyType_GenericNew;
+ SQLitePrepareProtocolType.ob_type= &PyType_Type;
+ return PyType_Ready(&SQLitePrepareProtocolType);
+}
View
2  src/prepare_protocol.h
@@ -35,5 +35,7 @@ extern PyTypeObject SQLitePrepareProtocolType;
int prepare_protocol_init(SQLitePrepareProtocol* self, PyObject* args, PyObject* kwargs);
void prepare_protocol_dealloc(SQLitePrepareProtocol* self);
+int prepare_protocol_setup_types(void);
+
#define UNKNOWN (-1)
#endif
View
38 src/row.c
@@ -1,6 +1,6 @@
/* row.c - an enhanced tuple for database rows
*
- * Copyright (C) 2004-2005 Gerhard Häring <gh@ghaering.de>
+ * Copyright (C) 2005 Gerhard Häring <gh@ghaering.de>
*
* This file is part of pysqlite.
*
@@ -26,8 +26,8 @@
void row_dealloc(Row* self)
{
- Py_DECREF(self->data);
- Py_DECREF(self->description);
+ Py_XDECREF(self->data);
+ Py_XDECREF(self->description);
self->ob_type->tp_free((PyObject*)self);
}
@@ -37,18 +37,29 @@ int row_init(Row* self, PyObject* args, PyObject* kwargs)
PyObject* data;
Cursor* cursor;
+ self->data = 0;
+ self->description = 0;
+
if (!PyArg_ParseTuple(args, "OO", &cursor, &data)) {
return -1;
}
- /* TODO: check if really cursor */
-
+ if (!PyObject_IsInstance((PyObject*)cursor, (PyObject*)&CursorType)) {
+ PyErr_SetString(PyExc_TypeError, "instance of cursor required for first argument");
+ return -1;
+ }
+
+ if (!PyTuple_Check(data)) {
+ PyErr_SetString(PyExc_TypeError, "tuple required for second argument");
+ return -1;
+ }
+
Py_INCREF(data);
self->data = data;
Py_INCREF(cursor->description);
self->description = cursor->description;
-
+
return 0;
}
@@ -62,9 +73,13 @@ PyObject* row_subscript(Row* self, PyObject* idx)
char* p1;
char* p2;
+ PyObject* item;
+
if (PyInt_Check(idx)) {
_idx = PyInt_AsLong(idx);
- return PyTuple_GetItem(self->data, _idx);
+ item = PyTuple_GetItem(self->data, _idx);
+ Py_INCREF(item);
+ return item;
} else if (PyString_Check(idx)) {
key = PyString_AsString(idx);
@@ -91,7 +106,9 @@ PyObject* row_subscript(Row* self, PyObject* idx)
if ((*p1 == (char)0) && (*p2 == (char)0)) {
/* found item */
- return PyTuple_GetItem(self->data, i);
+ item = PyTuple_GetItem(self->data, i);
+ Py_INCREF(item);
+ return item;
}
}
@@ -100,6 +117,7 @@ PyObject* row_subscript(Row* self, PyObject* idx)
return NULL;
} else if (PySlice_Check(idx)) {
PyErr_SetString(PyExc_ValueError, "slices not implemented, yet");
+ return NULL;
} else {
PyErr_SetString(PyExc_IndexError, "Index must be int or string");
return NULL;
@@ -167,7 +185,9 @@ PyTypeObject RowType = {
0 /* tp_free */
};
-void row_initmodule()
+extern int row_setup_types(void)
{
+ RowType.tp_new = PyType_GenericNew;
RowType.tp_as_mapping = &row_as_mapping;
+ return PyType_Ready(&RowType);
}
View
5 src/row.h
@@ -1,6 +1,6 @@
/* row.h - an enhanced tuple for database rows
*
- * Copyright (C) 2004-2005 Gerhard Häring <gh@ghaering.de>
+ * Copyright (C) 2005 Gerhard Häring <gh@ghaering.de>
*
* This file is part of pysqlite.
*
@@ -33,5 +33,8 @@ typedef struct _Row
} Row;
extern PyTypeObject RowType;
+extern PyMappingMethods row_as_mapping;
+
+int row_setup_types(void);
#endif
View
7 src/statement.c
@@ -84,6 +84,7 @@ int statement_reset(Statement* self)
int rc;
rc = SQLITE_OK;
+
if (self->in_use && self->st) {
Py_BEGIN_ALLOW_THREADS
rc = sqlite3_reset(self->st);
@@ -227,3 +228,9 @@ PyTypeObject StatementType = {
0, /* tp_new */
0 /* tp_free */
};
+
+extern int statement_setup_types(void)
+{
+ StatementType.tp_new = PyType_GenericNew;
+ return PyType_Ready(&StatementType);
+}
View
2  src/statement.h
@@ -46,4 +46,6 @@ int statement_finalize(Statement* self);
int statement_reset(Statement* self);
void statement_mark_dirty(Statement* self);
+int statement_setup_types(void);
+
#endif
View
33 src/util.c
@@ -48,44 +48,15 @@ double pysqlite_time(void)
return time;
}
-int _sqlite_step_with_busyhandler(sqlite3_stmt* statement, Connection* connection)
+int _sqlite_step_with_busyhandler(sqlite3_stmt* statement, Connection* connection
+)
{
- int counter = 0;
int rc;
- double how_long;
Py_BEGIN_ALLOW_THREADS
rc = sqlite3_step(statement);
Py_END_ALLOW_THREADS
- if (rc != SQLITE_BUSY) {
- return rc;
- }
-
- connection->timeout_started = pysqlite_time();
- while (1) {
- Py_BEGIN_ALLOW_THREADS
- rc = sqlite3_step(statement);
- Py_END_ALLOW_THREADS
- if (rc != SQLITE_BUSY) {
- break;
- }
-
- if (pysqlite_time() - connection->timeout_started > connection->timeout) {
- break;
- }
-
- how_long = 0.01 * (1 << counter);
- pysqlite_sleep(how_long);
-
- if (counter < 7) {
- /* Make sure the sleep interval does not grow indefinitely.
- * This way, there is a cap of 0.01 ** 7 == 1.28 seconds.
- */
- counter++;
- }
- }
-
return rc;
}
Please sign in to comment.
Something went wrong with that request. Please try again.