From f7c3fb8062f1c6eb53445e06dac77c6b6e85221d Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Sun, 5 Jan 2025 19:32:42 +0000 Subject: [PATCH] Lines delimiter. --- ext/closure/closure.go | 28 ++++++++++------------ ext/lines/lines.go | 52 ++++++++++++++++++++++++++++++++++------- ext/lines/lines_test.go | 2 +- 3 files changed, 56 insertions(+), 26 deletions(-) diff --git a/ext/closure/closure.go b/ext/closure/closure.go index 43e911e..6455b72 100644 --- a/ext/closure/closure.go +++ b/ext/closure/closure.go @@ -84,10 +84,11 @@ func (c *closure) BestIndex(idx *sqlite3.IndexInfo) error { cost := 1e7 for i, cst := range idx.Constraint { - if !cst.Usable { + switch { + case !cst.Usable: continue - } - if plan&1 == 0 && cst.Column == _COL_ROOT { + + case plan&1 == 0 && cst.Column == _COL_ROOT: switch cst.Op { case sqlite3.INDEX_CONSTRAINT_EQ: plan |= 1 @@ -97,9 +98,8 @@ func (c *closure) BestIndex(idx *sqlite3.IndexInfo) error { Omit: true, } } - continue - } - if plan&0xf0 == 0 && cst.Column == _COL_DEPTH { + + case plan&0xf0 == 0 && cst.Column == _COL_DEPTH: switch cst.Op { case sqlite3.INDEX_CONSTRAINT_LT, sqlite3.INDEX_CONSTRAINT_LE, sqlite3.INDEX_CONSTRAINT_EQ: plan |= posi << 4 @@ -110,9 +110,8 @@ func (c *closure) BestIndex(idx *sqlite3.IndexInfo) error { plan |= 2 } } - continue - } - if plan&0xf00 == 0 && cst.Column == _COL_TABLENAME { + + case plan&0xf00 == 0 && cst.Column == _COL_TABLENAME: switch cst.Op { case sqlite3.INDEX_CONSTRAINT_EQ: plan |= posi << 8 @@ -123,9 +122,8 @@ func (c *closure) BestIndex(idx *sqlite3.IndexInfo) error { Omit: true, } } - continue - } - if plan&0xf000 == 0 && cst.Column == _COL_IDCOLUMN { + + case plan&0xf000 == 0 && cst.Column == _COL_IDCOLUMN: switch cst.Op { case sqlite3.INDEX_CONSTRAINT_EQ: plan |= posi << 12 @@ -135,9 +133,8 @@ func (c *closure) BestIndex(idx *sqlite3.IndexInfo) error { Omit: true, } } - continue - } - if plan&0xf0000 == 0 && cst.Column == _COL_PARENTCOLUMN { + + case plan&0xf0000 == 0 && cst.Column == _COL_PARENTCOLUMN: switch cst.Op { case sqlite3.INDEX_CONSTRAINT_EQ: plan |= posi << 16 @@ -147,7 +144,6 @@ func (c *closure) BestIndex(idx *sqlite3.IndexInfo) error { Omit: true, } } - continue } } diff --git a/ext/lines/lines.go b/ext/lines/lines.go index b38bed7..c2e84e0 100644 --- a/ext/lines/lines.go +++ b/ext/lines/lines.go @@ -38,7 +38,7 @@ func RegisterFS(db *sqlite3.Conn, fsys fs.FS) error { return errors.Join( sqlite3.CreateModule(db, "lines", nil, func(db *sqlite3.Conn, _, _, _ string, _ ...string) (lines, error) { - err := db.DeclareVTab(`CREATE TABLE x(line TEXT, data HIDDEN)`) + err := db.DeclareVTab(`CREATE TABLE x(line TEXT, data HIDDEN, delim HIDDEN)`) if err == nil { err = db.VTabConfig(sqlite3.VTAB_INNOCUOUS) } @@ -46,7 +46,7 @@ func RegisterFS(db *sqlite3.Conn, fsys fs.FS) error { }), sqlite3.CreateModule(db, "lines_read", nil, func(db *sqlite3.Conn, _, _, _ string, _ ...string) (lines, error) { - err := db.DeclareVTab(`CREATE TABLE x(line TEXT, data HIDDEN)`) + err := db.DeclareVTab(`CREATE TABLE x(line TEXT, data HIDDEN, delim HIDDEN)`) if err == nil { err = db.VTabConfig(sqlite3.VTAB_DIRECTONLY) } @@ -58,19 +58,29 @@ type lines struct { fsys fs.FS } -func (l lines) BestIndex(idx *sqlite3.IndexInfo) error { +func (l lines) BestIndex(idx *sqlite3.IndexInfo) (err error) { + err = sqlite3.CONSTRAINT for i, cst := range idx.Constraint { - if cst.Column == 1 && cst.Op == sqlite3.INDEX_CONSTRAINT_EQ && cst.Usable { + if !cst.Usable || cst.Op != sqlite3.INDEX_CONSTRAINT_EQ { + continue + } + switch cst.Column { + case 1: idx.ConstraintUsage[i] = sqlite3.IndexConstraintUsage{ Omit: true, ArgvIndex: 1, } idx.EstimatedCost = 1e6 idx.EstimatedRows = 100 - return nil + err = nil + case 2: + idx.ConstraintUsage[i] = sqlite3.IndexConstraintUsage{ + Omit: true, + ArgvIndex: 2, + } } } - return sqlite3.CONSTRAINT + return err } func (l lines) Open() (sqlite3.VTabCursor, error) { @@ -85,6 +95,7 @@ type cursor struct { line []byte rowID int64 eof bool + delim byte } func (c *cursor) EOF() bool { @@ -140,6 +151,15 @@ func (c *reader) Filter(idxNum int, idxStr string, arg ...sqlite3.Value) error { return fmt.Errorf("lines: unsupported argument:%.0w %v", sqlite3.MISMATCH, typ) } + c.delim = '\n' + if len(arg) > 1 { + b := arg[1].RawText() + if len(b) != 1 { + return fmt.Errorf("lines: delimiter must be a single byte%.0w", sqlite3.MISMATCH) + } + c.delim = b[0] + } + c.reader = bufio.NewReader(r) c.closer, _ = r.(io.Closer) c.rowID = 0 @@ -150,7 +170,12 @@ func (c *reader) Next() (err error) { c.line = c.line[:0] for more := true; more; { var line []byte - line, more, err = c.reader.ReadLine() + if c.delim == '\n' { + line, more, err = c.reader.ReadLine() + } else { + line, err = c.reader.ReadSlice(c.delim) + more = err == bufio.ErrBufferFull + } c.line = append(c.line, line...) } if err == io.EOF { @@ -177,18 +202,27 @@ func (c *buffer) Filter(idxNum int, idxStr string, arg ...sqlite3.Value) error { return fmt.Errorf("lines: unsupported argument:%.0w %v", sqlite3.MISMATCH, typ) } + c.delim = '\n' + if len(arg) > 1 { + b := arg[1].RawText() + if len(b) != 1 { + return fmt.Errorf("lines: delimiter must be a single byte%.0w", sqlite3.MISMATCH) + } + c.delim = b[0] + } + c.rowID = 0 return c.Next() } func (c *buffer) Next() error { - i := bytes.IndexByte(c.data, '\n') + i := bytes.IndexByte(c.data, c.delim) j := i + 1 switch { case i < 0: i = len(c.data) j = i - case i > 0 && c.data[i-1] == '\r': + case i > 0 && c.delim == '\n' && c.data[i-1] == '\r': i-- } c.eof = len(c.data) == 0 diff --git a/ext/lines/lines_test.go b/ext/lines/lines_test.go index 4cc42cf..917eff2 100644 --- a/ext/lines/lines_test.go +++ b/ext/lines/lines_test.go @@ -163,7 +163,7 @@ func Test_lines_test(t *testing.T) { } defer db.Close() - rows, err := db.Query(`SELECT rowid, line FROM lines_read(?)`, "lines_test.go") + rows, err := db.Query(`SELECT rowid, line FROM lines_read(?, '}')`, "lines_test.go") if errors.Is(err, os.ErrNotExist) { t.Skip(err) }