-
-
Notifications
You must be signed in to change notification settings - Fork 31
Implement https://sqlite.org/c3ref/vtab_in.html #126
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -32,6 +32,9 @@ type IndexConstraint struct { | |||||||||||||
RValue Value | ||||||||||||||
// RValueKnown indicates whether RValue is set. | ||||||||||||||
RValueKnown bool | ||||||||||||||
// InOpAllAtOnce is true if the constraint is an IN operator | ||||||||||||||
// that can be processed all at once | ||||||||||||||
InOpAllAtOnce bool | ||||||||||||||
Comment on lines
+35
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: We abbreviate operator for common usages, but this is less common, so let's spell it out here.
Suggested change
|
||||||||||||||
} | ||||||||||||||
|
||||||||||||||
func (c *IndexConstraint) copyFromC(tls *libc.TLS, infoPtr uintptr, i int32, ppVal uintptr) { | ||||||||||||||
|
@@ -43,6 +46,12 @@ func (c *IndexConstraint) copyFromC(tls *libc.TLS, infoPtr uintptr, i int32, ppV | |||||||||||||
Usable: src.Fusable != 0, | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
if c.Op == IndexConstraintEq { | ||||||||||||||
if lib.Xsqlite3_vtab_in(tls, infoPtr, int32(i), -1) != 0 { | ||||||||||||||
c.InOpAllAtOnce = true | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
const binaryCollation = "BINARY" | ||||||||||||||
cCollation := lib.Xsqlite3_vtab_collation(tls, infoPtr, int32(i)) | ||||||||||||||
if isCStringEqual(cCollation, binaryCollation) { | ||||||||||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -252,14 +252,17 @@ func (outputs *IndexOutputs) copyToC(tls *libc.TLS, infoPtr uintptr) error { | |||||||||||||
info := (*lib.Sqlite3_index_info)(unsafe.Pointer(infoPtr)) | ||||||||||||||
|
||||||||||||||
aConstraintUsage := info.FaConstraintUsage | ||||||||||||||
for _, u := range outputs.ConstraintUsage { | ||||||||||||||
for i, u := range outputs.ConstraintUsage { | ||||||||||||||
ptr := (*lib.Sqlite3_index_constraint_usage)(unsafe.Pointer(aConstraintUsage)) | ||||||||||||||
ptr.FargvIndex = int32(u.ArgvIndex) | ||||||||||||||
if u.Omit { | ||||||||||||||
ptr.Fomit = 1 | ||||||||||||||
} else { | ||||||||||||||
ptr.Fomit = 0 | ||||||||||||||
} | ||||||||||||||
if u.InOpAllAtOnce { | ||||||||||||||
lib.Xsqlite3_vtab_in(tls, infoPtr, int32(i), 1) | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps check the return value of this? IIUC if it's zero, that indicates that the library user has improperly used this call. |
||||||||||||||
} | ||||||||||||||
aConstraintUsage += unsafe.Sizeof(lib.Sqlite3_index_constraint_usage{}) | ||||||||||||||
} | ||||||||||||||
info.FidxNum = outputs.ID.Num | ||||||||||||||
|
@@ -306,6 +309,9 @@ type IndexConstraintUsage struct { | |||||||||||||
// SQLite will always double-check that rows satisfy the constraint if Omit is false, | ||||||||||||||
// but may skip this check if Omit is true. | ||||||||||||||
Omit bool | ||||||||||||||
// Set InOpAllAtOnce to true if the constraint is an IN operator and the | ||||||||||||||
// Filter method should receive all values at once. | ||||||||||||||
InOpAllAtOnce bool | ||||||||||||||
Comment on lines
+312
to
+314
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: We abbreviate operator for common usages, but this is less common, so let's spell it out here.
Suggested change
The doc comment should also include instructions on how to access the values. |
||||||||||||||
} | ||||||||||||||
|
||||||||||||||
// IndexID is a virtual table index identifier. | ||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,6 +38,19 @@ func ExampleVTable() { | |
log.Fatal(err) | ||
} | ||
|
||
err = sqlitex.ExecuteTransient( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't feel like something that needs to be included in an introductory example. IIUC SQLite will fall back to a simpler set of indices and still operate without this. It definitely still needs testing, but shouldn't be in this example. |
||
conn, | ||
`SELECT a, b FROM templatevtab WHERE a IN (1001, 1003, 1005, 1007, 1009) ORDER BY rowid;`, | ||
&sqlitex.ExecOptions{ | ||
ResultFunc: func(stmt *sqlite.Stmt) error { | ||
fmt.Printf("%4d, %4d\n", stmt.ColumnInt(0), stmt.ColumnInt(1)) | ||
return nil | ||
}, | ||
}, | ||
) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
// Output: | ||
// 1001, 2001 | ||
// 1002, 2002 | ||
|
@@ -49,6 +62,11 @@ func ExampleVTable() { | |
// 1008, 2008 | ||
// 1009, 2009 | ||
// 1010, 2010 | ||
// 1001, 2001 | ||
// 1003, 2003 | ||
// 1005, 2005 | ||
// 1007, 2007 | ||
// 1009, 2009 | ||
} | ||
|
||
type templatevtab struct{} | ||
|
@@ -66,10 +84,22 @@ func templatevtabConnect(c *sqlite.Conn, opts *sqlite.VTableConnectOptions) (sql | |
return vtab, cfg, nil | ||
} | ||
|
||
func (vt *templatevtab) BestIndex(*sqlite.IndexInputs) (*sqlite.IndexOutputs, error) { | ||
func (vt *templatevtab) BestIndex(in *sqlite.IndexInputs) (*sqlite.IndexOutputs, error) { | ||
|
||
constraintUsage := make([]sqlite.IndexConstraintUsage, len(in.Constraints)) | ||
|
||
argvIndex := 1 | ||
for i, c := range in.Constraints { | ||
if c.InOpAllAtOnce { | ||
constraintUsage[i].ArgvIndex = argvIndex | ||
constraintUsage[i].InOpAllAtOnce = true | ||
argvIndex++ | ||
} | ||
} | ||
return &sqlite.IndexOutputs{ | ||
EstimatedCost: 10, | ||
EstimatedRows: 10, | ||
ConstraintUsage: constraintUsage, | ||
EstimatedCost: 10, | ||
EstimatedRows: 10, | ||
}, nil | ||
} | ||
|
||
|
@@ -86,10 +116,17 @@ func (vt *templatevtab) Destroy() error { | |
} | ||
|
||
type templatevtabCursor struct { | ||
rowid int64 | ||
rowid int64 | ||
inVals []int64 | ||
} | ||
|
||
func (cur *templatevtabCursor) Filter(id sqlite.IndexID, argv []sqlite.Value) error { | ||
for _, v := range argv { | ||
for vv := range v.All() { | ||
cur.inVals = append(cur.inVals, vv.Int64()) | ||
} | ||
} | ||
|
||
cur.rowid = 1 | ||
return nil | ||
} | ||
|
@@ -102,8 +139,14 @@ func (cur *templatevtabCursor) Next() error { | |
func (cur *templatevtabCursor) Column(i int, noChange bool) (sqlite.Value, error) { | ||
switch i { | ||
case templatevarColumnA: | ||
if len(cur.inVals) > 0 { | ||
return sqlite.IntegerValue(cur.inVals[cur.rowid-1]), nil | ||
} | ||
return sqlite.IntegerValue(1000 + cur.rowid), nil | ||
case templatevarColumnB: | ||
if len(cur.inVals) > 0 { | ||
return sqlite.IntegerValue(1000 + cur.inVals[cur.rowid-1]), nil | ||
} | ||
return sqlite.IntegerValue(2000 + cur.rowid), nil | ||
default: | ||
panic("unreachable") | ||
|
@@ -115,6 +158,9 @@ func (cur *templatevtabCursor) RowID() (int64, error) { | |
} | ||
|
||
func (cur *templatevtabCursor) EOF() bool { | ||
if len(cur.inVals) > 0 { | ||
return int(cur.rowid) > len(cur.inVals) | ||
} | ||
return cur.rowid > 10 | ||
} | ||
|
||
|
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I appreciate the simplicity of the iterator interface, I'm not quite sold on this API for a few reasons:
Value
objects from these functions is somewhat tricky and that complexity is not being surfaced to the user.I don't have a great counter-proposal for this design, but I didn't want to block the feedback on me coming up with something.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah good points. What if this were a method on the VTCursor interface instead? Then it would be much harder to use outside the Filter function (the only place
sqlite_vtab_in_{first,next}
are valid to use).Also do you think the golang iter interface is ok or passing in a callback function is preferred?