#10320 closed New feature (fixed)
CursorDebugWrapper should allow using iterators/generators for executemany().
| Reported by: | Vadim Fint | Owned by: | nobody |
|---|---|---|---|
| Component: | Database layer (models, ORM) | Version: | 1.0 |
| Severity: | Normal | Keywords: | |
| Cc: | Triage Stage: | Accepted | |
| Has patch: | yes | Needs documentation: | no |
| Needs tests: | yes | Patch needs improvement: | yes |
| Easy pickings: | no | UI/UX: | no |
Description
executemany on cursors can have a little speedup on huge lists if developer sends iterators or generators as args. Thus, they will have no len. CursorDebugWrapper tries to determine length for only one reason: put a record about number of executed queries into connection.queries.
It's good idea probably to determine if args have no len -- dont put info about amount of executed queries into connection.queries.
Right now I can do connection.cursor().cursor.executemany(sql, iterator) for that purpose. I mean -- python db wrappers usually allow iterators in executemany's args. Another ugly way right now - catch TypeError and silently ignore. This is because django do len() after calling executemany on underlying cursor =). Non-pythonic, yeah?
Change History (9)
comment:1 by , 17 years ago
comment:2 by , 17 years ago
| Has patch: | set |
|---|
comment:4 by , 17 years ago
| Triage Stage: | Unreviewed → Design decision needed |
|---|
comment:5 by , 15 years ago
| Severity: | → Normal |
|---|---|
| Type: | → New feature |
comment:6 by , 14 years ago
| Easy pickings: | unset |
|---|---|
| UI/UX: | unset |
Strictly speaking, PEP 249 defines executemany(operation, seq_of_parameters), where seq_of_parameters is a sequence, and len is always available on sequences.
However, three out of four database backends support calling Cursor.executemany with an iterator as second argument:
- The built-in
sqlite3module also allows using an iterator yielding parameters instead of a sequence. - It's clear from the source of pyscopg2, in
cursor_type.c, thatpsycopg.Cursor.executemanyaccepts iterators, and if it receives a non-iterable, it actually converts it to an iterator --if (!PyIter_Check(vars)) { vars = iter = PyObject_GetIter(vars); ... }. MySQLdb.Cursor.executemanyis implemented in Python, and the two possible code paths usefor row in args, which clearly works with iterators.- Only
cx_Oracle.Cursor.executemanyfails to accept an iterator as argument: inCursor.c,Cursor_ExecuteManydoesnumRows = PyList_GET_SIZE(listOfArguments);
So, it would be a good idea to support iterators in CursorDebugWrapper. Note that the performance argument is irrelevant for CursorDebugWrapper itself, since it's only used when DEBUG=True. The real reason for the fix is that CursorDebugWrapper shouldn't be more restrictive than regular cursors.
NB: django.db.backends.oracle.base.FormatStylePlaceholderCursor also relies on params not being a iterator, since it tries to access params[0]. But that could be fixed by adding something along the lines of if params is not None: params = list(params).
comment:7 by , 14 years ago
| Needs tests: | set |
|---|---|
| Patch needs improvement: | set |
| Triage Stage: | Design decision needed → Accepted |
Easiest fixup:
django/db/backends/util.py
, sql),Because param_list may be iterator and already iterated iterator (hmm =)) -- there is no way to determine it's length after underlying executemany() cursor call. Thus, -- '??' is okay, imho :).