mirror of
https://github.com/simonw/sqlite-utils.git
synced 2026-09-22 08:24:32 +02:00
Merge 37ef98352a into 79117b9d11
This commit is contained in:
commit
654636ad74
5 changed files with 308 additions and 48 deletions
|
|
@ -84,6 +84,37 @@ It's often worth trying: --encoding=latin-1
|
|||
maximize_csv_field_size_limit()
|
||||
|
||||
|
||||
class DatabasePath(click.Path):
|
||||
"""
|
||||
Custom Click parameter type for database paths that supports SQLite URI filenames.
|
||||
|
||||
URIs (starting with 'file:') skip file existence validation and are passed
|
||||
directly to sqlite3.connect() with uri=True.
|
||||
|
||||
See: https://www.sqlite.org/uri.html
|
||||
"""
|
||||
def __init__(self, exists=False, **kwargs):
|
||||
# Store original exists parameter for URI detection
|
||||
self._check_exists = exists
|
||||
# Always pass exists=False to parent to skip validation
|
||||
# We'll do our own validation for non-URI paths
|
||||
super().__init__(exists=False, **kwargs)
|
||||
|
||||
def convert(self, value, param, ctx):
|
||||
# If it's a URI (starts with "file:"), skip existence check
|
||||
if isinstance(value, str) and value.startswith("file:"):
|
||||
return value
|
||||
|
||||
# For non-URI paths, do normal path validation
|
||||
if self._check_exists:
|
||||
# Create a temporary Path validator with exists=True
|
||||
validator = click.Path(exists=True, file_okay=self.file_okay,
|
||||
dir_okay=self.dir_okay, allow_dash=self.allow_dash)
|
||||
return validator.convert(value, param, ctx)
|
||||
|
||||
return super().convert(value, param, ctx)
|
||||
|
||||
|
||||
class CaseInsensitiveChoice(click.Choice):
|
||||
def __init__(self, choices):
|
||||
super().__init__([choice.lower() for choice in choices])
|
||||
|
|
@ -154,7 +185,7 @@ def cli():
|
|||
@cli.command()
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.option(
|
||||
|
|
@ -253,7 +284,7 @@ def tables(
|
|||
@cli.command()
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.option(
|
||||
|
|
@ -319,7 +350,7 @@ def views(
|
|||
@cli.command()
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.argument("tables", nargs=-1)
|
||||
|
|
@ -348,7 +379,7 @@ def optimize(path, tables, no_vacuum, load_extension):
|
|||
@cli.command(name="rebuild-fts")
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.argument("tables", nargs=-1)
|
||||
|
|
@ -374,7 +405,7 @@ def rebuild_fts(path, tables, load_extension):
|
|||
@cli.command()
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.argument("names", nargs=-1)
|
||||
|
|
@ -401,7 +432,7 @@ def analyze(path, names):
|
|||
@cli.command()
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
def vacuum(path):
|
||||
|
|
@ -420,7 +451,7 @@ def vacuum(path):
|
|||
@cli.command()
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@load_extension_option
|
||||
|
|
@ -442,7 +473,7 @@ def dump(path, load_extension):
|
|||
@cli.command(name="add-column")
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.argument("table")
|
||||
|
|
@ -509,7 +540,7 @@ def add_column(
|
|||
@cli.command(name="add-foreign-key")
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.argument("table")
|
||||
|
|
@ -546,7 +577,7 @@ def add_foreign_key(
|
|||
@cli.command(name="add-foreign-keys")
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.argument("foreign_key", nargs=-1)
|
||||
|
|
@ -581,7 +612,7 @@ def add_foreign_keys(path, foreign_key, load_extension):
|
|||
@cli.command(name="index-foreign-keys")
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@load_extension_option
|
||||
|
|
@ -603,7 +634,7 @@ def index_foreign_keys(path, load_extension):
|
|||
@cli.command(name="create-index")
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.argument("table")
|
||||
|
|
@ -660,7 +691,7 @@ def create_index(
|
|||
@cli.command(name="enable-fts")
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.argument("table")
|
||||
|
|
@ -715,7 +746,7 @@ def enable_fts(
|
|||
@cli.command(name="populate-fts")
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.argument("table")
|
||||
|
|
@ -738,7 +769,7 @@ def populate_fts(path, table, column, load_extension):
|
|||
@cli.command(name="disable-fts")
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.argument("table")
|
||||
|
|
@ -761,7 +792,7 @@ def disable_fts(path, table, load_extension):
|
|||
@click.argument(
|
||||
"path",
|
||||
nargs=-1,
|
||||
type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@load_extension_option
|
||||
|
|
@ -784,7 +815,7 @@ def enable_wal(path, load_extension):
|
|||
@click.argument(
|
||||
"path",
|
||||
nargs=-1,
|
||||
type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@load_extension_option
|
||||
|
|
@ -806,7 +837,7 @@ def disable_wal(path, load_extension):
|
|||
@cli.command(name="enable-counts")
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.argument("tables", nargs=-1)
|
||||
|
|
@ -836,7 +867,7 @@ def enable_counts(path, tables, load_extension):
|
|||
@cli.command(name="reset-counts")
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(exists=True, file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@load_extension_option
|
||||
|
|
@ -905,7 +936,7 @@ def insert_upsert_options(*, require_pk=False):
|
|||
(
|
||||
click.argument(
|
||||
"path",
|
||||
type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
),
|
||||
click.argument("table"),
|
||||
|
|
@ -1420,7 +1451,7 @@ def upsert(
|
|||
@cli.command()
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.argument("sql")
|
||||
|
|
@ -1508,7 +1539,7 @@ def bulk(
|
|||
@cli.command(name="create-database")
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.option(
|
||||
|
|
@ -1545,7 +1576,7 @@ def create_database(path, enable_wal, init_spatialite, load_extension):
|
|||
@cli.command(name="create-table")
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.argument("table")
|
||||
|
|
@ -1657,7 +1688,7 @@ def create_table(
|
|||
@cli.command(name="duplicate")
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.argument("table")
|
||||
|
|
@ -1681,7 +1712,7 @@ def duplicate(path, table, new_table, ignore, load_extension):
|
|||
@cli.command(name="rename-table")
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.argument("table")
|
||||
|
|
@ -1707,7 +1738,7 @@ def rename_table(path, table, new_name, ignore, load_extension):
|
|||
@cli.command(name="drop-table")
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.argument("table")
|
||||
|
|
@ -1733,7 +1764,7 @@ def drop_table(path, table, ignore, load_extension):
|
|||
@cli.command(name="create-view")
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.argument("view")
|
||||
|
|
@ -1779,7 +1810,7 @@ def create_view(path, view, select, ignore, replace, load_extension):
|
|||
@cli.command(name="drop-view")
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.argument("view")
|
||||
|
|
@ -1805,13 +1836,13 @@ def drop_view(path, view, ignore, load_extension):
|
|||
@cli.command()
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.argument("sql")
|
||||
@click.option(
|
||||
"--attach",
|
||||
type=(str, click.Path(file_okay=True, dir_okay=False, allow_dash=False)),
|
||||
type=(str, DatabasePath(file_okay=True, dir_okay=False, allow_dash=False)),
|
||||
multiple=True,
|
||||
help="Additional databases to attach - specify alias and filepath",
|
||||
)
|
||||
|
|
@ -1899,7 +1930,7 @@ def query(
|
|||
)
|
||||
@click.option(
|
||||
"--attach",
|
||||
type=(str, click.Path(file_okay=True, dir_okay=False, allow_dash=False)),
|
||||
type=(str, DatabasePath(file_okay=True, dir_okay=False, allow_dash=False)),
|
||||
multiple=True,
|
||||
help="Additional databases to attach - specify alias and filepath",
|
||||
)
|
||||
|
|
@ -1932,7 +1963,7 @@ def query(
|
|||
@click.option("--dump", is_flag=True, help="Dump SQL for in-memory database")
|
||||
@click.option(
|
||||
"--save",
|
||||
type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
help="Save in-memory database to this file",
|
||||
)
|
||||
@click.option(
|
||||
|
|
@ -2158,7 +2189,7 @@ def _execute_query(
|
|||
@cli.command()
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.argument("dbtable")
|
||||
|
|
@ -2256,7 +2287,7 @@ def search(
|
|||
@cli.command()
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.argument("dbtable")
|
||||
|
|
@ -2342,7 +2373,7 @@ def rows(
|
|||
@cli.command()
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.argument("tables", nargs=-1)
|
||||
|
|
@ -2397,7 +2428,7 @@ def triggers(
|
|||
@cli.command()
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.argument("tables", nargs=-1)
|
||||
|
|
@ -2466,7 +2497,7 @@ def indexes(
|
|||
@cli.command()
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.argument("tables", nargs=-1, required=False)
|
||||
|
|
@ -2496,7 +2527,7 @@ def schema(
|
|||
@cli.command()
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.argument("table")
|
||||
|
|
@ -2644,7 +2675,7 @@ def transform(
|
|||
@cli.command()
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.argument("table")
|
||||
|
|
@ -2691,7 +2722,7 @@ def extract(
|
|||
@cli.command(name="insert-files")
|
||||
@click.argument(
|
||||
"path",
|
||||
type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.argument("table")
|
||||
|
|
@ -3024,7 +3055,7 @@ def _generate_convert_help():
|
|||
@cli.command(help=_generate_convert_help())
|
||||
@click.argument(
|
||||
"db_path",
|
||||
type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.argument("table", type=str)
|
||||
|
|
@ -3161,7 +3192,7 @@ def convert(
|
|||
@cli.command("add-geometry-column")
|
||||
@click.argument(
|
||||
"db_path",
|
||||
type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.argument("table", type=str)
|
||||
|
|
@ -3238,7 +3269,7 @@ def add_geometry_column(
|
|||
@cli.command("create-spatial-index")
|
||||
@click.argument(
|
||||
"db_path",
|
||||
type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
type=DatabasePath(file_okay=True, dir_okay=False, allow_dash=False),
|
||||
required=True,
|
||||
)
|
||||
@click.argument("table", type=str)
|
||||
|
|
|
|||
|
|
@ -383,7 +383,10 @@ class Database:
|
|||
self.conn = sqlite3.connect(":memory:")
|
||||
self.memory = True
|
||||
elif isinstance(filename_or_conn, (str, pathlib.Path)):
|
||||
if recreate and os.path.exists(filename_or_conn):
|
||||
filename_str = str(filename_or_conn)
|
||||
# Check if this is a URI filename (starts with "file:")
|
||||
is_uri = filename_str.startswith("file:")
|
||||
if recreate and not is_uri and os.path.exists(filename_or_conn):
|
||||
try:
|
||||
os.remove(filename_or_conn)
|
||||
except OSError:
|
||||
|
|
@ -391,7 +394,11 @@ class Database:
|
|||
# https://github.com/simonw/sqlite-utils/issues/503
|
||||
self.conn = sqlite3.connect(":memory:")
|
||||
raise
|
||||
self.conn = sqlite3.connect(str(filename_or_conn))
|
||||
if is_uri:
|
||||
# URI filenames need uri=True parameter
|
||||
self.conn = sqlite3.connect(filename_str, uri=True)
|
||||
else:
|
||||
self.conn = sqlite3.connect(filename_str)
|
||||
else:
|
||||
assert not recreate, "recreate cannot be used with connections, only paths"
|
||||
self.conn = cast(sqlite3.Connection, filename_or_conn)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue