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  }