mirror of
https://github.com/simonw/sqlite-utils.git
synced 2026-09-11 19:14:10 +02:00
parent
56dd09702f
commit
a6dfd7a7af
4 changed files with 171 additions and 2 deletions
|
|
@ -952,7 +952,7 @@ See :ref:`cli_create_table`.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
Usage: sqlite-utils create-table [OPTIONS] PATH TABLE COLUMNS...
|
Usage: sqlite-utils create-table [OPTIONS] PATH TABLE [COLUMNS]...
|
||||||
|
|
||||||
Add a table with the specified columns. Columns should be specified using
|
Add a table with the specified columns. Columns should be specified using
|
||||||
name, type pairs, for example:
|
name, type pairs, for example:
|
||||||
|
|
@ -965,7 +965,13 @@ See :ref:`cli_create_table`.
|
||||||
|
|
||||||
Valid column types are text, integer, real, float, blob and any.
|
Valid column types are text, integer, real, float, blob and any.
|
||||||
|
|
||||||
|
Use --sql to create the table from the results of a SQL query instead:
|
||||||
|
|
||||||
|
sqlite-utils create-table my.db tall_people \
|
||||||
|
--sql "select * from people where height > 180"
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
|
--sql TEXT Create the table using the results of this SQL query
|
||||||
--pk TEXT Column to use as primary key
|
--pk TEXT Column to use as primary key
|
||||||
--not-null TEXT Columns that should be created as NOT NULL
|
--not-null TEXT Columns that should be created as NOT NULL
|
||||||
--default <TEXT TEXT>... Default value that should be set for a column
|
--default <TEXT TEXT>... Default value that should be set for a column
|
||||||
|
|
|
||||||
|
|
@ -2167,6 +2167,15 @@ Use the ``any`` type for a strict column that should accept integers, floating p
|
||||||
"name" TEXT
|
"name" TEXT
|
||||||
) STRICT
|
) STRICT
|
||||||
|
|
||||||
|
Instead of listing columns you can populate a new table from the results of a SQL query using ``--sql``. This is a shortcut for ``CREATE TABLE name AS ...``:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
sqlite-utils create-table mydb.db tall_people \
|
||||||
|
--sql "select * from people where height > 180"
|
||||||
|
|
||||||
|
The new table takes its columns from the query, so ``--sql`` cannot be combined with column definitions or with the ``--pk``, ``--not-null``, ``--default``, ``--fk``, ``--transform`` and ``--strict`` options. It does work with ``--ignore`` and ``--replace``.
|
||||||
|
|
||||||
If a table with the same name already exists, you will get an error. You can choose to silently ignore this error with ``--ignore``, or you can replace the existing table with a new, empty table using ``--replace``.
|
If a table with the same name already exists, you will get an error. You can choose to silently ignore this error with ``--ignore``, or you can replace the existing table with a new, empty table using ``--replace``.
|
||||||
|
|
||||||
You can also pass ``--transform`` to transform the existing table to match the new schema. See :ref:`python_api_explicit_create` in the Python library documentation for details of how this option works.
|
You can also pass ``--transform`` to transform the existing table to match the new schema. See :ref:`python_api_explicit_create` in the Python library documentation for details of how this option works.
|
||||||
|
|
|
||||||
|
|
@ -1703,7 +1703,11 @@ def create_database(path, enable_wal, init_spatialite, load_extension):
|
||||||
required=True,
|
required=True,
|
||||||
)
|
)
|
||||||
@click.argument("table")
|
@click.argument("table")
|
||||||
@click.argument("columns", nargs=-1, required=True)
|
@click.argument("columns", nargs=-1)
|
||||||
|
@click.option(
|
||||||
|
"--sql",
|
||||||
|
help="Create the table using the results of this SQL query",
|
||||||
|
)
|
||||||
@click.option("pks", "--pk", help="Column to use as primary key", multiple=True)
|
@click.option("pks", "--pk", help="Column to use as primary key", multiple=True)
|
||||||
@click.option(
|
@click.option(
|
||||||
"--not-null",
|
"--not-null",
|
||||||
|
|
@ -1747,6 +1751,7 @@ def create_table(
|
||||||
path,
|
path,
|
||||||
table,
|
table,
|
||||||
columns,
|
columns,
|
||||||
|
sql,
|
||||||
pks,
|
pks,
|
||||||
not_null,
|
not_null,
|
||||||
default,
|
default,
|
||||||
|
|
@ -1769,10 +1774,52 @@ def create_table(
|
||||||
photo blob --pk id
|
photo blob --pk id
|
||||||
|
|
||||||
Valid column types are text, integer, real, float, blob and any.
|
Valid column types are text, integer, real, float, blob and any.
|
||||||
|
|
||||||
|
Use --sql to create the table from the results of a SQL query instead:
|
||||||
|
|
||||||
|
\b
|
||||||
|
sqlite-utils create-table my.db tall_people \\
|
||||||
|
--sql "select * from people where height > 180"
|
||||||
"""
|
"""
|
||||||
db = sqlite_utils.Database(path)
|
db = sqlite_utils.Database(path)
|
||||||
_register_db_for_cleanup(db)
|
_register_db_for_cleanup(db)
|
||||||
_load_extensions(db, load_extension)
|
_load_extensions(db, load_extension)
|
||||||
|
if sql is not None:
|
||||||
|
if columns:
|
||||||
|
raise click.ClickException("Cannot use columns with --sql")
|
||||||
|
incompatible = [
|
||||||
|
option
|
||||||
|
for option, value in (
|
||||||
|
("--pk", pks),
|
||||||
|
("--not-null", not_null),
|
||||||
|
("--default", default),
|
||||||
|
("--fk", fk),
|
||||||
|
("--transform", transform),
|
||||||
|
("--strict", strict),
|
||||||
|
)
|
||||||
|
if value
|
||||||
|
]
|
||||||
|
if incompatible:
|
||||||
|
raise click.ClickException(
|
||||||
|
"Cannot use {} with --sql".format(", ".join(incompatible))
|
||||||
|
)
|
||||||
|
if table in db.table_names():
|
||||||
|
if ignore:
|
||||||
|
return
|
||||||
|
elif replace:
|
||||||
|
db[table].drop()
|
||||||
|
else:
|
||||||
|
raise click.ClickException(
|
||||||
|
f'Table "{table}" already exists. Use --replace to delete and replace it.'
|
||||||
|
)
|
||||||
|
db.execute(
|
||||||
|
"CREATE TABLE {table} AS {sql}".format(
|
||||||
|
table=quote_identifier(table), sql=sql
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
if not columns:
|
||||||
|
raise click.ClickException("Provide columns or use --sql")
|
||||||
if len(columns) % 2 == 1:
|
if len(columns) % 2 == 1:
|
||||||
raise click.ClickException(
|
raise click.ClickException(
|
||||||
"columns must be an even number of 'name' 'type' pairs"
|
"columns must be an even number of 'name' 'type' pairs"
|
||||||
|
|
|
||||||
|
|
@ -1509,6 +1509,113 @@ def test_create_table_replace():
|
||||||
assert 'CREATE TABLE "dogs" (\n "id" INTEGER\n)' == db.table("dogs").schema
|
assert 'CREATE TABLE "dogs" (\n "id" INTEGER\n)' == db.table("dogs").schema
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_table_sql():
|
||||||
|
runner = CliRunner()
|
||||||
|
with runner.isolated_filesystem():
|
||||||
|
db = Database("test.db")
|
||||||
|
db["people"].insert_all(
|
||||||
|
[
|
||||||
|
{"id": 1, "name": "Ann", "height": 190},
|
||||||
|
{"id": 2, "name": "Bob", "height": 150},
|
||||||
|
],
|
||||||
|
pk="id",
|
||||||
|
)
|
||||||
|
result = runner.invoke(
|
||||||
|
cli.cli,
|
||||||
|
[
|
||||||
|
"create-table",
|
||||||
|
"test.db",
|
||||||
|
"tall",
|
||||||
|
"--sql",
|
||||||
|
"select id, name from people where height > 180",
|
||||||
|
],
|
||||||
|
catch_exceptions=False,
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert "CREATE TABLE tall(id INT,name TEXT)" == db["tall"].schema
|
||||||
|
assert [{"id": 1, "name": "Ann"}] == list(db["tall"].rows)
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_table_sql_error_if_table_exists():
|
||||||
|
runner = CliRunner()
|
||||||
|
with runner.isolated_filesystem():
|
||||||
|
db = Database("test.db")
|
||||||
|
db["dogs"].insert({"name": "Cleo"})
|
||||||
|
result = runner.invoke(
|
||||||
|
cli.cli, ["create-table", "test.db", "dogs", "--sql", "select 1"]
|
||||||
|
)
|
||||||
|
assert result.exit_code == 1
|
||||||
|
assert (
|
||||||
|
'Error: Table "dogs" already exists. Use --replace to delete and replace it.'
|
||||||
|
== result.output.strip()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_table_sql_ignore():
|
||||||
|
runner = CliRunner()
|
||||||
|
with runner.isolated_filesystem():
|
||||||
|
db = Database("test.db")
|
||||||
|
db["dogs"].insert({"name": "Cleo"})
|
||||||
|
result = runner.invoke(
|
||||||
|
cli.cli,
|
||||||
|
["create-table", "test.db", "dogs", "--sql", "select 1 as id", "--ignore"],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert 'CREATE TABLE "dogs" (\n "name" TEXT\n)' == db["dogs"].schema
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_table_sql_replace():
|
||||||
|
runner = CliRunner()
|
||||||
|
with runner.isolated_filesystem():
|
||||||
|
db = Database("test.db")
|
||||||
|
db["dogs"].insert({"name": "Cleo"})
|
||||||
|
result = runner.invoke(
|
||||||
|
cli.cli,
|
||||||
|
["create-table", "test.db", "dogs", "--sql", "select 1 as id", "--replace"],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert "CREATE TABLE dogs(id)" == db["dogs"].schema
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_table_sql_requires_columns_or_sql():
|
||||||
|
runner = CliRunner()
|
||||||
|
with runner.isolated_filesystem():
|
||||||
|
result = runner.invoke(cli.cli, ["create-table", "test.db", "t"])
|
||||||
|
assert result.exit_code == 1
|
||||||
|
assert "Error: Provide columns or use --sql" == result.output.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_table_sql_conflicts_with_columns():
|
||||||
|
runner = CliRunner()
|
||||||
|
with runner.isolated_filesystem():
|
||||||
|
result = runner.invoke(
|
||||||
|
cli.cli,
|
||||||
|
["create-table", "test.db", "t", "id", "integer", "--sql", "select 1"],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 1
|
||||||
|
assert "Error: Cannot use columns with --sql" == result.output.strip()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"extra_args,expected",
|
||||||
|
[
|
||||||
|
(["--pk", "id"], "Error: Cannot use --pk with --sql"),
|
||||||
|
(["--not-null", "id"], "Error: Cannot use --not-null with --sql"),
|
||||||
|
(["--default", "id", "1"], "Error: Cannot use --default with --sql"),
|
||||||
|
(["--strict"], "Error: Cannot use --strict with --sql"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_create_table_sql_conflicts_with_options(extra_args, expected):
|
||||||
|
runner = CliRunner()
|
||||||
|
with runner.isolated_filesystem():
|
||||||
|
result = runner.invoke(
|
||||||
|
cli.cli,
|
||||||
|
["create-table", "test.db", "t", "--sql", "select 1 as id"] + extra_args,
|
||||||
|
)
|
||||||
|
assert result.exit_code == 1
|
||||||
|
assert expected == result.output.strip()
|
||||||
|
|
||||||
|
|
||||||
def test_create_view():
|
def test_create_view():
|
||||||
runner = CliRunner()
|
runner = CliRunner()
|
||||||
with runner.isolated_filesystem():
|
with runner.isolated_filesystem():
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue