github.com/mithrandie/csvq@v1.18.1/lib/query/cursor.go (about) 1 package query 2 3 import ( 4 "context" 5 "errors" 6 "strings" 7 "sync" 8 9 "github.com/mithrandie/csvq/lib/parser" 10 "github.com/mithrandie/csvq/lib/value" 11 12 "github.com/mithrandie/ternary" 13 ) 14 15 var errUndeclaredCursor = errors.New("undeclared cursor") 16 var errPseudoCursor = errors.New("unpermmitted pseudo cursor usage") 17 var errCursorClosed = errors.New("cursor cloased") 18 19 type CursorMap struct { 20 *SyncMap 21 } 22 23 func NewCursorMap() CursorMap { 24 return CursorMap{ 25 NewSyncMap(), 26 } 27 } 28 29 func (m CursorMap) IsEmpty() bool { 30 return m.SyncMap == nil 31 } 32 33 func (m CursorMap) Store(name string, val *Cursor) { 34 m.store(strings.ToUpper(name), val) 35 } 36 37 func (m CursorMap) LoadDirect(name string) (interface{}, bool) { 38 return m.load(strings.ToUpper(name)) 39 } 40 41 func (m CursorMap) Load(name string) (*Cursor, bool) { 42 if v, ok := m.load(strings.ToUpper(name)); ok { 43 return v.(*Cursor), true 44 } 45 return nil, false 46 } 47 48 func (m CursorMap) Delete(name string) { 49 m.delete(strings.ToUpper(name)) 50 } 51 52 func (m CursorMap) Exists(name string) bool { 53 return m.exists(strings.ToUpper(name)) 54 } 55 56 func (m CursorMap) Declare(expr parser.CursorDeclaration) error { 57 if m.Exists(expr.Cursor.Literal) { 58 return NewCursorRedeclaredError(expr.Cursor) 59 } 60 m.Store(expr.Cursor.Literal, NewCursor(expr)) 61 return nil 62 } 63 64 func (m CursorMap) AddPseudoCursor(name parser.Identifier, values []value.Primary) error { 65 if m.Exists(name.Literal) { 66 return NewCursorRedeclaredError(name) 67 } 68 m.Store(name.Literal, NewPseudoCursor(name.Literal, values)) 69 return nil 70 } 71 72 func (m CursorMap) Dispose(name parser.Identifier) error { 73 if cur, ok := m.Load(name.Literal); ok { 74 if cur.isPseudo { 75 return errPseudoCursor 76 } 77 m.Delete(name.Literal) 78 return nil 79 } 80 return errUndeclaredCursor 81 } 82 83 func (m CursorMap) Open(ctx context.Context, scope *ReferenceScope, name parser.Identifier, values []parser.ReplaceValue) error { 84 if cur, ok := m.Load(name.Literal); ok { 85 return cur.Open(ctx, scope, name, values) 86 } 87 return errUndeclaredCursor 88 } 89 90 func (m CursorMap) Close(name parser.Identifier) error { 91 if cur, ok := m.Load(name.Literal); ok { 92 return cur.Close(name) 93 } 94 return errUndeclaredCursor 95 } 96 97 func (m CursorMap) Fetch(name parser.Identifier, position int, number int) ([]value.Primary, error) { 98 if cur, ok := m.Load(name.Literal); ok { 99 return cur.Fetch(name, position, number) 100 } 101 return nil, errUndeclaredCursor 102 } 103 104 func (m CursorMap) IsOpen(name parser.Identifier) (ternary.Value, error) { 105 if cur, ok := m.Load(name.Literal); ok { 106 return cur.IsOpen(), nil 107 } 108 return ternary.FALSE, errUndeclaredCursor 109 } 110 111 func (m CursorMap) IsInRange(name parser.Identifier) (ternary.Value, error) { 112 if cur, ok := m.Load(name.Literal); ok { 113 t, err := cur.IsInRange() 114 if err == errCursorClosed { 115 return ternary.FALSE, NewCursorClosedError(name) 116 } 117 return t, nil 118 } 119 return ternary.FALSE, errUndeclaredCursor 120 } 121 122 func (m CursorMap) Count(name parser.Identifier) (int, error) { 123 if cur, ok := m.Load(name.Literal); ok { 124 i, err := cur.Count() 125 if err != nil { 126 return 0, NewCursorClosedError(name) 127 } 128 return i, nil 129 } 130 return 0, errUndeclaredCursor 131 } 132 133 type Cursor struct { 134 Name string 135 query parser.SelectQuery 136 statement parser.Identifier 137 view *View 138 index int 139 fetched bool 140 141 isPseudo bool 142 143 mtx *sync.Mutex 144 } 145 146 func NewCursor(e parser.CursorDeclaration) *Cursor { 147 return &Cursor{ 148 Name: e.Cursor.Literal, 149 query: e.Query, 150 statement: e.Statement, 151 mtx: &sync.Mutex{}, 152 } 153 } 154 155 func NewPseudoCursor(name string, values []value.Primary) *Cursor { 156 header := NewHeader("", []string{"c1"}) 157 158 records := make(RecordSet, len(values)) 159 for i, v := range values { 160 records[i] = NewRecord([]value.Primary{v}) 161 } 162 view := NewView() 163 view.Header = header 164 view.RecordSet = records 165 166 return &Cursor{ 167 Name: name, 168 view: view, 169 index: -1, 170 fetched: false, 171 isPseudo: true, 172 mtx: &sync.Mutex{}, 173 } 174 } 175 176 func (c *Cursor) Open(ctx context.Context, scope *ReferenceScope, name parser.Identifier, values []parser.ReplaceValue) error { 177 if c.isPseudo { 178 return NewPseudoCursorError(name) 179 } 180 181 c.mtx.Lock() 182 defer c.mtx.Unlock() 183 184 if c.view != nil { 185 return NewCursorOpenError(name) 186 } 187 188 var view *View 189 var err error 190 if c.query.SelectEntity != nil { 191 view, err = Select(ctx, scope, c.query) 192 } else { 193 prepared, e := scope.Tx.PreparedStatements.Get(c.statement) 194 if e != nil { 195 return e 196 } 197 if len(prepared.Statements) != 1 { 198 return NewInvalidCursorStatementError(c.statement) 199 } 200 stmt, ok := prepared.Statements[0].(parser.SelectQuery) 201 if !ok { 202 return NewInvalidCursorStatementError(c.statement) 203 } 204 view, err = Select(ContextForPreparedStatement(ctx, NewReplaceValues(values)), scope, stmt) 205 } 206 if err != nil { 207 return err 208 } 209 210 c.view = view 211 c.index = -1 212 c.fetched = false 213 return nil 214 } 215 216 func (c *Cursor) Close(name parser.Identifier) error { 217 if c.isPseudo { 218 return NewPseudoCursorError(name) 219 } 220 221 c.mtx.Lock() 222 223 c.view = nil 224 c.index = 0 225 c.fetched = false 226 227 c.mtx.Unlock() 228 return nil 229 } 230 231 func (c *Cursor) Fetch(name parser.Identifier, position int, number int) ([]value.Primary, error) { 232 if c.view == nil { 233 return nil, NewCursorClosedError(name) 234 } 235 236 c.mtx.Lock() 237 defer c.mtx.Unlock() 238 239 if !c.fetched { 240 c.fetched = true 241 } 242 243 switch position { 244 case parser.ABSOLUTE: 245 c.index = number 246 case parser.RELATIVE: 247 c.index = c.index + number 248 case parser.FIRST: 249 c.index = 0 250 case parser.LAST: 251 c.index = c.view.RecordLen() - 1 252 case parser.PRIOR: 253 c.index = c.index - 1 254 default: // NEXT 255 c.index = c.index + 1 256 } 257 258 if c.index < 0 { 259 c.index = -1 260 return nil, nil 261 } 262 263 if c.view.RecordLen() <= c.index { 264 c.index = c.view.RecordLen() 265 return nil, nil 266 } 267 268 list := make([]value.Primary, len(c.view.RecordSet[c.index])) 269 for i := range c.view.RecordSet[c.index] { 270 list[i] = c.view.RecordSet[c.index][i][0] 271 } 272 273 return list, nil 274 } 275 276 func (c *Cursor) IsOpen() ternary.Value { 277 return ternary.ConvertFromBool(c.view != nil) 278 } 279 280 func (c *Cursor) IsInRange() (ternary.Value, error) { 281 if c.view == nil { 282 return ternary.FALSE, errCursorClosed 283 } 284 if !c.fetched { 285 return ternary.UNKNOWN, nil 286 } 287 return ternary.ConvertFromBool(-1 < c.index && c.index < c.view.RecordLen()), nil 288 } 289 290 func (c *Cursor) Count() (int, error) { 291 if c.view == nil { 292 return 0, errCursorClosed 293 } 294 return c.view.RecordLen(), nil 295 } 296 297 func (c *Cursor) Pointer() (int, error) { 298 return c.index, nil 299 }