Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stdlib sqlite3 executemany() does not support RETURNING statement #100021

Closed
zentarim opened this issue Dec 5, 2022 · 9 comments · Fixed by #103939
Closed

stdlib sqlite3 executemany() does not support RETURNING statement #100021

zentarim opened this issue Dec 5, 2022 · 9 comments · Fixed by #103939
Assignees
Labels
docs Documentation in the Doc dir topic-sqlite3 type-bug An unexpected behavior, bug, or error

Comments

@zentarim
Copy link
Contributor

zentarim commented Dec 5, 2022

Problem

sqlite3.Connection.executemany() does not support a RETURNING statement. All requests containing it fail with an exception:
sqlite3.ProgrammingError: executemany() can only execute DML statements.

Working shell example

#!/bin/sh
rm -f ./test.db
sqlite3 ./test.db  <<EOF
CREATE TABLE releases (
component VARCHAR(64) NOT NULL, 
version VARCHAR(64) NOT NULL, 
os VARCHAR(64) NOT NULL, 
PRIMARY KEY (component, version, os));
INSERT INTO releases VALUES('server', '1.0.0', 'Unix'), ('server', '1.0.0', 'NT') RETURNING *;
EOF

Produces a relevant output:

server|1.0.0|Unix
server|1.0.0|NT

However, a Python example doing similar thing:

#!/bin/env python3.10
from pathlib import Path
import sqlite3

if __name__ == '__main__':
    test_file = Path.cwd() / 'test.db'
    test_file.unlink(missing_ok=True)
    connection = sqlite3.connect(test_file)
    connection.execute(
        'CREATE TABLE releases ('
        'component VARCHAR(64) NOT NULL, '
        'version VARCHAR(64) NOT NULL, '
        'os VARCHAR(64) NOT NULL, '
        'PRIMARY KEY (component, version, os));',
        )
    values = [
        ('server', '1.0.0', 'Unix'),
        ('server', '1.0.0', 'NT')
    ]
    cursor = connection.executemany('INSERT INTO releases VALUES(?, ?, ?) RETURNING *;', values)
    print(cursor.fetchall())

generates an exception:

Traceback (most recent call last):
  File "/home/zentarim/py/test/./sql_many.py", line 20, in <module>
    cursor = connection.executemany('INSERT INTO releases VALUES(?, ?, ?) RETURNING *;', values)
sqlite3.ProgrammingError: executemany() can only execute DML statements.

Environment

Ubuntu 22.04.1 LTS
Kernel 6.0.0-1006-oem
Python 3.10.6
sqlite3 module version: 2.6.0
sqlite3 package version: 3.37.2

Not sure if it is a bug or unimplemented feature.

Thanks in advance!

Linked PRs

@zentarim zentarim added the type-bug An unexpected behavior, bug, or error label Dec 5, 2022
@PascalinDe
Copy link

PascalinDe commented Mar 26, 2023

Hi,

just to give a quick update on that issue on a more recent version of Python/SQLite module: when executing @zentarim's example script with Python3.11 on Debian bookworm, no exception is raised, but no values are returned either :

/tmp$ cat /tmp/test.py 
#!/bin/env python3.10
from pathlib import Path
import sqlite3

if __name__ == '__main__':
    test_file = Path.cwd() / 'test.db'
    test_file.unlink(missing_ok=True)
    connection = sqlite3.connect(test_file)
    connection.execute(
        'CREATE TABLE releases ('
        'component VARCHAR(64) NOT NULL, '
        'version VARCHAR(64) NOT NULL, '
        'os VARCHAR(64) NOT NULL, '
        'PRIMARY KEY (component, version, os));',
        )
    values = [
        ('server', '1.0.0', 'Unix'),
        ('server', '1.0.0', 'NT')
    ]
    cursor = connection.executemany('INSERT INTO releases VALUES(?, ?, ?) RETURNING *;', values)
    print(cursor.fetchall())
/tmp$ python3 test.py 
[]

The relevant versions are

/tmp$ python3 --version
Python 3.11.2
[...]
In [1]: import sqlite3

