github.com/mithrandie/csvq@v1.18.1/lib/query/reference_scope.go (about) 1 package query 2 3 import ( 4 "context" 5 "fmt" 6 "strings" 7 "sync" 8 "time" 9 10 "github.com/mithrandie/csvq/lib/option" 11 "github.com/mithrandie/csvq/lib/parser" 12 "github.com/mithrandie/csvq/lib/value" 13 14 "github.com/mithrandie/ternary" 15 ) 16 17 const LimitToUseFieldIndexSliceChache = 8 18 19 var blockScopePool = sync.Pool{ 20 New: func() interface{} { 21 return NewBlockScope() 22 }, 23 } 24 25 func GetBlockScope() BlockScope { 26 scope := blockScopePool.Get().(BlockScope) 27 return scope 28 } 29 30 func PutBlockScope(scope BlockScope) { 31 scope.Clear() 32 blockScopePool.Put(scope) 33 } 34 35 var nodeScopePool = sync.Pool{ 36 New: func() interface{} { 37 return NewNodeScope() 38 }, 39 } 40 41 func GetNodeScope() NodeScope { 42 scope := nodeScopePool.Get().(NodeScope) 43 return scope 44 } 45 46 func PutNodeScope(scope NodeScope) { 47 scope.Clear() 48 nodeScopePool.Put(scope) 49 } 50 51 type BlockScope struct { 52 Variables VariableMap 53 TemporaryTables ViewMap 54 Cursors CursorMap 55 Functions UserDefinedFunctionMap 56 } 57 58 func NewBlockScope() BlockScope { 59 return BlockScope{ 60 Variables: NewVariableMap(), 61 TemporaryTables: NewViewMap(), 62 Cursors: NewCursorMap(), 63 Functions: NewUserDefinedFunctionMap(), 64 } 65 } 66 67 func (scope BlockScope) Clear() { 68 scope.Variables.Clear() 69 scope.TemporaryTables.Clear() 70 scope.Cursors.Clear() 71 scope.Functions.Clear() 72 } 73 74 type NodeScope struct { 75 inlineTables InlineTableMap 76 aliases AliasMap 77 } 78 79 func NewNodeScope() NodeScope { 80 return NodeScope{ 81 inlineTables: make(InlineTableMap), 82 aliases: make(AliasMap), 83 } 84 } 85 86 func (scope NodeScope) Clear() { 87 scope.inlineTables.Clear() 88 scope.aliases.Clear() 89 } 90 91 type ReferenceRecord struct { 92 view *View 93 recordIndex int 94 95 cache *FieldIndexCache 96 } 97 98 func NewReferenceRecord(view *View, recordIdx int, cacheLen int) ReferenceRecord { 99 return ReferenceRecord{ 100 view: view, 101 recordIndex: recordIdx, 102 cache: NewFieldIndexCache(cacheLen, LimitToUseFieldIndexSliceChache), 103 } 104 } 105 106 func (r *ReferenceRecord) IsInRange() bool { 107 return -1 < r.recordIndex && r.recordIndex < r.view.RecordLen() 108 } 109 110 type FieldIndexCache struct { 111 limitToUseSlice int 112 m map[parser.QueryExpression]int 113 exprs []parser.QueryExpression 114 indices []int 115 } 116 117 func NewFieldIndexCache(initCap int, limitToUseSlice int) *FieldIndexCache { 118 return &FieldIndexCache{ 119 limitToUseSlice: limitToUseSlice, 120 m: nil, 121 exprs: make([]parser.QueryExpression, 0, initCap), 122 indices: make([]int, 0, initCap), 123 } 124 } 125 126 func (c *FieldIndexCache) Get(expr parser.QueryExpression) (int, bool) { 127 if c.m != nil { 128 idx, ok := c.m[expr] 129 return idx, ok 130 } 131 132 for i := range c.exprs { 133 if expr == c.exprs[i] { 134 return c.indices[i], true 135 } 136 } 137 return -1, false 138 } 139 140 func (c *FieldIndexCache) Add(expr parser.QueryExpression, idx int) { 141 if c.m == nil && c.limitToUseSlice <= len(c.exprs) { 142 c.m = make(map[parser.QueryExpression]int, c.limitToUseSlice*2) 143 for i := range c.exprs { 144 c.m[c.exprs[i]] = c.indices[i] 145 } 146 c.exprs = nil 147 c.indices = nil 148 } 149 150 if c.m == nil { 151 c.exprs = append(c.exprs, expr) 152 c.indices = append(c.indices, idx) 153 } else { 154 c.m[expr] = idx 155 } 156 } 157 158 type ReferenceScope struct { 159 Tx *Transaction 160 161 Blocks []BlockScope 162 nodes []NodeScope 163 164 cachedFilePath map[string]string 165 now time.Time 166 167 Records []ReferenceRecord 168 169 RecursiveTable *parser.InlineTable 170 RecursiveTmpView *View 171 RecursiveCount *int64 172 } 173 174 func NewReferenceScope(tx *Transaction) *ReferenceScope { 175 return NewReferenceScopeWithBlock(tx, GetBlockScope()) 176 } 177 178 func NewReferenceScopeWithBlock(tx *Transaction, scope BlockScope) *ReferenceScope { 179 return &ReferenceScope{ 180 Tx: tx, 181 Blocks: []BlockScope{scope}, 182 nodes: nil, 183 } 184 } 185 186 func (rs *ReferenceScope) CreateScopeForRecordEvaluation(view *View, recordIndex int) *ReferenceScope { 187 records := make([]ReferenceRecord, len(rs.Records)+1) 188 records[0] = NewReferenceRecord(view, recordIndex, view.FieldLen()) 189 for i := range rs.Records { 190 records[i+1] = rs.Records[i] 191 } 192 return rs.createScope(records) 193 } 194 195 func (rs *ReferenceScope) CreateScopeForSequentialEvaluation(view *View) *ReferenceScope { 196 return rs.CreateScopeForRecordEvaluation(view, -1) 197 } 198 199 func (rs *ReferenceScope) CreateScopeForAnalytics() *ReferenceScope { 200 records := make([]ReferenceRecord, len(rs.Records)) 201 records[0] = NewReferenceRecord(rs.Records[0].view, -1, rs.Records[0].view.FieldLen()) 202 for i := 1; i < len(rs.Records); i++ { 203 records[i] = rs.Records[i] 204 } 205 return rs.createScope(records) 206 } 207 208 func (rs *ReferenceScope) createScope(referenceRecords []ReferenceRecord) *ReferenceScope { 209 return &ReferenceScope{ 210 Tx: rs.Tx, 211 Blocks: rs.Blocks, 212 nodes: rs.nodes, 213 cachedFilePath: rs.cachedFilePath, 214 now: rs.now, 215 Records: referenceRecords, 216 RecursiveTable: rs.RecursiveTable, 217 RecursiveTmpView: rs.RecursiveTmpView, 218 RecursiveCount: rs.RecursiveCount, 219 } 220 } 221 222 func (rs *ReferenceScope) CreateChild() *ReferenceScope { 223 blocks := make([]BlockScope, len(rs.Blocks)+1) 224 blocks[0] = GetBlockScope() 225 for i := range rs.Blocks { 226 blocks[i+1] = rs.Blocks[i] 227 } 228 229 return &ReferenceScope{ 230 Tx: rs.Tx, 231 Blocks: blocks, 232 nodes: nil, 233 cachedFilePath: rs.cachedFilePath, 234 now: rs.now, 235 RecursiveTable: rs.RecursiveTable, 236 RecursiveTmpView: rs.RecursiveTmpView, 237 RecursiveCount: rs.RecursiveCount, 238 } 239 } 240 241 func (rs *ReferenceScope) CreateNode() *ReferenceScope { 242 nodes := make([]NodeScope, len(rs.nodes)+1) 243 nodes[0] = GetNodeScope() 244 for i := range rs.nodes { 245 nodes[i+1] = rs.nodes[i] 246 } 247 248 node := &ReferenceScope{ 249 Tx: rs.Tx, 250 Blocks: rs.Blocks, 251 nodes: nodes, 252 cachedFilePath: rs.cachedFilePath, 253 now: rs.now, 254 Records: rs.Records, 255 RecursiveTable: rs.RecursiveTable, 256 RecursiveTmpView: rs.RecursiveTmpView, 257 RecursiveCount: rs.RecursiveCount, 258 } 259 260 if node.cachedFilePath == nil { 261 node.cachedFilePath = make(map[string]string) 262 } 263 if node.now.IsZero() { 264 node.now = option.Now(rs.Tx.Flags.GetTimeLocation()) 265 } 266 267 return node 268 } 269 270 func (rs *ReferenceScope) Global() BlockScope { 271 return rs.Blocks[len(rs.Blocks)-1] 272 } 273 274 func (rs *ReferenceScope) CurrentBlock() BlockScope { 275 return rs.Blocks[0] 276 } 277 278 func (rs *ReferenceScope) ClearCurrentBlock() { 279 rs.CurrentBlock().Clear() 280 } 281 282 func (rs *ReferenceScope) CloseCurrentBlock() { 283 PutBlockScope(rs.CurrentBlock()) 284 } 285 286 func (rs *ReferenceScope) CloseCurrentNode() { 287 PutNodeScope(rs.nodes[0]) 288 } 289 290 func (rs *ReferenceScope) NextRecord() bool { 291 rs.Records[0].recordIndex++ 292 293 if rs.Records[0].view.Len() <= rs.Records[0].recordIndex { 294 return false 295 } 296 return true 297 } 298 299 func (rs *ReferenceScope) FilePathExists(identifier string) bool { 300 if rs.cachedFilePath == nil { 301 return false 302 } 303 _, ok := rs.cachedFilePath[identifier] 304 return ok 305 } 306 307 func (rs *ReferenceScope) StoreFilePath(identifier string, fpath string) { 308 if rs.cachedFilePath != nil { 309 rs.cachedFilePath[identifier] = fpath 310 } 311 } 312 313 func (rs *ReferenceScope) LoadFilePath(identifier string) (string, bool) { 314 if rs.cachedFilePath != nil { 315 if p, ok := rs.cachedFilePath[identifier]; ok { 316 return p, true 317 } 318 } 319 return "", false 320 } 321 322 func (rs *ReferenceScope) Now() time.Time { 323 if rs.now.IsZero() { 324 return option.Now(rs.Tx.Flags.GetTimeLocation()) 325 } 326 return rs.now 327 } 328 329 func (rs *ReferenceScope) DeclareVariable(ctx context.Context, expr parser.VariableDeclaration) error { 330 return rs.Blocks[0].Variables.Declare(ctx, rs, expr) 331 } 332 333 func (rs *ReferenceScope) DeclareVariableDirectly(variable parser.Variable, val value.Primary) error { 334 return rs.Blocks[0].Variables.Add(variable, val) 335 } 336 337 func (rs *ReferenceScope) GetVariable(expr parser.Variable) (val value.Primary, err error) { 338 for i := range rs.Blocks { 339 if v, ok := rs.Blocks[i].Variables.Get(expr); ok { 340 return v, nil 341 } 342 } 343 return nil, NewUndeclaredVariableError(expr) 344 } 345 346 func (rs *ReferenceScope) SubstituteVariable(ctx context.Context, expr parser.VariableSubstitution) (val value.Primary, err error) { 347 val, err = Evaluate(ctx, rs, expr.Value) 348 if err != nil { 349 return 350 } 351 352 for i := range rs.Blocks { 353 if rs.Blocks[i].Variables.Set(expr.Variable, val) { 354 return 355 } 356 } 357 err = NewUndeclaredVariableError(expr.Variable) 358 return 359 } 360 361 func (rs *ReferenceScope) SubstituteVariableDirectly(variable parser.Variable, val value.Primary) (value.Primary, error) { 362 for i := range rs.Blocks { 363 if rs.Blocks[i].Variables.Set(variable, val) { 364 return val, nil 365 } 366 } 367 return nil, NewUndeclaredVariableError(variable) 368 } 369 370 func (rs *ReferenceScope) DisposeVariable(expr parser.Variable) error { 371 for i := range rs.Blocks { 372 if rs.Blocks[i].Variables.Dispose(expr) { 373 return nil 374 } 375 } 376 return NewUndeclaredVariableError(expr) 377 } 378 379 func (rs *ReferenceScope) AllVariables() VariableMap { 380 all := NewVariableMap() 381 for i := range rs.Blocks { 382 rs.Blocks[i].Variables.Range(func(key, val interface{}) bool { 383 if !all.Exists(key.(string)) { 384 all.Store(key.(string), val.(value.Primary)) 385 } 386 return true 387 }) 388 } 389 return all 390 } 391 392 func (rs *ReferenceScope) TemporaryTableExists(identifier string) bool { 393 identifier = strings.ToUpper(identifier) 394 for i := range rs.Blocks { 395 if rs.Blocks[i].TemporaryTables.Exists(identifier) { 396 return true 397 } 398 } 399 return false 400 } 401 402 func (rs *ReferenceScope) GetTemporaryTable(identifier parser.Identifier) (*View, error) { 403 fileIdentifier := strings.ToUpper(identifier.Literal) 404 for i := range rs.Blocks { 405 if view, err := rs.Blocks[i].TemporaryTables.Get(fileIdentifier); err == nil { 406 return view, nil 407 } 408 } 409 return nil, NewUndeclaredTemporaryTableError(identifier) 410 } 411 412 func (rs *ReferenceScope) GetTemporaryTableWithInternalId(ctx context.Context, identifier parser.Identifier, flags *option.Flags) (view *View, err error) { 413 fileIdentifier := strings.ToUpper(identifier.Literal) 414 for i := range rs.Blocks { 415 if view, err = rs.Blocks[i].TemporaryTables.GetWithInternalId(ctx, fileIdentifier, flags); err == nil { 416 return 417 } else if err != errTableNotLoaded { 418 return nil, err 419 } 420 } 421 return nil, NewUndeclaredTemporaryTableError(identifier) 422 } 423 424 func (rs *ReferenceScope) SetTemporaryTable(view *View) { 425 rs.Blocks[0].TemporaryTables.Set(view) 426 } 427 428 func (rs *ReferenceScope) ReplaceTemporaryTable(view *View) { 429 for i := range rs.Blocks { 430 if rs.Blocks[i].TemporaryTables.Exists(view.FileInfo.IdentifiedPath()) { 431 rs.Blocks[i].TemporaryTables.Set(view) 432 return 433 } 434 } 435 } 436 437 func (rs *ReferenceScope) DisposeTemporaryTable(name parser.QueryExpression) error { 438 for i := range rs.Blocks { 439 if rs.Blocks[i].TemporaryTables.DisposeTemporaryTable(name) { 440 return nil 441 } 442 } 443 return NewUndeclaredTemporaryTableError(name) 444 } 445 446 func (rs *ReferenceScope) StoreTemporaryTable(session *Session, uncomittedViews map[string]*FileInfo) []string { 447 msglist := make([]string, 0, len(uncomittedViews)) 448 for i := range rs.Blocks { 449 rs.Blocks[i].TemporaryTables.Range(func(key, value interface{}) bool { 450 if _, ok := uncomittedViews[key.(string)]; ok { 451 view := value.(*View) 452 453 if view.FileInfo.IsStdin() { 454 session.updateStdinView(view.Copy()) 455 msglist = append(msglist, fmt.Sprintf("Commit: restore point of view %q is created.", view.FileInfo.Path)) 456 } else if view.FileInfo.IsTemporaryTable() { 457 view.CreateRestorePoint() 458 msglist = append(msglist, fmt.Sprintf("Commit: restore point of view %q is created.", view.FileInfo.Path)) 459 } 460 } 461 return true 462 }) 463 } 464 return msglist 465 } 466 467 func (rs *ReferenceScope) RestoreTemporaryTable(uncomittedViews map[string]*FileInfo) []string { 468 msglist := make([]string, 0, len(uncomittedViews)) 469 for i := range rs.Blocks { 470 rs.Blocks[i].TemporaryTables.Range(func(key, value interface{}) bool { 471 if _, ok := uncomittedViews[key.(string)]; ok { 472 view := value.(*View) 473 474 if view.FileInfo.IsStdin() { 475 rs.Blocks[i].TemporaryTables.Delete(view.FileInfo.IdentifiedPath()) 476 msglist = append(msglist, fmt.Sprintf("Rollback: view %q is restored.", view.FileInfo.Path)) 477 } else if view.FileInfo.IsTemporaryTable() { 478 view.Restore() 479 msglist = append(msglist, fmt.Sprintf("Rollback: view %q is restored.", view.FileInfo.Path)) 480 } 481 } 482 return true 483 }) 484 } 485 return msglist 486 } 487 488 func (rs *ReferenceScope) AllTemporaryTables() ViewMap { 489 all := NewViewMap() 490 491 for i := range rs.Blocks { 492 rs.Blocks[i].TemporaryTables.Range(func(key, value interface{}) bool { 493 if value.(*View).FileInfo.IsInMemoryTable() { 494 k := key.(string) 495 if !all.Exists(k) { 496 all.Store(k, value.(*View)) 497 } 498 } 499 return true 500 }) 501 } 502 return all 503 } 504 505 func (rs *ReferenceScope) DeclareCursor(expr parser.CursorDeclaration) error { 506 return rs.Blocks[0].Cursors.Declare(expr) 507 } 508 509 func (rs *ReferenceScope) AddPseudoCursor(name parser.Identifier, values []value.Primary) error { 510 return rs.Blocks[0].Cursors.AddPseudoCursor(name, values) 511 } 512 513 func (rs *ReferenceScope) DisposeCursor(name parser.Identifier) error { 514 for i := range rs.Blocks { 515 err := rs.Blocks[i].Cursors.Dispose(name) 516 if err == nil { 517 return nil 518 } 519 if err == errPseudoCursor { 520 return NewPseudoCursorError(name) 521 } 522 } 523 return NewUndeclaredCursorError(name) 524 } 525 526 func (rs *ReferenceScope) OpenCursor(ctx context.Context, name parser.Identifier, values []parser.ReplaceValue) error { 527 var err error 528 for i := range rs.Blocks { 529 err = rs.Blocks[i].Cursors.Open(ctx, rs, name, values) 530 if err == nil { 531 return nil 532 } 533 if err != errUndeclaredCursor { 534 return err 535 } 536 } 537 return NewUndeclaredCursorError(name) 538 } 539 540 func (rs *ReferenceScope) CloseCursor(name parser.Identifier) error { 541 for i := range rs.Blocks { 542 err := rs.Blocks[i].Cursors.Close(name) 543 if err == nil { 544 return nil 545 } 546 if err != errUndeclaredCursor { 547 return err 548 } 549 } 550 return NewUndeclaredCursorError(name) 551 } 552 553 func (rs *ReferenceScope) FetchCursor(name parser.Identifier, position int, number int) ([]value.Primary, error) { 554 var values []value.Primary 555 var err error 556 557 for i := range rs.Blocks { 558 values, err = rs.Blocks[i].Cursors.Fetch(name, position, number) 559 if err == nil { 560 return values, nil 561 } 562 if err != errUndeclaredCursor { 563 return nil, err 564 } 565 } 566 return nil, NewUndeclaredCursorError(name) 567 } 568 569 func (rs *ReferenceScope) CursorIsOpen(name parser.Identifier) (ternary.Value, error) { 570 for i := range rs.Blocks { 571 if ok, err := rs.Blocks[i].Cursors.IsOpen(name); err == nil { 572 return ok, nil 573 } 574 } 575 return ternary.FALSE, NewUndeclaredCursorError(name) 576 } 577 578 func (rs *ReferenceScope) CursorIsInRange(name parser.Identifier) (ternary.Value, error) { 579 var result ternary.Value 580 var err error 581 582 for i := range rs.Blocks { 583 result, err = rs.Blocks[i].Cursors.IsInRange(name) 584 if err == nil { 585 return result, nil 586 } 587 if err != errUndeclaredCursor { 588 return result, err 589 } 590 } 591 return ternary.FALSE, NewUndeclaredCursorError(name) 592 } 593 594 func (rs *ReferenceScope) CursorCount(name parser.Identifier) (int, error) { 595 var count int 596 var err error 597 598 for i := range rs.Blocks { 599 count, err = rs.Blocks[i].Cursors.Count(name) 600 if err == nil { 601 return count, nil 602 } 603 if err != errUndeclaredCursor { 604 return 0, err 605 } 606 } 607 return 0, NewUndeclaredCursorError(name) 608 } 609 610 func (rs *ReferenceScope) AllCursors() CursorMap { 611 all := NewCursorMap() 612 for i := range rs.Blocks { 613 rs.Blocks[i].Cursors.Range(func(key, val interface{}) bool { 614 cur := val.(*Cursor) 615 if !cur.isPseudo { 616 if !all.Exists(key.(string)) { 617 all.Store(key.(string), cur) 618 } 619 } 620 return true 621 }) 622 } 623 return all 624 } 625 626 func (rs *ReferenceScope) DeclareFunction(expr parser.FunctionDeclaration) error { 627 return rs.Blocks[0].Functions.Declare(expr) 628 } 629 630 func (rs *ReferenceScope) DeclareAggregateFunction(expr parser.AggregateDeclaration) error { 631 return rs.Blocks[0].Functions.DeclareAggregate(expr) 632 } 633 634 func (rs *ReferenceScope) GetFunction(expr parser.QueryExpression, name string) (*UserDefinedFunction, error) { 635 for i := range rs.Blocks { 636 if fn, ok := rs.Blocks[i].Functions.Get(name); ok { 637 return fn, nil 638 } 639 } 640 return nil, NewFunctionNotExistError(expr, name) 641 } 642 643 func (rs *ReferenceScope) DisposeFunction(name parser.Identifier) error { 644 for i := range rs.Blocks { 645 if rs.Blocks[i].Functions.Dispose(name) { 646 return nil 647 } 648 } 649 return NewFunctionNotExistError(name, name.Literal) 650 } 651 652 func (rs *ReferenceScope) AllFunctions() (UserDefinedFunctionMap, UserDefinedFunctionMap) { 653 scalarAll := NewUserDefinedFunctionMap() 654 aggregateAll := NewUserDefinedFunctionMap() 655 656 for i := range rs.Blocks { 657 rs.Blocks[i].Functions.Range(func(key, val interface{}) bool { 658 fn := val.(*UserDefinedFunction) 659 if fn.IsAggregate { 660 if !aggregateAll.Exists(key.(string)) { 661 aggregateAll.Store(key.(string), fn) 662 } 663 } else { 664 if !scalarAll.Exists(key.(string)) { 665 scalarAll.Store(key.(string), fn) 666 } 667 } 668 return true 669 }) 670 } 671 672 return scalarAll, aggregateAll 673 } 674 675 func (rs *ReferenceScope) SetInlineTable(ctx context.Context, inlineTable parser.InlineTable) error { 676 return rs.nodes[0].inlineTables.Set(ctx, rs, inlineTable) 677 } 678 679 func (rs *ReferenceScope) GetInlineTable(name parser.Identifier) (*View, error) { 680 for i := range rs.nodes { 681 if view, err := rs.nodes[i].inlineTables.Get(name); err == nil { 682 return view, nil 683 } 684 } 685 return nil, NewUndefinedInLineTableError(name) 686 } 687 688 func (rs *ReferenceScope) StoreInlineTable(name parser.Identifier, view *View) error { 689 return rs.nodes[0].inlineTables.Store(name, view) 690 } 691 692 func (rs *ReferenceScope) InlineTableExists(name parser.Identifier) bool { 693 for i := range rs.nodes { 694 if rs.nodes[i].inlineTables.Exists(name) { 695 return true 696 } 697 } 698 return false 699 } 700 701 func (rs *ReferenceScope) LoadInlineTable(ctx context.Context, clause parser.WithClause) error { 702 for _, v := range clause.InlineTables { 703 inlineTable := v.(parser.InlineTable) 704 err := rs.SetInlineTable(ctx, inlineTable) 705 if err != nil { 706 return err 707 } 708 } 709 710 return nil 711 } 712 713 func (rs *ReferenceScope) AddAlias(alias parser.Identifier, path string) error { 714 return rs.nodes[0].aliases.Add(alias, path) 715 } 716 717 func (rs *ReferenceScope) GetAlias(alias parser.Identifier) (path string, err error) { 718 for i := range rs.nodes { 719 if path, err = rs.nodes[i].aliases.Get(alias); err == nil { 720 return 721 } 722 } 723 err = NewTableNotLoadedError(alias) 724 return 725 }