github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/sys/sqlquery/impl_viewrecords.go (about)

     1  /*
     2   * Copyright (c) 2022-present unTill Pro, Ltd.
     3   */
     4  
     5  package sqlquery
     6  
     7  import (
     8  	"context"
     9  	"encoding/json"
    10  	"fmt"
    11  	"strconv"
    12  
    13  	"github.com/blastrain/vitess-sqlparser/sqlparser"
    14  
    15  	"github.com/voedger/voedger/pkg/appdef"
    16  	"github.com/voedger/voedger/pkg/istructs"
    17  	coreutils "github.com/voedger/voedger/pkg/utils"
    18  )
    19  
    20  func readViewRecords(ctx context.Context, wsid istructs.WSID, viewRecordQName appdef.QName, expr sqlparser.Expr, appStructs istructs.IAppStructs, f *filter, callback istructs.ExecQueryCallback) error {
    21  	view := appStructs.AppDef().View(viewRecordQName)
    22  
    23  	if !f.acceptAll {
    24  		allowedFields := make(map[string]bool, view.Key().FieldCount()+view.Value().FieldCount())
    25  		for _, f := range view.Key().Fields() {
    26  			allowedFields[f.Name()] = true
    27  		}
    28  		for _, f := range view.Value().Fields() {
    29  			allowedFields[f.Name()] = true
    30  		}
    31  		for field := range f.fields {
    32  			if !allowedFields[field] {
    33  				return fmt.Errorf("field '%s' does not exist in '%s' value def", field, viewRecordQName)
    34  			}
    35  		}
    36  	}
    37  
    38  	kk := make([]keyPart, 0)
    39  	var keyParts func(expr sqlparser.Expr) error
    40  	keyParts = func(expr sqlparser.Expr) error {
    41  		switch r := expr.(type) {
    42  		case *sqlparser.ComparisonExpr:
    43  			if r.Operator != sqlparser.EqualStr {
    44  				return fmt.Errorf("unsupported operator: %s", r.Operator)
    45  			}
    46  
    47  			cn := r.Left.(*sqlparser.ColName)
    48  
    49  			var name string
    50  			if !cn.Qualifier.IsEmpty() {
    51  				name = fmt.Sprintf("%s.%s", cn.Qualifier.Name, cn.Name)
    52  			} else {
    53  				name = cn.Name.String()
    54  			}
    55  
    56  			kk = append(kk, keyPart{
    57  				name:  name,
    58  				value: r.Right.(*sqlparser.SQLVal).Val,
    59  			})
    60  		case *sqlparser.AndExpr:
    61  			e := keyParts(r.Left)
    62  			if e != nil {
    63  				return e
    64  			}
    65  			e = keyParts(r.Right)
    66  			if e != nil {
    67  				return e
    68  			}
    69  		case nil:
    70  		default:
    71  			return fmt.Errorf("unsupported expression: %T", r)
    72  		}
    73  		return nil
    74  	}
    75  	err := keyParts(expr)
    76  	if err != nil {
    77  		return err
    78  	}
    79  
    80  	kb := appStructs.ViewRecords().KeyBuilder(viewRecordQName)
    81  
    82  	for _, k := range kk {
    83  		f := view.Key().Field(k.name)
    84  		if f == nil {
    85  			return fmt.Errorf("field '%s' does not exist in '%s' key def", k.name, viewRecordQName)
    86  		}
    87  		switch f.DataKind() {
    88  		case appdef.DataKind_int32:
    89  			fallthrough
    90  		case appdef.DataKind_int64:
    91  			fallthrough
    92  		case appdef.DataKind_float32:
    93  			fallthrough
    94  		case appdef.DataKind_float64:
    95  			fallthrough
    96  		case appdef.DataKind_RecordID:
    97  			v, e := strconv.ParseFloat(string(k.value), bitSize64)
    98  			if e != nil {
    99  				return e
   100  			}
   101  			kb.PutNumber(k.name, v)
   102  		case appdef.DataKind_bytes, appdef.DataKind_string:
   103  			fallthrough
   104  		case appdef.DataKind_QName:
   105  			kb.PutChars(k.name, string(k.value))
   106  		default:
   107  			return errUnsupportedDataKind
   108  		}
   109  	}
   110  
   111  	return appStructs.ViewRecords().Read(ctx, wsid, kb, func(key istructs.IKey, value istructs.IValue) (err error) {
   112  		data := coreutils.FieldsToMap(key, appStructs.AppDef(), getFilter(f.filter), coreutils.WithNonNilsOnly())
   113  		for k, v := range coreutils.FieldsToMap(value, appStructs.AppDef(), getFilter(f.filter), coreutils.WithNonNilsOnly()) {
   114  			data[k] = v
   115  		}
   116  		bb, err := json.Marshal(data)
   117  		if err != nil {
   118  			return err
   119  		}
   120  
   121  		return callback(&result{value: string(bb)})
   122  	})
   123  }