github.com/mithrandie/csvq@v1.18.1/lib/query/load_view.go (about) 1 package query 2 3 import ( 4 "bytes" 5 "context" 6 gojson "encoding/json" 7 "fmt" 8 "io" 9 "net/http" 10 "os" 11 "strconv" 12 "strings" 13 "sync" 14 15 "github.com/mithrandie/csvq/lib/file" 16 "github.com/mithrandie/csvq/lib/json" 17 "github.com/mithrandie/csvq/lib/option" 18 "github.com/mithrandie/csvq/lib/parser" 19 "github.com/mithrandie/csvq/lib/value" 20 21 "github.com/mithrandie/go-text" 22 "github.com/mithrandie/go-text/csv" 23 "github.com/mithrandie/go-text/fixedlen" 24 txjson "github.com/mithrandie/go-text/json" 25 "github.com/mithrandie/go-text/jsonl" 26 "github.com/mithrandie/go-text/ltsv" 27 ) 28 29 const fileLoadingPreparedRecordSetCap = 300 30 const fileLoadingBuffer = 300 31 32 const inlineTablePrefix = "@__io__" 33 34 type RecordReader interface { 35 Read() ([]text.RawText, error) 36 } 37 38 func isTableObjectAsDataObject(tablePath parser.QueryExpression) bool { 39 _, ok := tablePath.(parser.Identifier) 40 return !ok 41 } 42 43 func isTableObjectAsURL(tablePath parser.QueryExpression) bool { 44 i, ok := tablePath.(parser.Identifier) 45 if !ok { 46 return false 47 } 48 49 return strings.HasPrefix(i.Literal, "http://") || strings.HasPrefix(i.Literal, "https://") 50 } 51 52 func LoadView(ctx context.Context, scope *ReferenceScope, tables []parser.QueryExpression, forUpdate bool, useInternalId bool) (*View, error) { 53 if tables == nil { 54 var obj parser.QueryExpression 55 if scope.Tx.Session.CanReadStdin { 56 obj = parser.Stdin{} 57 } else { 58 obj = parser.Dual{} 59 } 60 tables = []parser.QueryExpression{parser.Table{Object: obj}} 61 } 62 63 table := tables[0] 64 65 for i := 1; i < len(tables); i++ { 66 table = parser.Table{ 67 Object: parser.Join{ 68 Table: table, 69 JoinTable: tables[i], 70 JoinType: parser.Token{Token: parser.CROSS}, 71 }, 72 } 73 } 74 75 view, err := loadView(ctx, scope, table, forUpdate, useInternalId) 76 return view, err 77 } 78 79 func LoadViewFromTableIdentifier(ctx context.Context, scope *ReferenceScope, table parser.QueryExpression, forUpdate bool, useInternalId bool) (*View, error) { 80 tables := []parser.QueryExpression{ 81 parser.Table{Object: table}, 82 } 83 84 return LoadView(ctx, scope, tables, forUpdate, useInternalId) 85 } 86 87 func loadView(ctx context.Context, scope *ReferenceScope, tableExpr parser.QueryExpression, forUpdate bool, useInternalId bool) (view *View, err error) { 88 if parentheses, ok := tableExpr.(parser.Parentheses); ok { 89 return loadView(ctx, scope, parentheses.Expr, forUpdate, useInternalId) 90 } 91 92 table := tableExpr.(parser.Table) 93 tableName, err := ParseTableName(ctx, scope, table) 94 if err != nil { 95 return nil, err 96 } 97 98 switch table.Object.(type) { 99 case parser.Dual: 100 view = NewDualView() 101 case parser.FormatSpecifiedFunction: 102 formatSpecifiedFunction := table.Object.(parser.FormatSpecifiedFunction) 103 tablePath := formatSpecifiedFunction.Path 104 options := scope.Tx.Flags.ImportOptions.Copy() 105 106 var felem value.Primary 107 if formatSpecifiedFunction.FormatElement != nil { 108 p, err := Evaluate(ctx, scope, formatSpecifiedFunction.FormatElement) 109 if err != nil { 110 return nil, err 111 } 112 felem = value.ToString(p) 113 } 114 115 isInlineObject := false 116 switch formatSpecifiedFunction.Type.Token { 117 case parser.CSV_INLINE, parser.JSON_INLINE, parser.JSON_TABLE: 118 isInlineObject = true 119 120 if isTableObjectAsDataObject(formatSpecifiedFunction.Path) { 121 p, err := Evaluate(ctx, scope, formatSpecifiedFunction.Path) 122 if err != nil { 123 return nil, err 124 } 125 d := value.ToString(p) 126 if value.IsNull(d) { 127 return nil, NewEmptyInlineTableError(formatSpecifiedFunction) 128 } 129 tablePath = DataObject{BaseExpr: formatSpecifiedFunction.GetBaseExpr(), Raw: d.(*value.String).Raw()} 130 } else if isTableObjectAsURL(formatSpecifiedFunction.Path) { 131 tablePath = parser.Url{BaseExpr: formatSpecifiedFunction.GetBaseExpr(), Raw: formatSpecifiedFunction.Path.(parser.Identifier).Literal} 132 } 133 134 forUpdate = false 135 136 switch formatSpecifiedFunction.Type.Token { 137 case parser.CSV_INLINE: 138 formatSpecifiedFunction.Type.Token = parser.CSV 139 default: // JSON_INLINE, JSON_TABLE 140 formatSpecifiedFunction.Type.Token = parser.JSON 141 } 142 } 143 144 encodingIdx := 0 145 noHeaderIdx := 1 146 withoutNullIdx := 2 147 148 switch formatSpecifiedFunction.Type.Token { 149 case parser.CSV: 150 if felem == nil { 151 return nil, NewTableObjectInvalidArgumentError(formatSpecifiedFunction, "delimiter is not specified") 152 } 153 if value.IsNull(felem) { 154 return nil, NewTableObjectInvalidDelimiterError(formatSpecifiedFunction, formatSpecifiedFunction.FormatElement.String()) 155 } 156 s := felem.(*value.String).Raw() 157 d := []rune(s) 158 if 1 != len(d) { 159 return nil, NewTableObjectInvalidDelimiterError(formatSpecifiedFunction, formatSpecifiedFunction.FormatElement.String()) 160 } 161 if 3 < len(formatSpecifiedFunction.Args) { 162 return nil, NewTableObjectArgumentsLengthError(formatSpecifiedFunction, 5) 163 } 164 options.Delimiter = d[0] 165 if options.Delimiter == '\t' { 166 options.Format = option.TSV 167 } else { 168 options.Format = option.CSV 169 } 170 case parser.FIXED: 171 if felem == nil { 172 return nil, NewTableObjectInvalidArgumentError(formatSpecifiedFunction, "delimiter positions are not specified") 173 } 174 if value.IsNull(felem) { 175 return nil, NewTableObjectInvalidDelimiterPositionsError(formatSpecifiedFunction, formatSpecifiedFunction.FormatElement.String()) 176 } 177 s := felem.(*value.String).Raw() 178 179 var positions []int 180 if !strings.EqualFold("SPACES", s) { 181 if strings.HasPrefix(s, "s[") || strings.HasPrefix(s, "S[") { 182 options.SingleLine = true 183 s = s[1:] 184 } 185 err = gojson.Unmarshal([]byte(s), &positions) 186 if err != nil { 187 return nil, NewTableObjectInvalidDelimiterPositionsError(formatSpecifiedFunction, formatSpecifiedFunction.FormatElement.String()) 188 } 189 } 190 if 3 < len(formatSpecifiedFunction.Args) { 191 return nil, NewTableObjectArgumentsLengthError(formatSpecifiedFunction, 5) 192 } 193 options.DelimiterPositions = positions 194 options.Format = option.FIXED 195 case parser.JSON, parser.JSONL: 196 if felem == nil { 197 return nil, NewTableObjectInvalidArgumentError(formatSpecifiedFunction, "json query is not specified") 198 } 199 if value.IsNull(felem) { 200 return nil, NewTableObjectInvalidJsonQueryError(formatSpecifiedFunction, formatSpecifiedFunction.FormatElement.String()) 201 } 202 if 0 < len(formatSpecifiedFunction.Args) { 203 return nil, NewTableObjectJsonArgumentsLengthError(formatSpecifiedFunction, 2) 204 } 205 206 options.JsonQuery = felem.(*value.String).Raw() 207 options.Encoding = text.UTF8 208 if formatSpecifiedFunction.Type.Token == parser.JSONL { 209 options.Format = option.JSONL 210 } else { 211 options.Format = option.JSON 212 } 213 case parser.LTSV: 214 if 2 < len(formatSpecifiedFunction.Args) { 215 return nil, NewTableObjectJsonArgumentsLengthError(formatSpecifiedFunction, 3) 216 } 217 options.Format = option.LTSV 218 withoutNullIdx, noHeaderIdx = noHeaderIdx, withoutNullIdx 219 default: 220 return nil, NewInvalidTableObjectError(formatSpecifiedFunction, formatSpecifiedFunction.Type.Literal) 221 } 222 223 args := make([]value.Primary, 3) 224 defer func() { 225 for i := range args { 226 if args[i] != nil { 227 value.Discard(args[i]) 228 } 229 } 230 }() 231 232 for i, a := range formatSpecifiedFunction.Args { 233 if pt, ok := a.(parser.PrimitiveType); ok && value.IsNull(pt.Value) { 234 continue 235 } 236 237 var p value.Primary = value.NewNull() 238 if fr, ok := a.(parser.FieldReference); ok { 239 if col, ok := fr.Column.(parser.Identifier); ok { 240 a = parser.NewStringValue(col.Literal) 241 } else { 242 return nil, NewTableObjectInvalidArgumentError(formatSpecifiedFunction, fmt.Sprintf("cannot be converted as an argument: %s", formatSpecifiedFunction.Args[encodingIdx].String())) 243 } 244 } 245 if pv, err := Evaluate(ctx, scope, a); err == nil { 246 p = pv 247 } 248 249 switch i { 250 case encodingIdx: 251 v := value.ToString(p) 252 if !value.IsNull(v) { 253 args[i] = v 254 } else { 255 return nil, NewTableObjectInvalidArgumentError(formatSpecifiedFunction, fmt.Sprintf("cannot be converted as a encoding value: %s", formatSpecifiedFunction.Args[encodingIdx].String())) 256 } 257 case noHeaderIdx: 258 v := value.ToBoolean(p) 259 if !value.IsNull(v) { 260 args[i] = v 261 } else { 262 return nil, NewTableObjectInvalidArgumentError(formatSpecifiedFunction, fmt.Sprintf("cannot be converted as a no-header value: %s", formatSpecifiedFunction.Args[noHeaderIdx].String())) 263 } 264 case withoutNullIdx: 265 v := value.ToBoolean(p) 266 if !value.IsNull(v) { 267 args[i] = v 268 } else { 269 return nil, NewTableObjectInvalidArgumentError(formatSpecifiedFunction, fmt.Sprintf("cannot be converted as a without-null value: %s", formatSpecifiedFunction.Args[withoutNullIdx].String())) 270 } 271 } 272 } 273 274 if args[encodingIdx] != nil { 275 if options.Encoding, err = option.ParseEncoding(args[0].(*value.String).Raw()); err != nil { 276 return nil, NewTableObjectInvalidArgumentError(formatSpecifiedFunction, err.Error()) 277 } 278 } 279 if args[noHeaderIdx] != nil { 280 options.NoHeader = args[noHeaderIdx].(*value.Boolean).Raw() 281 } 282 if args[withoutNullIdx] != nil { 283 options.WithoutNull = args[withoutNullIdx].(*value.Boolean).Raw() 284 } 285 286 view, err = loadObject( 287 ctx, 288 scope, 289 tablePath, 290 tableName, 291 forUpdate, 292 useInternalId, 293 isInlineObject, 294 options, 295 ) 296 if err != nil { 297 return nil, err 298 } 299 300 case parser.Identifier, parser.Url, parser.TableFunction, parser.Stdin: 301 options := scope.Tx.Flags.ImportOptions.Copy() 302 options.Format = option.AutoSelect 303 304 view, err = loadObject( 305 ctx, 306 scope, 307 table.Object, 308 tableName, 309 forUpdate, 310 useInternalId, 311 false, 312 options, 313 ) 314 if err != nil { 315 return nil, err 316 } 317 318 case parser.Join: 319 join := table.Object.(parser.Join) 320 view, err = loadView(ctx, scope, join.Table, forUpdate, useInternalId) 321 if err != nil { 322 return nil, err 323 } 324 325 if t, ok := join.JoinTable.(parser.Table); ok && !t.Lateral.IsEmpty() { 326 switch join.Direction.Token { 327 case parser.RIGHT, parser.FULL: 328 return nil, NewIncorrectLateralUsageError(t) 329 } 330 331 joinTableName, err := ParseTableName(ctx, scope, t) 332 if err != nil { 333 return nil, err 334 } 335 336 subquery := t.Object.(parser.Subquery) 337 var hfields Header 338 resultSetList := make([]RecordSet, view.RecordLen()) 339 340 if err := EvaluateSequentially(ctx, scope, view, func(seqScope *ReferenceScope, rIdx int) error { 341 appliedView, err := Select(ctx, seqScope, subquery.Query) 342 if err != nil { 343 return err 344 } 345 346 if 0 < len(joinTableName.Literal) { 347 if err = appliedView.Header.Update(joinTableName.Literal, nil); err != nil { 348 return err 349 } 350 } 351 352 calcView := NewView() 353 calcView.Header = view.Header.Copy() 354 calcView.RecordSet = RecordSet{view.RecordSet[rIdx].Copy()} 355 if err = joinViews(ctx, scope, calcView, appliedView, join); err != nil { 356 return err 357 } 358 359 if rIdx == 0 { 360 hfields = calcView.Header 361 } 362 resultSetList[rIdx] = calcView.RecordSet 363 return nil 364 }); err != nil { 365 return nil, err 366 } 367 368 resultSet := make(RecordSet, 0, view.RecordLen()) 369 for i := range resultSetList { 370 resultSet = append(resultSet, resultSetList[i]...) 371 } 372 373 view.Header = hfields 374 view.RecordSet = resultSet 375 view.FileInfo = nil 376 377 } else { 378 joinView, err := loadView(ctx, scope, join.JoinTable, forUpdate, useInternalId) 379 if err != nil { 380 return nil, err 381 } 382 383 if err = joinViews(ctx, scope, view, joinView, join); err != nil { 384 return nil, err 385 } 386 } 387 388 case parser.Subquery: 389 subquery := table.Object.(parser.Subquery) 390 view, err = Select(ctx, scope, subquery.Query) 391 if err != nil { 392 return nil, err 393 } 394 395 if 0 < len(tableName.Literal) { 396 if err := scope.AddAlias(tableName, ""); err != nil { 397 return nil, err 398 } 399 400 if err = view.Header.Update(tableName.Literal, nil); err != nil { 401 return nil, err 402 } 403 } 404 405 if view.FileInfo != nil { 406 view.FileInfo.ViewType = ViewTypeInlineTable 407 } 408 } 409 410 if view.FileInfo != nil && !(view.FileInfo.IsUpdatable() || view.FileInfo.IsRemoteObject()) { 411 view.FileInfo.Path = "" 412 } 413 414 return view, err 415 } 416 417 func joinViews(ctx context.Context, scope *ReferenceScope, view *View, joinView *View, join parser.Join) error { 418 condition, includeFields, excludeFields, err := ParseJoinCondition(join, view, joinView) 419 if err != nil { 420 return err 421 } 422 423 joinType := join.JoinType.Token 424 if join.JoinType.IsEmpty() { 425 if join.Direction.IsEmpty() { 426 joinType = parser.INNER 427 } else { 428 joinType = parser.OUTER 429 } 430 } 431 432 switch joinType { 433 case parser.CROSS: 434 if err = CrossJoin(ctx, scope, view, joinView); err != nil { 435 return err 436 } 437 case parser.INNER: 438 if err = InnerJoin(ctx, scope, view, joinView, condition); err != nil { 439 return err 440 } 441 case parser.OUTER: 442 if err = OuterJoin(ctx, scope, view, joinView, condition, join.Direction.Token); err != nil { 443 return err 444 } 445 } 446 447 if includeFields != nil { 448 includeIndices := NewUintPool(len(includeFields), LimitToUseUintSlicePool) 449 excludeIndices := NewUintPool(view.FieldLen()-len(includeFields), LimitToUseUintSlicePool) 450 alternatives := make(map[int]int) 451 452 for i := range includeFields { 453 idx, _ := view.Header.SearchIndex(includeFields[i]) 454 includeIndices.Add(uint(idx)) 455 456 eidx, _ := view.Header.SearchIndex(excludeFields[i]) 457 excludeIndices.Add(uint(eidx)) 458 459 alternatives[idx] = eidx 460 } 461 462 fieldIndices := make([]int, 0, view.FieldLen()-excludeIndices.Len()) 463 header := make(Header, 0, view.FieldLen()-excludeIndices.Len()) 464 _ = includeIndices.Range(func(_ int, fidx uint) error { 465 view.Header[fidx].View = "" 466 view.Header[fidx].Number = 0 467 view.Header[fidx].IsJoinColumn = true 468 header = append(header, view.Header[fidx]) 469 fieldIndices = append(fieldIndices, int(fidx)) 470 return nil 471 }) 472 for i := range view.Header { 473 if excludeIndices.Exists(uint(i)) || includeIndices.Exists(uint(i)) { 474 continue 475 } 476 header = append(header, view.Header[i]) 477 fieldIndices = append(fieldIndices, i) 478 } 479 view.Header = header 480 fieldLen := len(fieldIndices) 481 482 if err = NewGoroutineTaskManager(view.RecordLen(), -1, scope.Tx.Flags.CPU).Run(ctx, func(index int) error { 483 record := make(Record, fieldLen) 484 for i, idx := range fieldIndices { 485 if includeIndices.Exists(uint(idx)) && value.IsNull(view.RecordSet[index][idx][0]) { 486 record[i] = view.RecordSet[index][alternatives[idx]] 487 } else { 488 record[i] = view.RecordSet[index][idx] 489 } 490 } 491 view.RecordSet[index] = record 492 return nil 493 }); err != nil { 494 return err 495 } 496 } 497 498 return nil 499 } 500 501 func loadObjectFromStdin( 502 ctx context.Context, 503 scope *ReferenceScope, 504 stdin parser.Stdin, 505 tableName parser.Identifier, 506 forUpdate bool, 507 useInternalId bool, 508 options option.ImportOptions, 509 ) (*View, error) { 510 if options.Format == option.AutoSelect { 511 options.Format = scope.Tx.Flags.ImportOptions.Format 512 } 513 514 fileInfo := NewStdinFileInfo(stdin.String(), options, scope.Tx.Flags.ExportOptions) 515 516 scope.Tx.viewLoadingMutex.Lock() 517 defer scope.Tx.viewLoadingMutex.Unlock() 518 519 var err error 520 view, ok := scope.Global().TemporaryTables.Load(stdin.String()) 521 if !ok || (forUpdate && !view.FileInfo.ForUpdate) { 522 if forUpdate { 523 if err = scope.Tx.LockStdinContext(ctx); err != nil { 524 return nil, err 525 } 526 } else { 527 if err = scope.Tx.RLockStdinContext(ctx); err != nil { 528 return nil, err 529 } 530 defer scope.Tx.RUnlockStdin() 531 } 532 view, err = scope.Tx.Session.GetStdinView(ctx, scope.Tx.Flags, fileInfo, stdin) 533 if err != nil { 534 return nil, err 535 } 536 scope.Global().TemporaryTables.Set(view) 537 } 538 539 if useInternalId { 540 if view, err = scope.Global().TemporaryTables.GetWithInternalId(ctx, stdin.String(), scope.Tx.Flags); err != nil { 541 if err == errTableNotLoaded { 542 err = NewUndeclaredTemporaryTableError(parser.Identifier{Literal: stdin.String()}) 543 } 544 return nil, err 545 } 546 } else { 547 if view, err = scope.Global().TemporaryTables.Get(stdin.String()); err != nil { 548 if err == errTableNotLoaded { 549 err = NewUndeclaredTemporaryTableError(parser.Identifier{Literal: stdin.String()}) 550 } 551 return nil, err 552 } 553 } 554 555 if err = scope.AddAlias(tableName, view.FileInfo.Path); err != nil { 556 return nil, err 557 } 558 559 if !strings.EqualFold(stdin.String(), tableName.Literal) { 560 if err = view.Header.Update(tableName.Literal, nil); err != nil { 561 return nil, err 562 } 563 } 564 565 return view, nil 566 } 567 568 func loadObjectFromString( 569 ctx context.Context, 570 scope *ReferenceScope, 571 data string, 572 tablePath parser.QueryExpression, 573 tableName parser.Identifier, 574 options option.ImportOptions, 575 ) (*View, error) { 576 fileInfo := NewInlineFileInfo(inlineTablePrefix+file.RandomString(12), options, scope.Tx.Flags.ExportOptions) 577 578 r := strings.NewReader(data) 579 view, err := loadViewFromFile(ctx, scope.Tx.Flags, r, fileInfo, options, tablePath) 580 if err != nil { 581 if _, ok := err.(Error); !ok { 582 err = NewDataParsingError(tablePath, tablePath.String(), err.Error()) 583 } 584 return nil, err 585 } 586 587 if 0 < len(tableName.Literal) { 588 if err = scope.AddAlias(tableName, ""); err != nil { 589 return nil, err 590 } 591 if err = view.Header.Update(tableName.Literal, nil); err != nil { 592 return nil, err 593 } 594 } 595 596 return view, nil 597 } 598 599 func loadDataObject( 600 ctx context.Context, 601 scope *ReferenceScope, 602 dataObject DataObject, 603 tablePath parser.QueryExpression, 604 tableName parser.Identifier, 605 options option.ImportOptions, 606 ) (*View, error) { 607 if options.Format == option.AutoSelect { 608 options.Format = scope.Tx.Flags.ImportOptions.Format 609 } 610 611 view, err := loadObjectFromString(ctx, scope, dataObject.Raw, tablePath, tableName, options) 612 if err == nil { 613 view.FileInfo.ViewType = ViewTypeStringObject 614 } 615 return view, err 616 } 617 618 func loadHttpObject( 619 ctx context.Context, 620 scope *ReferenceScope, 621 httpObject HttpObject, 622 tablePath parser.QueryExpression, 623 tableName parser.Identifier, 624 options option.ImportOptions, 625 ) (*View, error) { 626 scope.Tx.viewLoadingMutex.Lock() 627 628 urlResource, ok := scope.Tx.UrlCache[httpObject.URL] 629 if !ok { 630 res, err := http.Get(httpObject.URL) 631 if err != nil { 632 scope.Tx.viewLoadingMutex.Unlock() 633 return nil, NewHttpRequestError(tablePath, httpObject.URL, err.Error()) 634 } 635 if 400 <= res.StatusCode { 636 scope.Tx.viewLoadingMutex.Unlock() 637 return nil, NewHttpRequestError(tablePath, httpObject.URL, fmt.Sprintf("code %d, status %q", res.StatusCode, res.Status)) 638 } 639 640 urlResource, err = NewUrlResource(res) 641 if err != nil { 642 scope.Tx.viewLoadingMutex.Unlock() 643 return nil, err 644 } 645 scope.Tx.UrlCache[httpObject.URL] = urlResource 646 } 647 648 data := string(urlResource.Data) 649 scope.Tx.viewLoadingMutex.Unlock() 650 651 if options.Format == option.AutoSelect { 652 switch urlResource.MimeType { 653 case "text/csv": 654 options.Format = option.CSV 655 case "application/json": 656 options.Format = option.JSON 657 default: 658 options.Format = scope.Tx.Flags.ImportOptions.Format 659 } 660 } 661 662 view, err := loadObjectFromString(ctx, scope, data, tablePath, tableName, options) 663 if err == nil { 664 view.FileInfo.Path = httpObject.URL 665 view.FileInfo.ViewType = ViewTypeRemoteObject 666 } 667 return view, err 668 } 669 670 func loadInlineObjectFromFile( 671 ctx context.Context, 672 scope *ReferenceScope, 673 tableIdentifier parser.Identifier, 674 tableName parser.Identifier, 675 options option.ImportOptions, 676 ) (view *View, err error) { 677 scope.Tx.viewLoadingMutex.Lock() 678 defer scope.Tx.viewLoadingMutex.Unlock() 679 680 fileInfo, err := NewFileInfo(tableIdentifier, scope.Tx.Flags.Repository, options, scope.Tx.Flags.ImportOptions.Format) 681 if err != nil { 682 return 683 } 684 fileInfo.ViewType = ViewTypeInlineTable 685 fileInfo.SetDefaultFileInfoAttributes(options, scope.Tx.Flags.ExportOptions) 686 687 var fp *os.File 688 689 cachedView, cacheExists := scope.Tx.CachedViews.Load(fileInfo.IdentifiedPath()) 690 691 if cacheExists { 692 fp = cachedView.FileInfo.Handler.File() 693 } else { 694 h, e := scope.Tx.FileContainer.CreateHandlerForRead(ctx, fileInfo.Path, scope.Tx.WaitTimeout, scope.Tx.RetryDelay) 695 if e != nil { 696 tableIdentifier.Literal = fileInfo.Path 697 err = ConvertFileHandlerError(e, tableIdentifier) 698 return 699 } 700 defer func() { 701 err = appendCompositeError(err, scope.Tx.FileContainer.Close(h)) 702 }() 703 fp = h.File() 704 } 705 _, err = fp.Seek(0, io.SeekStart) 706 if err != nil { 707 return nil, NewIOError(tableIdentifier, err.Error()) 708 } 709 710 view, err = loadViewFromFile(ctx, scope.Tx.Flags, fp, fileInfo, options, tableIdentifier) 711 if err != nil { 712 if _, ok := err.(Error); !ok { 713 err = NewDataParsingError(tableIdentifier, fileInfo.Path, err.Error()) 714 } 715 return 716 } 717 718 if 0 < len(tableName.Literal) { 719 if err = scope.AddAlias(tableName, ""); err != nil { 720 return 721 } 722 if err = view.Header.Update(tableName.Literal, nil); err != nil { 723 return 724 } 725 } 726 727 if !scope.FilePathExists(tableIdentifier.Literal) { 728 scope.StoreFilePath(tableIdentifier.Literal, view.FileInfo.Path) 729 } 730 731 return 732 } 733 734 func loadObjectFromFile( 735 ctx context.Context, 736 scope *ReferenceScope, 737 fileIdentifier parser.Identifier, 738 tableName parser.Identifier, 739 forUpdate bool, 740 useInternalId bool, 741 options option.ImportOptions, 742 ) (view *View, err error) { 743 filePath, err := cacheViewFromFile( 744 ctx, 745 scope, 746 fileIdentifier, 747 forUpdate, 748 options, 749 ) 750 if err != nil { 751 return 752 } 753 754 if useInternalId { 755 if view, err = scope.Tx.CachedViews.GetWithInternalId(ctx, strings.ToUpper(filePath), scope.Tx.Flags); err != nil { 756 if err == errTableNotLoaded { 757 err = NewTableNotLoadedError(parser.Identifier{BaseExpr: fileIdentifier.GetBaseExpr(), Literal: filePath}) 758 } 759 return 760 } 761 } else { 762 if view, err = scope.Tx.CachedViews.Get(strings.ToUpper(filePath)); err != nil { 763 err = NewTableNotLoadedError(parser.Identifier{BaseExpr: fileIdentifier.GetBaseExpr(), Literal: filePath}) 764 return 765 } 766 } 767 768 if 0 < len(tableName.Literal) { 769 if err = scope.AddAlias(tableName, filePath); err != nil { 770 return 771 } 772 } 773 774 if !strings.EqualFold(FormatTableName(filePath), tableName.Literal) { 775 if err = view.Header.Update(tableName.Literal, nil); err != nil { 776 return 777 } 778 } 779 780 return 781 } 782 783 func loadObject( 784 ctx context.Context, 785 scope *ReferenceScope, 786 tablePath parser.QueryExpression, 787 tableName parser.Identifier, 788 forUpdate bool, 789 useInternalId bool, 790 isInlineObject bool, 791 options option.ImportOptions, 792 ) (*View, error) { 793 if stdin, ok := tablePath.(parser.Stdin); ok { 794 return loadObjectFromStdin(ctx, scope, stdin, tableName, forUpdate, useInternalId, options) 795 } 796 797 if !isInlineObject { 798 if tableFunction, ok := tablePath.(parser.TableFunction); ok && strings.ToUpper(tableFunction.Name) == "INLINE" { 799 isInlineObject = true 800 } 801 } 802 803 originalTablePath := tablePath 804 tablePath, err := NormalizeTableObject(ctx, scope, tablePath) 805 if err != nil { 806 return nil, err 807 } 808 809 if dataObject, ok := tablePath.(DataObject); ok { 810 return loadDataObject(ctx, scope, dataObject, originalTablePath, tableName, options) 811 } 812 813 if httpObject, ok := tablePath.(HttpObject); ok { 814 return loadHttpObject(ctx, scope, httpObject, originalTablePath, tableName, options) 815 } 816 817 fileIdentifier := tablePath.(parser.Identifier) 818 819 if isInlineObject { 820 return loadInlineObjectFromFile(ctx, scope, fileIdentifier, tableName, options) 821 } 822 823 if scope.RecursiveTable != nil && strings.EqualFold(fileIdentifier.Literal, scope.RecursiveTable.Name.Literal) && scope.RecursiveTmpView != nil { 824 view := scope.RecursiveTmpView.Copy() 825 if !strings.EqualFold(scope.RecursiveTable.Name.Literal, tableName.Literal) { 826 if err := view.Header.Update(tableName.Literal, nil); err != nil { 827 return nil, err 828 } 829 } 830 return view, nil 831 } 832 833 if scope.InlineTableExists(fileIdentifier) { 834 if err := scope.AddAlias(tableName, ""); err != nil { 835 return nil, err 836 } 837 838 view, _ := scope.GetInlineTable(fileIdentifier) 839 if !strings.EqualFold(fileIdentifier.Literal, tableName.Literal) { 840 if err := view.Header.Update(tableName.Literal, nil); err != nil { 841 return nil, err 842 } 843 } 844 return view, nil 845 } 846 847 if scope.TemporaryTableExists(fileIdentifier.Literal) { 848 var view *View = nil 849 var err error 850 851 if useInternalId { 852 view, err = scope.GetTemporaryTableWithInternalId(ctx, fileIdentifier, scope.Tx.Flags) 853 } else { 854 view, err = scope.GetTemporaryTable(fileIdentifier) 855 } 856 if err != nil { 857 return nil, err 858 } 859 860 if err := scope.AddAlias(tableName, fileIdentifier.Literal); err != nil { 861 return nil, err 862 } 863 864 if !strings.EqualFold(FormatTableName(fileIdentifier.Literal), tableName.Literal) { 865 if err := view.Header.Update(tableName.Literal, nil); err != nil { 866 return nil, err 867 } 868 } 869 870 return view, nil 871 } 872 873 return loadObjectFromFile(ctx, scope, fileIdentifier, tableName, forUpdate, useInternalId, options) 874 } 875 876 func cacheViewFromFile( 877 ctx context.Context, 878 scope *ReferenceScope, 879 fileIdentifier parser.Identifier, 880 forUpdate bool, 881 options option.ImportOptions, 882 ) (filePath string, err error) { 883 scope.Tx.viewLoadingMutex.Lock() 884 defer scope.Tx.viewLoadingMutex.Unlock() 885 886 filePath, view, isCached, err := func() (string, *View, bool, error) { 887 if p, ok := scope.LoadFilePath(fileIdentifier.Literal); ok { 888 if v, ok := scope.Tx.CachedViews.Load(strings.ToUpper(p)); ok { 889 return p, v, true, nil 890 } 891 } 892 893 // Uncommitted newly created tables are not yet created as files, so they need to be checked if they are 894 // registered in the cache with CreateFilePath(), instead of SearchFilePath(). 895 p, e := CreateFilePath(fileIdentifier, scope.Tx.Flags.Repository) 896 if e != nil { 897 return "", nil, false, NewIOError(fileIdentifier, err.Error()) 898 } 899 if v, ok := scope.Tx.CachedViews.Load(strings.ToUpper(p)); ok { 900 return p, v, true, nil 901 } 902 903 p, _, e = SearchFilePath(fileIdentifier, scope.Tx.Flags.Repository, options, scope.Tx.Flags.ImportOptions.Format) 904 if e != nil { 905 return "", nil, false, e 906 } 907 v, ok := scope.Tx.CachedViews.Load(strings.ToUpper(p)) 908 return p, v, ok, nil 909 }() 910 if err != nil { 911 return 912 } 913 914 if !isCached || (forUpdate && !view.FileInfo.ForUpdate) { 915 var fileInfo *FileInfo = nil 916 if isCached { 917 fileInfo = view.FileInfo 918 if err = scope.Tx.CachedViews.Dispose(scope.Tx.FileContainer, fileInfo.IdentifiedPath()); err != nil { 919 return 920 } 921 } else { 922 fileInfo, err = NewFileInfo(fileIdentifier, scope.Tx.Flags.Repository, options, scope.Tx.Flags.ImportOptions.Format) 923 if err != nil { 924 return 925 } 926 fileInfo.SetDefaultFileInfoAttributes(options, scope.Tx.Flags.ExportOptions) 927 filePath = fileInfo.Path 928 } 929 930 var fp *os.File 931 932 if forUpdate { 933 h, e := scope.Tx.FileContainer.CreateHandlerForUpdate(ctx, fileInfo.Path, scope.Tx.WaitTimeout, scope.Tx.RetryDelay) 934 if e != nil { 935 fileIdentifier.Literal = fileInfo.Path 936 err = ConvertFileHandlerError(e, fileIdentifier) 937 return 938 } 939 fileInfo.Handler = h 940 fp = h.File() 941 } else { 942 h, e := scope.Tx.FileContainer.CreateHandlerForRead(ctx, fileInfo.Path, scope.Tx.WaitTimeout, scope.Tx.RetryDelay) 943 if e != nil { 944 fileIdentifier.Literal = fileInfo.Path 945 err = ConvertFileHandlerError(e, fileIdentifier) 946 return 947 } 948 defer func() { 949 err = appendCompositeError(err, scope.Tx.FileContainer.Close(h)) 950 }() 951 fp = h.File() 952 } 953 _, err = fp.Seek(0, io.SeekStart) 954 if err != nil { 955 return filePath, NewIOError(fileIdentifier, err.Error()) 956 } 957 958 view, err = loadViewFromFile(ctx, scope.Tx.Flags, fp, fileInfo, options, fileIdentifier) 959 if err != nil { 960 if _, ok := err.(Error); !ok { 961 err = NewDataParsingError(fileIdentifier, fileInfo.Path, err.Error()) 962 } 963 if forUpdate { 964 err = appendCompositeError(err, scope.Tx.FileContainer.Close(fileInfo.Handler)) 965 } 966 return 967 } 968 view.FileInfo.ForUpdate = forUpdate 969 scope.Tx.CachedViews.Set(view) 970 } 971 972 if !scope.FilePathExists(fileIdentifier.Literal) { 973 scope.StoreFilePath(fileIdentifier.Literal, filePath) 974 } 975 976 return 977 } 978 979 func loadViewFromFile(ctx context.Context, flags *option.Flags, fp io.Reader, fileInfo *FileInfo, options option.ImportOptions, expr parser.QueryExpression) (*View, error) { 980 fileReader, err := file.NewReader(fp, 2048) 981 if err != nil { 982 return nil, NewIOError(expr, err.Error()) 983 } 984 985 switch fileInfo.Format { 986 case option.FIXED: 987 return loadViewFromFixedLengthTextFile(ctx, fileReader, fileInfo, options.WithoutNull, expr) 988 case option.LTSV: 989 return loadViewFromLTSVFile(ctx, flags, fileReader, fileInfo, options.WithoutNull, expr) 990 case option.JSON: 991 return loadViewFromJsonFile(fileReader, fileInfo, expr) 992 case option.JSONL: 993 return loadViewFromJsonLinesFile(ctx, flags, fileReader, fileInfo, expr) 994 } 995 return loadViewFromCSVFile(ctx, fileReader, fileInfo, options.AllowUnevenFields, options.WithoutNull, expr) 996 } 997 998 func loadViewFromFixedLengthTextFile(ctx context.Context, fp *file.Reader, fileInfo *FileInfo, withoutNull bool, expr parser.QueryExpression) (*View, error) { 999 fileHead, err := fp.HeadBytes() 1000 if err != nil { 1001 return nil, NewIOError(expr, err.Error()) 1002 } 1003 1004 enc, err := text.DetectInSpecifiedEncoding(fileHead, fileInfo.Encoding) 1005 if err != nil { 1006 return nil, NewCannotDetectFileEncodingError(expr) 1007 } 1008 fileInfo.Encoding = enc 1009 1010 var r io.Reader 1011 1012 if fileInfo.DelimiterPositions == nil { 1013 data, err := io.ReadAll(fp) 1014 if err != nil { 1015 return nil, NewIOError(expr, err.Error()) 1016 } 1017 br := bytes.NewReader(data) 1018 1019 d, err := fixedlen.NewDelimiter(br, fileInfo.Encoding) 1020 if err != nil { 1021 return nil, err 1022 } 1023 d.NoHeader = fileInfo.NoHeader 1024 d.Encoding = fileInfo.Encoding 1025 fileInfo.DelimiterPositions, err = d.Delimit() 1026 if err != nil { 1027 return nil, err 1028 } 1029 1030 if _, err = br.Seek(0, io.SeekStart); err != nil { 1031 return nil, NewIOError(expr, err.Error()) 1032 } 1033 r = br 1034 } else { 1035 r = fp 1036 } 1037 1038 reader, err := fixedlen.NewReader(r, fileInfo.DelimiterPositions, fileInfo.Encoding) 1039 if err != nil { 1040 return nil, err 1041 } 1042 reader.WithoutNull = withoutNull 1043 reader.Encoding = fileInfo.Encoding 1044 reader.SingleLine = fileInfo.SingleLine 1045 1046 var header []string 1047 if !fileInfo.NoHeader && !fileInfo.SingleLine { 1048 header, err = reader.ReadHeader() 1049 if err != nil && err != io.EOF { 1050 return nil, err 1051 } 1052 } 1053 1054 records, err := readRecordSet(ctx, reader, fp.Size()) 1055 if err != nil { 1056 return nil, err 1057 } 1058 1059 if header == nil { 1060 header = make([]string, len(fileInfo.DelimiterPositions)) 1061 for i := 0; i < len(fileInfo.DelimiterPositions); i++ { 1062 header[i] = "c" + strconv.Itoa(i+1) 1063 } 1064 } 1065 1066 if reader.DetectedLineBreak != "" { 1067 fileInfo.LineBreak = reader.DetectedLineBreak 1068 } 1069 1070 view := NewView() 1071 view.Header = NewHeaderWithAutofill(FormatTableName(fileInfo.Path), header) 1072 view.RecordSet = records 1073 view.FileInfo = fileInfo 1074 return view, nil 1075 } 1076 1077 func loadViewFromCSVFile(ctx context.Context, fp *file.Reader, fileInfo *FileInfo, allowUnevenFields bool, withoutNull bool, expr parser.QueryExpression) (*View, error) { 1078 if fileInfo.Format == option.TSV { 1079 fileInfo.Delimiter = '\t' 1080 } 1081 1082 fileHead, err := fp.HeadBytes() 1083 if err != nil { 1084 return nil, NewIOError(expr, err.Error()) 1085 } 1086 1087 enc, err := text.DetectInSpecifiedEncoding(fileHead, fileInfo.Encoding) 1088 if err != nil { 1089 return nil, NewCannotDetectFileEncodingError(expr) 1090 } 1091 fileInfo.Encoding = enc 1092 1093 reader, err := csv.NewReader(fp, fileInfo.Encoding) 1094 if err != nil { 1095 return nil, err 1096 } 1097 reader.Delimiter = fileInfo.Delimiter 1098 reader.WithoutNull = withoutNull 1099 reader.AllowUnevenFields = allowUnevenFields 1100 1101 var header []string 1102 if !fileInfo.NoHeader { 1103 header, err = reader.ReadHeader() 1104 if err != nil && err != io.EOF { 1105 return nil, err 1106 } 1107 } 1108 1109 records, err := readRecordSet(ctx, reader, fp.Size()) 1110 if err != nil { 1111 return nil, err 1112 } 1113 1114 if header == nil { 1115 header = make([]string, reader.FieldsPerRecord) 1116 for i := 0; i < reader.FieldsPerRecord; i++ { 1117 header[i] = "c" + strconv.Itoa(i+1) 1118 } 1119 } 1120 1121 if reader.DetectedLineBreak != "" { 1122 fileInfo.LineBreak = reader.DetectedLineBreak 1123 } 1124 fileInfo.EncloseAll = reader.EnclosedAll 1125 1126 view := NewView() 1127 1128 if allowUnevenFields { 1129 if len(header) < reader.FieldsPerRecord { 1130 header = append(header, make([]string, reader.FieldsPerRecord-len(header))...) 1131 } 1132 view.Header = NewHeaderWithAutofill(FormatTableName(fileInfo.Path), header) 1133 1134 for i := range records { 1135 if reader.FieldsPerRecord <= len(records[i]) { 1136 continue 1137 } 1138 1139 filling := make([]Cell, reader.FieldsPerRecord-len(records[i])) 1140 for j := range filling { 1141 if withoutNull { 1142 filling[j] = NewCell(value.NewString("")) 1143 } else { 1144 filling[j] = NewCell(value.NewNull()) 1145 } 1146 } 1147 1148 records[i] = append(records[i], filling...) 1149 } 1150 } else { 1151 view.Header = NewHeader(FormatTableName(fileInfo.Path), header) 1152 } 1153 1154 view.RecordSet = records 1155 view.FileInfo = fileInfo 1156 return view, nil 1157 } 1158 1159 func loadViewFromLTSVFile(ctx context.Context, flags *option.Flags, fp *file.Reader, fileInfo *FileInfo, withoutNull bool, expr parser.QueryExpression) (*View, error) { 1160 fileHead, err := fp.HeadBytes() 1161 if err != nil { 1162 return nil, NewIOError(expr, err.Error()) 1163 } 1164 1165 enc, err := text.DetectInSpecifiedEncoding(fileHead, fileInfo.Encoding) 1166 if err != nil { 1167 return nil, NewCannotDetectFileEncodingError(expr) 1168 } 1169 fileInfo.Encoding = enc 1170 1171 reader, err := ltsv.NewReader(fp, fileInfo.Encoding) 1172 if err != nil { 1173 return nil, NewIOError(expr, err.Error()) 1174 } 1175 reader.WithoutNull = withoutNull 1176 1177 records, err := readRecordSet(ctx, reader, fp.Size()) 1178 if err != nil { 1179 return nil, err 1180 } 1181 1182 header := reader.Header.Fields() 1183 if err = NewGoroutineTaskManager(len(records), -1, flags.CPU).Run(ctx, func(index int) error { 1184 for j := len(records[index]); j < len(header); j++ { 1185 if withoutNull { 1186 records[index] = append(records[index], NewCell(value.NewString(""))) 1187 } else { 1188 records[index] = append(records[index], NewCell(value.NewNull())) 1189 } 1190 } 1191 return nil 1192 }); err != nil { 1193 return nil, err 1194 } 1195 1196 if reader.DetectedLineBreak != "" { 1197 fileInfo.LineBreak = reader.DetectedLineBreak 1198 } 1199 1200 view := NewView() 1201 view.Header = NewHeader(FormatTableName(fileInfo.Path), header) 1202 view.RecordSet = records 1203 view.FileInfo = fileInfo 1204 return view, nil 1205 } 1206 1207 func readRecordSet(ctx context.Context, reader RecordReader, fileSize int64) (RecordSet, error) { 1208 var err error 1209 recordSet := make(RecordSet, 0, fileLoadingPreparedRecordSetCap) 1210 rowch := make(chan []text.RawText, fileLoadingBuffer) 1211 panicCh := make(chan bool, 1) 1212 pos := 0 1213 1214 wg := sync.WaitGroup{} 1215 1216 wg.Add(1) 1217 go func() { 1218 defer func() { 1219 if err == nil { 1220 if panicReport := recover(); panicReport != nil { 1221 err = NewFatalError(panicReport) 1222 } 1223 } 1224 panicCh <- true 1225 wg.Done() 1226 }() 1227 1228 for { 1229 row, ok := <-rowch 1230 if !ok { 1231 break 1232 } 1233 1234 record := make(Record, len(row)) 1235 for i, v := range row { 1236 if v == nil { 1237 record[i] = NewCell(value.NewNull()) 1238 } else { 1239 record[i] = NewCell(value.NewString(string(v))) 1240 } 1241 } 1242 1243 if 0 < fileSize && 0 < pos && len(recordSet) == fileLoadingPreparedRecordSetCap && int64(pos) < fileSize { 1244 l := int((float64(fileSize) / float64(pos)) * fileLoadingPreparedRecordSetCap * 1.2) 1245 newSet := make(RecordSet, fileLoadingPreparedRecordSetCap, l) 1246 copy(newSet, recordSet) 1247 recordSet = newSet 1248 } 1249 1250 recordSet = append(recordSet, record) 1251 } 1252 }() 1253 1254 wg.Add(1) 1255 go func() { 1256 panicOccurred := false 1257 1258 defer func() { 1259 if err == nil && !panicOccurred { 1260 if panicReport := recover(); panicReport != nil { 1261 err = NewFatalError(panicReport) 1262 } 1263 } 1264 close(rowch) 1265 wg.Done() 1266 }() 1267 1268 i := 0 1269 1270 for { 1271 if i&15 == 0 && ctx.Err() != nil { 1272 err = ConvertContextError(ctx.Err()) 1273 break 1274 } 1275 1276 row, e := reader.Read() 1277 if e == io.EOF { 1278 break 1279 } 1280 if e != nil { 1281 err = e 1282 break 1283 } 1284 1285 if 0 < fileSize && i < fileLoadingPreparedRecordSetCap { 1286 for j := range row { 1287 pos += len(row[j]) 1288 } 1289 } 1290 1291 select { 1292 case _ = <-panicCh: 1293 panicOccurred = true 1294 case rowch <- row: 1295 // Row data sent. 1296 } 1297 1298 if panicOccurred { 1299 break 1300 } 1301 i++ 1302 } 1303 }() 1304 1305 wg.Wait() 1306 close(panicCh) 1307 1308 return recordSet, err 1309 } 1310 1311 func loadViewFromJsonFile(fp *file.Reader, fileInfo *FileInfo, expr parser.QueryExpression) (*View, error) { 1312 jsonText, err := io.ReadAll(fp) 1313 if err != nil { 1314 return nil, NewIOError(expr, err.Error()) 1315 } 1316 1317 headerLabels, rows, escapeType, err := json.LoadTable(fileInfo.JsonQuery, string(jsonText)) 1318 if err != nil { 1319 return nil, NewLoadJsonError(expr, err.Error()) 1320 } 1321 1322 records := make(RecordSet, len(rows)) 1323 for i := range rows { 1324 records[i] = NewRecord(rows[i]) 1325 } 1326 1327 fileInfo.Encoding = text.UTF8 1328 fileInfo.JsonEscape = escapeType 1329 1330 view := NewView() 1331 view.Header = NewHeader(FormatTableName(fileInfo.Path), headerLabels) 1332 view.RecordSet = records 1333 view.FileInfo = fileInfo 1334 return view, nil 1335 } 1336 1337 func loadViewFromJsonLinesFile(ctx context.Context, flags *option.Flags, fp *file.Reader, fileInfo *FileInfo, expr parser.QueryExpression) (*View, error) { 1338 var err error 1339 headerList := make([]string, 0, 32) 1340 headerMap := make(map[string]bool, 32) 1341 objectList := make([]txjson.Object, 0, fileLoadingPreparedRecordSetCap) 1342 1343 escapeType := txjson.Backslash 1344 fileSize := fp.Size() 1345 jsonQuery, err := json.Query.Parse(fileInfo.JsonQuery) 1346 if err != nil { 1347 return nil, NewLoadJsonError(expr, err.Error()) 1348 } 1349 1350 rowch := make(chan txjson.Object, fileLoadingBuffer) 1351 panicCh := make(chan bool, 1) 1352 pos := 0 1353 1354 reader := jsonl.NewReader(fp) 1355 reader.SetUseInteger(false) 1356 1357 wg := sync.WaitGroup{} 1358 1359 wg.Add(1) 1360 go func() { 1361 defer func() { 1362 if err == nil { 1363 if panicReport := recover(); panicReport != nil { 1364 err = NewFatalError(panicReport) 1365 } 1366 } 1367 panicCh <- true 1368 wg.Done() 1369 }() 1370 1371 for { 1372 row, ok := <-rowch 1373 if !ok { 1374 break 1375 } 1376 1377 for _, v := range row.Keys() { 1378 if _, ok := headerMap[v]; !ok { 1379 headerMap[v] = true 1380 headerList = append(headerList, v) 1381 } 1382 } 1383 1384 if 0 < fileSize && 0 < pos && len(objectList) == fileLoadingPreparedRecordSetCap && int64(pos) < fileSize { 1385 l := int((float64(fileSize) / float64(pos)) * fileLoadingPreparedRecordSetCap * 1.2) 1386 newSet := make([]txjson.Object, fileLoadingPreparedRecordSetCap, l) 1387 copy(newSet, objectList) 1388 objectList = newSet 1389 } 1390 1391 objectList = append(objectList, row) 1392 } 1393 }() 1394 1395 wg.Add(1) 1396 go func() { 1397 panicOccurred := false 1398 1399 defer func() { 1400 if err == nil && !panicOccurred { 1401 if panicReport := recover(); panicReport != nil { 1402 err = NewFatalError(panicReport) 1403 } 1404 } 1405 close(rowch) 1406 wg.Done() 1407 }() 1408 1409 i := 0 1410 for { 1411 if i&15 == 0 && ctx.Err() != nil { 1412 err = ConvertContextError(ctx.Err()) 1413 break 1414 } 1415 1416 row, et, e := reader.Read() 1417 if e == io.EOF { 1418 break 1419 } 1420 if e != nil { 1421 err = e 1422 break 1423 } 1424 1425 rowObj, ok := row.(txjson.Object) 1426 if !ok { 1427 err = NewJsonLinesStructureError(expr) 1428 break 1429 } 1430 1431 if jsonQuery != nil { 1432 jstruct, e := json.Extract(jsonQuery, rowObj) 1433 if e != nil { 1434 err = e 1435 break 1436 } 1437 jarray, ok := jstruct.(txjson.Array) 1438 if !ok || len(jarray) < 1 { 1439 err = NewJsonLinesStructureError(expr) 1440 break 1441 } 1442 rowObj, ok = jarray[0].(txjson.Object) 1443 if !ok { 1444 err = NewJsonLinesStructureError(expr) 1445 break 1446 } 1447 } 1448 1449 if escapeType < et { 1450 escapeType = et 1451 } 1452 1453 if 0 < fileSize && i < fileLoadingPreparedRecordSetCap { 1454 pos = reader.Pos() 1455 } 1456 1457 select { 1458 case _ = <-panicCh: 1459 panicOccurred = true 1460 case rowch <- rowObj: 1461 // Row data sent. 1462 } 1463 1464 if panicOccurred { 1465 break 1466 } 1467 i++ 1468 } 1469 }() 1470 1471 wg.Wait() 1472 close(panicCh) 1473 1474 if err != nil { 1475 return nil, err 1476 } 1477 1478 recordSet := make(RecordSet, len(objectList)) 1479 1480 if err = NewGoroutineTaskManager(len(objectList), -1, flags.CPU).Run(ctx, func(index int) error { 1481 values := make([]value.Primary, len(headerList)) 1482 1483 for i, v := range headerList { 1484 if objectList[index].Exists(v) { 1485 values[i] = json.ConvertToValue(objectList[index].Value(v)) 1486 } else { 1487 values[i] = value.NewNull() 1488 } 1489 } 1490 1491 recordSet[index] = NewRecord(values) 1492 return nil 1493 }); err != nil { 1494 return nil, err 1495 } 1496 1497 fileInfo.Encoding = text.UTF8 1498 fileInfo.JsonEscape = escapeType 1499 1500 view := NewView() 1501 view.Header = NewHeader(FormatTableName(fileInfo.Path), headerList) 1502 view.RecordSet = recordSet 1503 view.FileInfo = fileInfo 1504 return view, nil 1505 }