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

table.create(..., replace=True) #568

Closed
simonw opened this issue Jul 22, 2023 · 7 comments
Closed

table.create(..., replace=True) #568

simonw opened this issue Jul 22, 2023 · 7 comments
Labels
enhancement New feature or request python-library

Comments

@simonw
Copy link
Owner

simonw commented Jul 22, 2023

Found myself using this pattern to quickly prototype a schema:

import sqlite_utils
db = sqlite_utils.Database(memory=True)

print(db["answers_chunks"].create({
    "id": int,
    "content": str,
    "embedding_type_id": int,
    "embedding": bytes,
    "embedding_content_md5": str,
    "source": str,
}, pk="id", transform=True).schema)
image

Using replace=True to drop and then recreate the table would be neat here, and would be consistent with other places that use replace=True.

@simonw simonw added enhancement New feature or request python-library labels Jul 22, 2023
@simonw
Copy link
Owner Author

simonw commented Jul 22, 2023

https://sqlite-utils.datasette.io/en/stable/cli-reference.html#create-table

sqlite-utils create-table ... --replace

That also has --ignore:

  --ignore                  If table already exists, do nothing
  --replace                 If table already exists, replace it
  --transform               If table already exists, try to transform the schema

@simonw
Copy link
Owner Author

simonw commented Jul 22, 2023

Here's where those are implemented for the create-table CLI command:

# Does table already exist?
if table in db.table_names():
if ignore:
return
elif replace:
db[table].drop()
elif transform:
pass
else:
raise click.ClickException(
'Table "{}" already exists. Use --replace to delete and replace it.'.format(
table
)
)
db[table].create(
coltypes,
pk=pk,
not_null=not_null,
defaults=dict(default),
foreign_keys=fk,
transform=transform,
)

@simonw
Copy link
Owner Author

simonw commented Jul 22, 2023

I think this is replace=True and ignore=True to match the CLI. And refactoring the CLI to use them.

@simonw
Copy link
Owner Author

simonw commented Jul 22, 2023

Demo:

>>> from sqlite_utils import Database
>>> db = Database(memory=True)
>>> db["foo"].create({"id": int})
<Table foo (id)>
>>> db["foo"].create({"id": int})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/simon/Dropbox/Development/sqlite-utils/sqlite_utils/db.py", line 1647, in create
    self.db.create_table(
  File "/Users/simon/Dropbox/Development/sqlite-utils/sqlite_utils/db.py", line 1030, in create_table
    self.execute(sql)
  File "/Users/simon/Dropbox/Development/sqlite-utils/sqlite_utils/db.py", line 510, in execute
    return self.conn.execute(sql)
           ^^^^^^^^^^^^^^^^^^^^^^
sqlean.dbapi2.OperationalError: table [foo] already exists
>>> db["foo"].create({"id": int}, ignore=True)
<Table foo (id)>
>>> db["foo"].create({"id": int, "name": str}, replace=True)
<Table foo (id, name)>
>>> db["foo"].create({"id": int, "name": str, "age": int}, transform=True)
<Table foo (id, name, age)>

@simonw
Copy link
Owner Author

simonw commented Jul 22, 2023

Manual testing of CLI command as well:

$ sqlite-utils create-table /tmp/f.db foo id integer                         
$ sqlite-utils create-table /tmp/f.db foo id integer
Error: Table "foo" already exists. Use --replace to delete and replace it.
$ sqlite-utils create-table /tmp/f.db foo id integer --replace
$ sqlite-utils create-table /tmp/f.db foo id                  
$ sqlite-utils schema /tmp/f.db
CREATE TABLE [foo] (
   [id] INTEGER
);
$ sqlite-utils create-table /tmp/f.db foo id integer name str --transform
Error: column types must be one of ('INTEGER', 'TEXT', 'FLOAT', 'BLOB')
$ sqlite-utils create-table /tmp/f.db foo id integer name text --transform
$ sqlite-utils schema /tmp/f.db                                          
CREATE TABLE "foo" (
   [id] INTEGER,
   [name] TEXT
);
$ sqlite-utils create-table /tmp/f.db foo id integer name text --ignore   
$ sqlite-utils create-table /tmp/f.db foo id integer name text --replace
$ sqlite-utils schema /tmp/f.db                                        
CREATE TABLE [foo] (
   [id] INTEGER,
   [name] TEXT
);                                                                    

@simonw
Copy link
Owner Author

simonw commented Jul 22, 2023

I think this broke a test:

$ pytest tests/test_tracer.py                           
=============================================== test session starts ================================================
platform darwin -- Python 3.11.4, pytest-7.2.2, pluggy-1.0.0
rootdir: /Users/simon/Dropbox/Development/sqlite-utils
plugins: icdiff-0.6, hypothesis-6.68.2
collected 2 items                                                                                                  

tests/test_tracer.py F.                                                                                      [100%]

===================================================== FAILURES =====================================================
___________________________________________________ test_tracer ____________________________________________________

    def test_tracer():
        collected = []
        db = Database(
            memory=True, tracer=lambda sql, params: collected.append((sql, params))
        )
        db["dogs"].insert({"name": "Cleopaws"})
        db["dogs"].enable_fts(["name"])
        db["dogs"].search("Cleopaws")
>       assert collected == [
            ("PRAGMA recursive_triggers=on;", None),
            ("select name from sqlite_master where type = 'view'", None),
            ("select name from sqlite_master where type = 'table'", None),
            ("select name from sqlite_master where type = 'view'", None),
            ("CREATE TABLE [dogs] (\n   [name] TEXT\n);\n        ", None),
            ("select name from sqlite_master where type = 'view'", None),
            ("INSERT INTO [dogs] ([name]) VALUES (?);", ["Cleopaws"]),
            ("select name from sqlite_master where type = 'view'", None),
            (
                "CREATE VIRTUAL TABLE [dogs_fts] USING FTS5 (\n    [name],\n    content=[dogs]\n)",
                None,
            ),
            (
                "INSERT INTO [dogs_fts] (rowid, [name])\n    SELECT rowid, [name] FROM [dogs];",
                None,
            ),
            ("select name from sqlite_master where type = 'view'", None),
        ]
E       assert equals failed
E         [                                                  [                                                 
E           ('PRAGMA recursive_triggers=on;', None),           ('PRAGMA recursive_triggers=on;', None),        
E           (                                                                                                  
E             "select name from sqlite_master where type =                                                     
E         'view'",                                                                                             
E             None,                                                       ...
E         
E         ...Full output truncated (13 lines hidden), use '-vv' to show

tests/test_tracer.py:12: AssertionError
============================================= short test summary info ==============================================
FAILED tests/test_tracer.py::test_tracer - assert equals failed
=========================================== 1 failed, 1 passed in 0.05s ============================================

simonw added a commit that referenced this issue Jul 22, 2023
simonw added a commit that referenced this issue Jul 22, 2023
@simonw
Copy link
Owner Author

simonw commented Jul 22, 2023

Here's why that test broke:

if self[name].exists():
if ignore:
return self[name]
elif replace:
self[name].drop()

I added an extra if self[name].exists() check to the db.create_table() method.

simonw added a commit that referenced this issue Jul 22, 2023
simonw added a commit that referenced this issue Jul 22, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request python-library
Projects
None yet
Development

No branches or pull requests

1 participant