diff --git a/sqlparser/lib/src/engine/module/fts5.dart b/sqlparser/lib/src/engine/module/fts5.dart index 35844f79..fd337caa 100644 --- a/sqlparser/lib/src/engine/module/fts5.dart +++ b/sqlparser/lib/src/engine/module/fts5.dart @@ -14,6 +14,7 @@ class Fts5Extension implements Extension { /// FTS5 module for `CREATE VIRTUAL TABLE USING fts5` support class _Fts5Module extends Module { static final RegExp _option = RegExp(r'^(.+)\s*=\s*(.+)$'); + static final RegExp _cleanTicks = RegExp('[\'"]'); _Fts5Module() : super('fts5'); @@ -31,14 +32,21 @@ class _Fts5Module extends Module { // actual syntax is columnNames.add(argument.trim().split(' ').first); } else { - options[match.group(1)!] = match.group(2)!; + options[match.group(1)!.replaceAll(_cleanTicks, '')] = + match.group(2)!.replaceAll(_cleanTicks, ''); } } + //No external content when null or empty String + final contentTable = + (options['content']?.isNotEmpty ?? false) ? options['content'] : null; + //Fallback to "rowid" when option is not set + final contentRowId = options['content_rowid'] ?? 'rowid'; + return Fts5Table._( name: stmt.tableName, - contentTable: options['content'], - contentRowId: options['content_rowid'], + contentTable: contentTable, + contentRowId: (contentTable != null) ? contentRowId : null, columns: [ for (var arg in columnNames) TableColumn(arg, const ResolvedType(type: BasicType.text)), diff --git a/sqlparser/test/engine/module/fts5_test.dart b/sqlparser/test/engine/module/fts5_test.dart index cb6ef575..11876a0c 100644 --- a/sqlparser/test/engine/module/fts5_test.dart +++ b/sqlparser/test/engine/module/fts5_test.dart @@ -18,6 +18,12 @@ void main() { final columns = table.resultColumns; expect(columns, hasLength(1)); expect(columns.single.name, 'bar'); + expect( + table, + isA() + .having((e) => e.contentTable, 'contentTable', null) + .having((e) => e.contentRowId, 'contentRowId', null), + ); }); test('can create fts5 tables with content table', () { @@ -40,6 +46,51 @@ void main() { ); }); + test('does clean option values', () { + final result = engine.analyze('CREATE VIRTUAL TABLE foo USING ' + "fts5(bar , tokenize = 'porter ascii', content='tbl', content_rowid='d')"); + + final table = const SchemaFromCreateTable() + .read(result.root as TableInducingStatement); + + expect( + table, + isA() + .having((e) => e.contentTable, 'contentTable', 'tbl') + .having((e) => e.contentRowId, 'contentRowId', 'd'), + ); + }); + + test('detects empty content table', () { + final result = engine.analyze('CREATE VIRTUAL TABLE foo USING ' + "fts5(bar , tokenize = 'porter ascii',content='', content_rowid='d')"); + + final table = const SchemaFromCreateTable() + .read(result.root as TableInducingStatement); + + expect( + table, + isA() + .having((e) => e.contentTable, 'contentTable', null) + .having((e) => e.contentRowId, 'contentRowId', null), + ); + }); + + test('content table undefined rowid falls back', () { + final result = engine.analyze('CREATE VIRTUAL TABLE foo USING ' + "fts5(bar , tokenize = 'porter ascii',content='tbl')"); + + final table = const SchemaFromCreateTable() + .read(result.root as TableInducingStatement); + + expect( + table, + isA() + .having((e) => e.contentTable, 'contentTable', 'tbl') + .having((e) => e.contentRowId, 'contentRowId', 'rowid'), + ); + }); + group('creating fts5vocab tables', () { final engine = SqlEngine(_fts5Options);