Coverage for coverage / sqlitedb.py: 100.000%
131 statements
« prev ^ index » next coverage.py v7.12.1a0.dev1, created at 2025-11-30 17:57 +0000
« prev ^ index » next coverage.py v7.12.1a0.dev1, created at 2025-11-30 17:57 +0000
1# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
2# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt
4"""SQLite abstraction for coverage.py"""
6from __future__ import annotations 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
8import contextlib 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
9import re 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
10import sqlite3 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
11from collections.abc import Iterable, Iterator 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
12from typing import Any, cast 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
14from coverage.debug import auto_repr, clipped_repr, exc_one_line 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
15from coverage.exceptions import DataError 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
16from coverage.types import TDebugCtl 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
19class SqliteDb: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
20 """A simple abstraction over a SQLite database.
22 Use as a context manager, then you can use it like a
23 :class:`python:sqlite3.Connection` object::
25 with SqliteDb(filename, debug_control) as db:
26 with db.execute("select a, b from some_table") as cur:
27 for a, b in cur:
28 etc(a, b)
30 """
32 def __init__(self, filename: str, debug: TDebugCtl, no_disk: bool = False) -> None: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
33 self.debug = debug 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
34 self.filename = filename 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
35 self.no_disk = no_disk 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
36 self.nest = 0 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
37 self.con: sqlite3.Connection | None = None 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
39 __repr__ = auto_repr 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
41 def _connect(self) -> None: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
42 """Connect to the db and do universal initialization."""
43 if self.con is not None: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
44 return 1CDEF01GHIJ23abcdKLefghMNijklOPmnopQRqrstSTuvwxUVyzABWXYZ4
46 # It can happen that Python switches threads while the tracer writes
47 # data. The second thread will also try to write to the data,
48 # effectively causing a nested context. However, given the idempotent
49 # nature of the tracer operations, sharing a connection among threads
50 # is not a problem.
51 if self.debug.should("sql"): 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
52 self.debug.write(f"Connecting to {self.filename!r}") 1CDEF01GHIJ23abcdKLefghMNijklOPmnopQRqrstSTuvwxUVyzABWXYZ4
53 try: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
54 # Use uri=True when connecting to memory URIs
55 if self.filename.startswith("file:"): 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
56 self.con = sqlite3.connect(self.filename, check_same_thread=False, uri=True) 1CDEF01GHIJ23abcdKLefghMNijklOPmnopQRqrstSTuvwxUVyzABWXYZ4
57 else:
58 self.con = sqlite3.connect(self.filename, check_same_thread=False) 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
59 except sqlite3.Error as exc: 1CDEFGHIJabcdefghijklmnopqrstuvwxyzABYZ
60 raise DataError(f"Couldn't use data file {self.filename!r}: {exc}") from exc 1CDEFGHIJabcdefghijklmnopqrstuvwxyzABYZ
62 if self.debug.should("sql"): 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
63 self.debug.write(f"Connected to {self.filename!r} as {self.con!r}") 1CDEF01GHIJ23abcdKLefghMNijklOPmnopQRqrstSTuvwxUVyzABWXYZ4
65 self.con.create_function("REGEXP", 2, lambda txt, pat: re.search(txt, pat) is not None) 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
67 # Turning off journal_mode can speed up writing. It can't always be
68 # disabled, so we have to be prepared for *-journal files elsewhere.
69 # In Python 3.12+, we can change the config to allow journal_mode=off.
70 if hasattr(sqlite3, "SQLITE_DBCONFIG_DEFENSIVE"): 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
71 # Turn off defensive mode, so that journal_mode=off can succeed.
72 self.con.setconfig( # type: ignore[attr-defined, unused-ignore] 1abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)
73 sqlite3.SQLITE_DBCONFIG_DEFENSIVE,
74 False,
75 )
77 # This pragma makes writing faster. It disables rollbacks, but we never need them.
78 self.execute_void("pragma journal_mode=off") 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
80 # This pragma makes writing faster. It can fail in unusual situations
81 # (https://github.com/coveragepy/coveragepy/issues/1646), so use fail_ok=True
82 # to keep things going.
83 self.execute_void("pragma synchronous=off", fail_ok=True) 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
85 def close(self, force: bool = False) -> None: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
86 """If needed, close the connection."""
87 if self.con is not None: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
88 if force or not self.no_disk: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
89 if self.debug.should("sql"): 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
90 self.debug.write(f"Closing {self.con!r} on {self.filename!r}") 1CDEF01GHIJ23abcdKLefghMNijklOPmnopQRqrstSTuvwxUVyzABWXYZ4
91 self.con.close() 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
92 self.con = None 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
94 def __enter__(self) -> SqliteDb: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
95 if self.nest == 0: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
96 self._connect() 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
97 assert self.con is not None 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
98 self.con.__enter__() 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
99 self.nest += 1 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
100 return self 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
102 def __exit__(self, exc_type, exc_value, traceback) -> None: # type: ignore[no-untyped-def] 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
103 self.nest -= 1 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
104 if self.nest == 0: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
105 try: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
106 assert self.con is not None 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
107 self.con.__exit__(exc_type, exc_value, traceback) 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
108 self.close() 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
109 except Exception as exc: 1CDEF01GHIJ23abcdKLefghMNijklOPmnopQRqrstSTuvwxUVyzABWXYZ4
110 if self.debug.should("sql"): 1CDEF01GHIJ23abcdKLefghMNijklOPmnopQRqrstSTuvwxUVyzABWXYZ4
111 self.debug.write(f"EXCEPTION from __exit__: {exc_one_line(exc)}") 1CDEF01GHIJ23abcdKLefghMNijklOPmnopQRqrstSTuvwxUVyzABWXYZ4
112 raise DataError(f"Couldn't end data file {self.filename!r}: {exc}") from exc 1CDEF01GHIJ23abcdKLefghMNijklOPmnopQRqrstSTuvwxUVyzABWXYZ4
114 def _execute(self, sql: str, parameters: Iterable[Any]) -> sqlite3.Cursor: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
115 """Same as :meth:`python:sqlite3.Connection.execute`."""
116 if self.debug.should("sql"): 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
117 tail = f" with {parameters!r}" if parameters else "" 1CDEF01GHIJ23abcdKLefghMNijklOPmnopQRqrstSTuvwxUVyzABWXYZ4
118 self.debug.write(f"Executing {sql!r}{tail}") 1CDEF01GHIJ23abcdKLefghMNijklOPmnopQRqrstSTuvwxUVyzABWXYZ4
119 try: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
120 assert self.con is not None 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
121 try: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
122 return self.con.execute(sql, parameters) # type: ignore[arg-type] 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
123 except Exception: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
124 # In some cases, an error might happen that isn't really an
125 # error. Try again immediately.
126 # https://github.com/coveragepy/coveragepy/issues/1010
127 return self.con.execute(sql, parameters) # type: ignore[arg-type] 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
128 except sqlite3.Error as exc: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
129 msg = str(exc) 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
130 if not self.no_disk: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
131 try: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
132 # `execute` is the first thing we do with the database, so try
133 # hard to provide useful hints if something goes wrong now.
134 with open(self.filename, "rb") as bad_file: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
135 cov4_sig = b"!coverage.py: This is a private format" 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
136 if bad_file.read(len(cov4_sig)) == cov4_sig: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
137 msg = ( 1CDEF01GHIJ23abcdKLefghMNijklOPmnopQRqrstSTuvwxUVyzABWXYZ4
138 "Looks like a coverage 4.x data file. "
139 + "Are you mixing versions of coverage?"
140 )
141 except Exception: 1CDEF01GHIJ23abcdKLefghMNijklOPmnopQRqrstSTuvwxUVyzABWXYZ4
142 pass 1CDEF01GHIJ23abcdKLefghMNijklOPmnopQRqrstSTuvwxUVyzABWXYZ4
143 if self.debug.should("sql"): 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
144 self.debug.write(f"EXCEPTION from execute: {exc_one_line(exc)}") 1CDEF01GHIJ23abcdKLefghMNijklOPmnopQRqrstSTuvwxUVyzABWXYZ4
145 raise DataError(f"Couldn't use data file {self.filename!r}: {msg}") from exc 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
147 @contextlib.contextmanager 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
148 def execute( 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)
149 self,
150 sql: str,
151 parameters: Iterable[Any] = (),
152 ) -> Iterator[sqlite3.Cursor]:
153 """Context managed :meth:`python:sqlite3.Connection.execute`.
155 Use with a ``with`` statement to auto-close the returned cursor.
156 """
157 cur = self._execute(sql, parameters) 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
158 try: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
159 yield cur 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
160 finally:
161 cur.close() 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
163 def execute_void(self, sql: str, parameters: Iterable[Any] = (), fail_ok: bool = False) -> None: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
164 """Same as :meth:`python:sqlite3.Connection.execute` when you don't need the cursor.
166 If `fail_ok` is True, then SQLite errors are ignored.
167 """
168 try: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
169 # PyPy needs the .close() calls here, or sqlite gets twisted up:
170 # https://bitbucket.org/pypy/pypy/issues/2872/default-isolation-mode-is-different-on
171 self._execute(sql, parameters).close() 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
172 except DataError: 1CDEF01GHIJ23abcdKLefghMNijklOPmnopQRqrstSTuvwxUVyzABWXYZ4
173 if not fail_ok: 1CDEF01GHIJ23abcdKLefghMNijklOPmnopQRqrstSTuvwxUVyzABWXYZ4
174 raise 1CDEF01GHIJ23abcdKLefghMNijklOPmnopQRqrstSTuvwxUVyzABWXYZ4
176 def execute_for_rowid(self, sql: str, parameters: Iterable[Any] = ()) -> int: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
177 """Like execute, but returns the lastrowid."""
178 with self.execute(sql, parameters) as cur: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
179 assert cur.lastrowid is not None 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
180 rowid: int = cur.lastrowid 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
181 if self.debug.should("sqldata"): 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
182 self.debug.write(f"Row id result: {rowid!r}") 1CDEF01GHIJ23abcdKLefghMNijklOPmnopQRqrstSTuvwxUVyzABWXYZ4
183 return rowid 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
185 def execute_one(self, sql: str, parameters: Iterable[Any] = ()) -> tuple[Any, ...] | None: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
186 """Execute a statement and return the one row that results.
188 This is like execute(sql, parameters).fetchone(), except it is
189 correct in reading the entire result set. This will raise an
190 exception if more than one row results.
192 Returns a row, or None if there were no rows.
193 """
194 with self.execute(sql, parameters) as cur: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
195 rows = list(cur) 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
196 if len(rows) == 0: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
197 return None 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
198 elif len(rows) == 1: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
199 return cast(tuple[Any, ...], rows[0]) 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
200 else:
201 raise AssertionError(f"SQL {sql!r} shouldn't return {len(rows)} rows")
203 def _executemany(self, sql: str, data: list[Any]) -> sqlite3.Cursor: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
204 """Same as :meth:`python:sqlite3.Connection.executemany`."""
205 if self.debug.should("sql"): 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
206 final = ":" if self.debug.should("sqldata") else "" 1CDEF01GHIJ23abcdKLefghMNijklOPmnopQRqrstSTuvwxUVyzABWXYZ4
207 self.debug.write(f"Executing many {sql!r} with {len(data)} rows{final}") 1CDEF01GHIJ23abcdKLefghMNijklOPmnopQRqrstSTuvwxUVyzABWXYZ4
208 if self.debug.should("sqldata"): 1CDEF01GHIJ23abcdKLefghMNijklOPmnopQRqrstSTuvwxUVyzABWXYZ4
209 for i, row in enumerate(data): 1CDEF01GHIJ23abcdKLefghMNijklOPmnopQRqrstSTuvwxUVyzABWXYZ4
210 self.debug.write(f"{i:4d}: {row!r}") 1CDEF01GHIJ23abcdKLefghMNijklOPmnopQRqrstSTuvwxUVyzABWXYZ4
211 assert self.con is not None 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
212 try: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
213 return self.con.executemany(sql, data) 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
214 except Exception: 1CDEF01GHIJ23abcdKLefghMNijklOPmnopQRqrstSTuvwxUVyzABWXYZ4
215 # In some cases, an error might happen that isn't really an
216 # error. Try again immediately.
217 # https://github.com/coveragepy/coveragepy/issues/1010
218 return self.con.executemany(sql, data) 1CDEF01GHIJ23abcdKLefghMNijklOPmnopQRqrstSTuvwxUVyzABWXYZ4
220 def executemany_void(self, sql: str, data: list[Any]) -> None: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
221 """Same as :meth:`python:sqlite3.Connection.executemany` when you don't need the cursor."""
222 self._executemany(sql, data).close() 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
224 def executescript(self, script: str) -> None: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
225 """Same as :meth:`python:sqlite3.Connection.executescript`."""
226 if self.debug.should("sql"): 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
227 self.debug.write( 1CDEF01GHIJ23abcdKLefghMNijklOPmnopQRqrstSTuvwxUVyzABWXYZ4
228 "Executing script with {} chars: {}".format(
229 len(script),
230 clipped_repr(script, 100),
231 )
232 )
233 assert self.con is not None 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
234 self.con.executescript(script).close() 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
236 def dump(self) -> str: 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
237 """Return a multi-line string, the SQL dump of the database."""
238 assert self.con is not None 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4
239 return "\n".join(self.con.iterdump()) 1CDEF01GHIJ23abcdKLefghMNijklOPmn5op6QR7qr8st9ST!uv#wx$UV%yz'AB(WX)YZ4