In [2]: sqlite3.version
Out[2]: '2.6.0'

In [3]: sqlite3.sqlite_version
Out[3]: '3.40.1'

Thank you for your work!

@erlend-aasland erlend-aasland self-assigned this Mar 27, 2023
@erlend-aasland
Copy link
Contributor

erlend-aasland commented Apr 26, 2023

executemany was written (back in 2004 IIRC) to deliberately discard any resulting rows, since it was meant to only execute DRM statements where (at that time) no resulting rows were expected. In 2021, support for RETURNING was added; it's a pretty new SQLite feature. If executemany was to be redesigned to be able to return resulting rows, it would be a change of semantics for this API, which might be a breaking change.

TL;DR: I'd prefer to be conservative regarding this, and not alter the current semantics of executemany, which means I lean towards closing this as wont-fix.

Not sure if it is a bug or unimplemented feature.

It's an unimplemented feature.

@erlend-aasland erlend-aasland added pending The issue will be closed if no feedback is provided type-feature A feature request or enhancement and removed type-bug An unexpected behavior, bug, or error labels Apr 26, 2023
@erlend-aasland erlend-aasland changed the title stdlib sqlite3 executemany() does not support RETURNING statement Alter sqlite3's executemany() to support returning resulting rows Apr 26, 2023
@PascalinDe
Copy link

Thank you for the explanation!

erlend-aasland added a commit to erlend-aasland/cpython that referenced this issue Apr 27, 2023
@erlend-aasland erlend-aasland added type-bug An unexpected behavior, bug, or error docs Documentation in the Doc dir and removed type-feature A feature request or enhancement pending The issue will be closed if no feedback is provided labels Apr 27, 2023
@erlend-aasland erlend-aasland changed the title Alter sqlite3's executemany() to support returning resulting rows stdlib sqlite3 executemany() does not support RETURNING statement Apr 27, 2023
@erlend-aasland
Copy link
Contributor

FTR, I see there's room for improvement in the docs, so I reverted the PR title change I did yesterday and I've made this a docs issue. See gh-103939.

I'll create a topic on Discourse about the feature request.

@zentarim
Copy link
Contributor Author

executemany was written (back in 2004 IIRC) to deliberately discard any resulting rows, since it was meant to only execute DRM statements where (at that time) no resulting rows were expected. In 2021, support for RETURNING was added; it's a pretty new SQLite feature. If executemany was to be redesigned to be able to return resulting rows, it would be a change of semantics for this API, which might be a breaking change.

TL;DR: I'd prefer to be conservative regarding this, and not alter the current semantics of executemany, which means I lean towards closing this as wont-fix.

Not sure if it is a bug or unimplemented feature.

It's an unimplemented feature.

I deliberately don't talk about internals for I know less than nothing about them. I just want to point out that from the user point of view there are barely any breaking changes expected. If somebody uses an executemany() statement, they already do not expect it to return any data.

Anyway, thanks for the explanation!

@erlend-aasland
Copy link
Contributor

I just want to point out that from the user point of view there are barely any breaking changes expected.

You are probably correct, but people rely on any kind of behaviour in unexpected ways. If we change the semantics of executemany(), it might break existing code. As I said, I want to move this discussion to Discourse, in order to get more attention to it. I'm not strictly opposed to changing the semantics, but for now I'm on the conservative side.

Anyway, thanks for the explanation!

Anytime! Thanks for the report :)

@erlend-aasland
Copy link
Contributor

Oh, please take a look at the PR, if you find the time.

@erlend-aasland
Copy link
Contributor

@PascalinDe, @zentarim: I created a topic on Discourse regarding the feature request, in case you want to participate in the discussion over there.

@PascalinDe
Copy link

@PascalinDe, @zentarim: I created a topic on Discourse regarding the feature request, in case you want to participate in the discussion over there.

Thank you, I'm not familiar enough with the internals to actually participate, but I'll be sure to follow the discussion!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs Documentation in the Doc dir topic-sqlite3 type-bug An unexpected behavior, bug, or error
Projects
Status: Done
Development

Successfully merging a pull request may close this issue.

4 participants