github.com/ncruces/go-sqlite3@v0.15.1-0.20240520133447-53eef1510ff0/ext/array/array.go (about) 1 // Package array provides the array table-valued SQL function. 2 // 3 // https://sqlite.org/carray.html 4 package array 5 6 import ( 7 "fmt" 8 "reflect" 9 10 "github.com/ncruces/go-sqlite3" 11 "github.com/ncruces/go-sqlite3/internal/util" 12 ) 13 14 // Register registers the array single-argument, table-valued SQL function. 15 // The argument must be bound to a Go slice or array of 16 // ints, floats, bools, strings or byte slices, 17 // using [sqlite3.BindPointer] or [sqlite3.Pointer]. 18 func Register(db *sqlite3.Conn) { 19 sqlite3.CreateModule[array](db, "array", nil, 20 func(db *sqlite3.Conn, _, _, _ string, _ ...string) (array, error) { 21 err := db.DeclareVTab(`CREATE TABLE x(value, array HIDDEN)`) 22 return array{}, err 23 }) 24 } 25 26 type array struct{} 27 28 func (array) BestIndex(idx *sqlite3.IndexInfo) error { 29 for i, cst := range idx.Constraint { 30 if cst.Column == 1 && cst.Op == sqlite3.INDEX_CONSTRAINT_EQ && cst.Usable { 31 idx.ConstraintUsage[i] = sqlite3.IndexConstraintUsage{ 32 Omit: true, 33 ArgvIndex: 1, 34 } 35 idx.EstimatedCost = 1 36 idx.EstimatedRows = 100 37 return nil 38 } 39 } 40 return sqlite3.CONSTRAINT 41 } 42 43 func (array) Open() (sqlite3.VTabCursor, error) { 44 return &cursor{}, nil 45 } 46 47 type cursor struct { 48 array reflect.Value 49 rowID int 50 } 51 52 func (c *cursor) EOF() bool { 53 return c.rowID >= c.array.Len() 54 } 55 56 func (c *cursor) Next() error { 57 c.rowID++ 58 return nil 59 } 60 61 func (c *cursor) RowID() (int64, error) { 62 return int64(c.rowID), nil 63 } 64 65 func (c *cursor) Column(ctx *sqlite3.Context, n int) error { 66 if n != 0 { 67 return nil 68 } 69 70 v := c.array.Index(c.rowID) 71 k := v.Kind() 72 73 if k == reflect.Interface { 74 if v.IsNil() { 75 ctx.ResultNull() 76 return nil 77 } 78 v = v.Elem() 79 k = v.Kind() 80 } 81 82 switch { 83 case v.CanInt(): 84 ctx.ResultInt64(v.Int()) 85 86 case v.CanUint(): 87 i64 := int64(v.Uint()) 88 if i64 < 0 { 89 return fmt.Errorf("array: integer element overflow:%.0w %d", sqlite3.MISMATCH, v.Uint()) 90 } 91 ctx.ResultInt64(i64) 92 93 case v.CanFloat(): 94 ctx.ResultFloat(v.Float()) 95 96 case k == reflect.Bool: 97 ctx.ResultBool(v.Bool()) 98 99 case k == reflect.String: 100 ctx.ResultText(v.String()) 101 102 case (k == reflect.Slice || k == reflect.Array && v.CanAddr()) && 103 v.Type().Elem().Kind() == reflect.Uint8: 104 ctx.ResultBlob(v.Bytes()) 105 106 default: 107 return fmt.Errorf("array: unsupported element:%.0w %v", sqlite3.MISMATCH, util.ReflectType(v)) 108 } 109 return nil 110 } 111 112 func (c *cursor) Filter(idxNum int, idxStr string, arg ...sqlite3.Value) error { 113 array := reflect.ValueOf(arg[0].Pointer()) 114 array, err := indexable(array) 115 if err != nil { 116 return err 117 } 118 119 c.array = array 120 c.rowID = 0 121 return nil 122 } 123 124 func indexable(v reflect.Value) (reflect.Value, error) { 125 switch v.Kind() { 126 case reflect.Slice: 127 return v, nil 128 case reflect.Array: 129 return v, nil 130 case reflect.Pointer: 131 if v := v.Elem(); v.Kind() == reflect.Array { 132 return v, nil 133 } 134 } 135 return v, fmt.Errorf("array: unsupported argument:%.0w %v", sqlite3.MISMATCH, util.ReflectType(v)) 136 }