table.virtual_table_using property, closes #196

This commit is contained in:
Simon Willison 2020-11-04 19:53:32 -08:00
commit 59d8689ed0
3 changed files with 81 additions and 1 deletions

View file

@ -1430,6 +1430,12 @@ The ``detect_fts()`` method returns the associated SQLite FTS table name, if one
>> db["authors"].detect_fts()
"authors_fts"
The ``.virtual_table_using`` property reveals if a table is a virtual table. It returns ``None`` for regular tables and the upper case version of the type of virtual table otherwise. For example::
>> db["authors"].enable_fts(["name"])
>> db["authors_fts"].virtual_table_using
"FTS5"
.. _python_api_fts:
Enabling full-text search

View file

@ -10,12 +10,32 @@ import itertools
import json
import os
import pathlib
import re
import sys
import textwrap
import uuid
SQLITE_MAX_VARS = 999
_virtual_table_using_re = re.compile(
r"""
^ # Start of string
\s*CREATE\s+VIRTUAL\s+TABLE\s+ # CREATE VIRTUAL TABLE
(
'(?P<squoted_table>[^']*(?:''[^']*)*)' | # single quoted name
"(?P<dquoted_table>[^"]*(?:""[^"]*)*)" | # double quoted name
`(?P<backtick_table>[^`]+)` | # `backtick` quoted name
\[(?P<squarequoted_table>[^\]]+)\] | # [...] quoted name
(?P<identifier> # SQLite non-quoted identifier
[A-Za-z_\u0080-\uffff] # \u0080-\uffff = "any character larger than u007f"
[A-Za-z_\u0080-\uffff0-9\$]* # zero-or-more alphanemuric or $
)
)
\s+(IF\s+NOT\s+EXISTS\s+)? # IF NOT EXISTS (optional)
USING\s+(?P<using>\w+) # e.g. USING FTS5
""",
re.VERBOSE | re.IGNORECASE,
)
try:
import pandas as pd
@ -676,6 +696,14 @@ class Table(Queryable):
)
return fks
@property
def virtual_table_using(self):
"Returns type of virtual table or None if this is not a virtual table"
match = _virtual_table_using_re.match(self.schema)
if match is None:
return None
return match.groupdict()["using"].upper()
@property
def indexes(self):
sql = 'PRAGMA index_list("{}")'.format(self.name)

View file

@ -1,4 +1,4 @@
from sqlite_utils.db import Index, View
from sqlite_utils.db import Index, View, Database
import pytest
@ -142,3 +142,49 @@ def test_triggers(fresh_db):
(t.name, t.table) for t in fresh_db["authors"].triggers
}
assert [] == fresh_db["other"].triggers
@pytest.mark.parametrize(
"sql,expected_name,expected_using",
[
(
"""
CREATE VIRTUAL TABLE foo USING FTS5(name)
""",
"foo",
"FTS5",
),
(
"""
CREATE VIRTUAL TABLE "foo" USING FTS4(name)
""",
"foo",
"FTS4",
),
(
"""
CREATE VIRTUAL TABLE IF NOT EXISTS `foo` USING FTS4(name)
""",
"foo",
"FTS4",
),
(
"""
CREATE VIRTUAL TABLE IF NOT EXISTS `foo` USING fts5(name)
""",
"foo",
"FTS5",
),
(
"""
CREATE TABLE IF NOT EXISTS `foo` (id integer primary key)
""",
"foo",
None,
),
],
)
def test_virtual_table_using(sql, expected_name, expected_using):
db = Database(memory=True)
db.execute(sql)
assert db[expected_name].virtual_table_using == expected_using