Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions sqlite_utils/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -1176,7 +1176,7 @@ def insert_upsert_implementation(
)
else:
raise
if tracker is not None:
if tracker is not None and db.table(table).exists():
db.table(table).transform(types=tracker.types)

# Clean up open file-like objects
Expand Down Expand Up @@ -2033,7 +2033,7 @@ def memory(
rows = (_flatten(row) for row in rows)

db.table(file_table).insert_all(rows, alter=True)
if tracker is not None:
if tracker is not None and db.table(file_table).exists():
db.table(file_table).transform(types=tracker.types)
# Add convenient t / t1 / t2 views
view_names = ["t{}".format(i + 1)]
Expand Down
29 changes: 24 additions & 5 deletions sqlite_utils/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
Tuple,
)
import uuid
import warnings
from sqlite_utils.plugins import pm

try:
Expand Down Expand Up @@ -401,13 +402,16 @@ def close(self) -> None:
self.conn.close()

@contextlib.contextmanager
def ensure_autocommit_off(self) -> Generator[None, None, None]:
def ensure_autocommit_on(self) -> Generator[None, None, None]:
"""
Ensure autocommit is off for this database connection.
Ensure autocommit is on for this database connection.

In SQLite's Python module, ``isolation_level = None`` enables autocommit mode,
where each statement is committed immediately.

Example usage::

with db.ensure_autocommit_off():
with db.ensure_autocommit_on():
# do stuff here

This will reset to the previous autocommit state at the end of the block.
Expand All @@ -419,6 +423,21 @@ def ensure_autocommit_off(self) -> Generator[None, None, None]:
finally:
self.conn.isolation_level = old_isolation_level

@contextlib.contextmanager
def ensure_autocommit_off(self) -> Generator[None, None, None]:
"""
Deprecated alias for :meth:`ensure_autocommit_on`.

This method name is confusing - ``isolation_level = None`` actually enables
autocommit mode in SQLite, not disables it. Use :meth:`ensure_autocommit_on` instead.
"""
warnings.warn(
"ensure_autocommit_off() is deprecated, use ensure_autocommit_on() instead",
DeprecationWarning,
)
with self.ensure_autocommit_on():
yield

@contextlib.contextmanager
def tracer(
self, tracer: Optional[Callable[[str, Optional[Sequence]], None]] = None
Expand Down Expand Up @@ -781,13 +800,13 @@ def enable_wal(self) -> None:
Sets ``journal_mode`` to ``'wal'`` to enable Write-Ahead Log mode.
"""
if self.journal_mode != "wal":
with self.ensure_autocommit_off():
with self.ensure_autocommit_on():
self.execute("PRAGMA journal_mode=wal;")

def disable_wal(self) -> None:
"Sets ``journal_mode`` back to ``'delete'`` to disable Write-Ahead Log mode."
if self.journal_mode != "delete":
with self.ensure_autocommit_off():
with self.ensure_autocommit_on():
self.execute("PRAGMA journal_mode=delete;")

def _ensure_counts_table(self) -> None:
Expand Down
18 changes: 18 additions & 0 deletions tests/test_cli_insert.py
Original file line number Diff line number Diff line change
Expand Up @@ -597,3 +597,21 @@ def try_until(expected):
proc.stdin.close()
proc.wait()
assert proc.returncode == 0


def test_insert_csv_headers_only(tmpdir):
"""Test that CSV with only header row (no data) works with --detect-types (issue #702)"""
db_path = str(tmpdir / "test.db")
csv_path = str(tmpdir / "headers_only.csv")
with open(csv_path, "w") as fp:
fp.write("id,name,age\n")
# Should not crash with --detect-types (which is now the default)
result = CliRunner().invoke(
cli.cli,
["insert", db_path, "data", csv_path, "--csv"],
catch_exceptions=False,
)
assert result.exit_code == 0
# Table should not exist since there were no data rows
db = Database(db_path)
assert not db["data"].exists()