github.com/mithrandie/csvq@v1.18.1/lib/query/built_in_command.go (about) 1 package query 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "os" 8 "path/filepath" 9 "strconv" 10 "strings" 11 12 "github.com/mithrandie/csvq/lib/doc" 13 "github.com/mithrandie/csvq/lib/file" 14 "github.com/mithrandie/csvq/lib/option" 15 "github.com/mithrandie/csvq/lib/parser" 16 "github.com/mithrandie/csvq/lib/syntax" 17 "github.com/mithrandie/csvq/lib/value" 18 19 "github.com/mithrandie/go-text" 20 "github.com/mithrandie/go-text/color" 21 "github.com/mithrandie/ternary" 22 ) 23 24 type ObjectStatus int 25 26 const ( 27 ObjectFixed ObjectStatus = iota 28 ObjectCreated 29 ObjectUpdated 30 ReadOnly 31 ) 32 33 const IgnoredFlagPrefix = "(ignored) " 34 35 const ( 36 ReloadConfig = "CONFIG" 37 ) 38 39 const ( 40 ShowTables = "TABLES" 41 ShowViews = "VIEWS" 42 ShowCursors = "CURSORS" 43 ShowFunctions = "FUNCTIONS" 44 ShowStatements = "STATEMENTS" 45 ShowFlags = "FLAGS" 46 ShowEnv = "ENV" 47 ShowRuninfo = "RUNINFO" 48 ) 49 50 var ShowObjectList = []string{ 51 ShowTables, 52 ShowViews, 53 ShowCursors, 54 ShowFunctions, 55 ShowStatements, 56 ShowFlags, 57 ShowEnv, 58 ShowRuninfo, 59 } 60 61 func Echo(ctx context.Context, scope *ReferenceScope, expr parser.Echo) (string, error) { 62 p, err := Evaluate(ctx, scope, expr.Value) 63 if err != nil { 64 return "", err 65 } 66 67 return NewStringFormatter().Format("%s", []value.Primary{p}) 68 } 69 70 func Print(ctx context.Context, scope *ReferenceScope, expr parser.Print) (string, error) { 71 p, err := Evaluate(ctx, scope, expr.Value) 72 if err != nil { 73 return "", err 74 } 75 return p.String(), err 76 } 77 78 func Printf(ctx context.Context, scope *ReferenceScope, expr parser.Printf) (string, error) { 79 var format string 80 formatValue, err := Evaluate(ctx, scope, expr.Format) 81 if err != nil { 82 return "", err 83 } 84 formatString := value.ToString(formatValue) 85 if !value.IsNull(formatString) { 86 format = formatString.(*value.String).Raw() 87 value.Discard(formatString) 88 } 89 90 args := make([]value.Primary, len(expr.Values)) 91 for i, v := range expr.Values { 92 p, err := Evaluate(ctx, scope, v) 93 if err != nil { 94 return "", err 95 } 96 args[i] = p 97 } 98 99 message, err := NewStringFormatter().Format(format, args) 100 if err != nil { 101 return "", NewReplaceValueLengthError(expr, err.(Error).Message()) 102 } 103 return message, nil 104 } 105 106 func Source(ctx context.Context, scope *ReferenceScope, expr parser.Source) ([]parser.Statement, error) { 107 var fpath string 108 109 if ident, ok := expr.FilePath.(parser.Identifier); ok { 110 fpath = ident.Literal 111 } else { 112 p, err := Evaluate(ctx, scope, expr.FilePath) 113 if err != nil { 114 return nil, err 115 } 116 s := value.ToString(p) 117 if value.IsNull(s) { 118 return nil, NewSourceInvalidFilePathError(expr, expr.FilePath) 119 } 120 fpath = s.(*value.String).Raw() 121 value.Discard(s) 122 } 123 124 if len(fpath) < 1 { 125 return nil, NewSourceInvalidFilePathError(expr, expr.FilePath) 126 } 127 128 return LoadStatementsFromFile(ctx, scope.Tx, parser.Identifier{BaseExpr: expr.BaseExpr, Literal: fpath}) 129 } 130 131 func LoadContentsFromFile(ctx context.Context, tx *Transaction, fpath parser.Identifier) (content string, err error) { 132 p := fpath.Literal 133 if !filepath.IsAbs(p) { 134 if abs, err := filepath.Abs(p); err == nil { 135 p = abs 136 } 137 } 138 139 if !file.Exists(p) { 140 return content, NewFileNotExistError(fpath) 141 } 142 143 h, err := tx.FileContainer.CreateHandlerWithoutLock(ctx, p, tx.WaitTimeout, tx.RetryDelay) 144 if err != nil { 145 return content, ConvertFileHandlerError(err, fpath) 146 } 147 defer func() { 148 err = appendCompositeError(err, tx.FileContainer.Close(h)) 149 }() 150 151 buf, err := io.ReadAll(h.File()) 152 if err != nil { 153 return content, ConvertFileHandlerError(err, fpath) 154 } 155 return string(buf), nil 156 } 157 158 func LoadStatementsFromFile(ctx context.Context, tx *Transaction, fpath parser.Identifier) (statements []parser.Statement, err error) { 159 content, err := LoadContentsFromFile(ctx, tx, fpath) 160 if err != nil { 161 return nil, err 162 } 163 164 statements, _, err = parser.Parse(content, fpath.Literal, false, tx.Flags.AnsiQuotes) 165 if err != nil { 166 err = NewSyntaxError(err.(*parser.SyntaxError)) 167 } 168 return statements, err 169 } 170 171 func ParseExecuteStatements(ctx context.Context, scope *ReferenceScope, expr parser.Execute) ([]parser.Statement, error) { 172 var input string 173 stmt, err := Evaluate(ctx, scope, expr.Statements) 174 if err != nil { 175 return nil, err 176 } 177 stmtStr := value.ToString(stmt) 178 if !value.IsNull(stmtStr) { 179 input = stmt.(*value.String).Raw() 180 value.Discard(stmtStr) 181 } 182 183 args := make([]value.Primary, len(expr.Values)) 184 for i, v := range expr.Values { 185 p, err := Evaluate(ctx, scope, v) 186 if err != nil { 187 return nil, err 188 } 189 args[i] = p 190 } 191 192 input, err = NewStringFormatter().Format(input, args) 193 if err != nil { 194 return nil, NewReplaceValueLengthError(expr, err.(Error).Message()) 195 } 196 statements, _, err := parser.Parse(input, fmt.Sprintf("(L:%d C:%d) EXECUTE", expr.Line(), expr.Char()), false, scope.Tx.Flags.AnsiQuotes) 197 if err != nil { 198 err = NewSyntaxError(err.(*parser.SyntaxError)) 199 } 200 return statements, err 201 } 202 203 func SetFlag(ctx context.Context, scope *ReferenceScope, expr parser.SetFlag) error { 204 var val interface{} 205 var v value.Primary 206 var p value.Primary 207 var err error 208 209 if ident, ok := expr.Value.(parser.Identifier); ok { 210 v = value.NewString(ident.Literal) 211 } else { 212 v, err = Evaluate(ctx, scope, expr.Value) 213 if err != nil { 214 return err 215 } 216 } 217 218 switch strings.ToUpper(expr.Flag.Name) { 219 case option.RepositoryFlag, option.TimezoneFlag, option.DatetimeFormatFlag, 220 option.ImportFormatFlag, option.DelimiterFlag, option.DelimiterPositionsFlag, option.JsonQueryFlag, option.EncodingFlag, 221 option.ExportEncodingFlag, option.FormatFlag, option.ExportDelimiterFlag, option.ExportDelimiterPositionsFlag, 222 option.LineBreakFlag, option.JsonEscapeFlag: 223 p = value.ToString(v) 224 if value.IsNull(p) { 225 return NewFlagValueNotAllowedFormatError(expr) 226 } 227 val = p.(*value.String).Raw() 228 case option.AnsiQuotesFlag, option.StrictEqualFlag, option.AllowUnevenFieldsFlag, 229 option.NoHeaderFlag, option.WithoutNullFlag, option.WithoutHeaderFlag, option.EncloseAllFlag, 230 option.PrettyPrintFlag, option.ScientificNotationFlag, option.StripEndingLineBreakFlag, 231 option.EastAsianEncodingFlag, option.CountDiacriticalSignFlag, option.CountFormatCodeFlag, option.ColorFlag, 232 option.QuietFlag, option.StatsFlag: 233 p = value.ToBoolean(v) 234 if value.IsNull(p) { 235 return NewFlagValueNotAllowedFormatError(expr) 236 } 237 val = p.(*value.Boolean).Raw() 238 case option.WaitTimeoutFlag: 239 p = value.ToFloat(v) 240 if value.IsNull(p) { 241 return NewFlagValueNotAllowedFormatError(expr) 242 } 243 val = p.(*value.Float).Raw() 244 case option.LimitRecursion, option.CPUFlag: 245 p = value.ToInteger(v) 246 if value.IsNull(p) { 247 return NewFlagValueNotAllowedFormatError(expr) 248 } 249 val = p.(*value.Integer).Raw() 250 default: 251 return NewInvalidFlagNameError(expr.Flag) 252 } 253 254 value.Discard(p) 255 256 if err = scope.Tx.SetFlag(expr.Flag.Name, val); err != nil { 257 return NewInvalidFlagValueError(expr, err.Error()) 258 } 259 return nil 260 } 261 262 func AddFlagElement(ctx context.Context, scope *ReferenceScope, expr parser.AddFlagElement) error { 263 switch strings.ToUpper(expr.Flag.Name) { 264 case option.DatetimeFormatFlag: 265 e := parser.SetFlag{ 266 BaseExpr: expr.GetBaseExpr(), 267 Flag: expr.Flag, 268 Value: expr.Value, 269 } 270 return SetFlag(ctx, scope, e) 271 case option.RepositoryFlag, option.TimezoneFlag, option.AnsiQuotesFlag, option.StrictEqualFlag, 272 option.ImportFormatFlag, option.DelimiterFlag, option.AllowUnevenFieldsFlag, option.DelimiterPositionsFlag, 273 option.JsonQueryFlag, option.EncodingFlag, 274 option.ExportEncodingFlag, option.FormatFlag, option.ExportDelimiterFlag, option.ExportDelimiterPositionsFlag, 275 option.LineBreakFlag, option.JsonEscapeFlag, option.NoHeaderFlag, option.WithoutNullFlag, option.WithoutHeaderFlag, 276 option.EncloseAllFlag, option.PrettyPrintFlag, option.ScientificNotationFlag, option.StripEndingLineBreakFlag, 277 option.EastAsianEncodingFlag, option.CountDiacriticalSignFlag, option.CountFormatCodeFlag, option.ColorFlag, 278 option.QuietFlag, option.StatsFlag, 279 option.WaitTimeoutFlag, 280 option.LimitRecursion, option.CPUFlag: 281 282 return NewAddFlagNotSupportedNameError(expr) 283 default: 284 return NewInvalidFlagNameError(expr.Flag) 285 } 286 } 287 288 func RemoveFlagElement(ctx context.Context, scope *ReferenceScope, expr parser.RemoveFlagElement) error { 289 p, err := Evaluate(ctx, scope, expr.Value) 290 if err != nil { 291 return err 292 } 293 294 scope.Tx.operationMutex.Lock() 295 defer scope.Tx.operationMutex.Unlock() 296 297 switch strings.ToUpper(expr.Flag.Name) { 298 case option.DatetimeFormatFlag: 299 if i := value.ToInteger(p); !value.IsNull(i) { 300 idx := int(i.(*value.Integer).Raw()) 301 value.Discard(i) 302 303 if -1 < idx && idx < len(scope.Tx.Flags.DatetimeFormat) { 304 scope.Tx.Flags.DatetimeFormat = append(scope.Tx.Flags.DatetimeFormat[:idx], scope.Tx.Flags.DatetimeFormat[idx+1:]...) 305 } 306 } else if s := value.ToString(p); !value.IsNull(s) { 307 val := s.(*value.String).Raw() 308 value.Discard(s) 309 310 formats := make([]string, 0, len(scope.Tx.Flags.DatetimeFormat)) 311 for _, v := range scope.Tx.Flags.DatetimeFormat { 312 if val != v { 313 formats = append(formats, v) 314 } 315 } 316 scope.Tx.Flags.DatetimeFormat = formats 317 } else { 318 return NewInvalidFlagValueToBeRemovedError(expr) 319 } 320 case option.RepositoryFlag, option.TimezoneFlag, option.AnsiQuotesFlag, option.StrictEqualFlag, 321 option.ImportFormatFlag, option.DelimiterFlag, option.AllowUnevenFieldsFlag, option.DelimiterPositionsFlag, 322 option.JsonQueryFlag, option.EncodingFlag, 323 option.ExportEncodingFlag, option.FormatFlag, option.ExportDelimiterFlag, option.ExportDelimiterPositionsFlag, 324 option.LineBreakFlag, option.JsonEscapeFlag, option.NoHeaderFlag, option.WithoutNullFlag, option.WithoutHeaderFlag, 325 option.EncloseAllFlag, option.PrettyPrintFlag, option.ScientificNotationFlag, option.StripEndingLineBreakFlag, 326 option.EastAsianEncodingFlag, option.CountDiacriticalSignFlag, option.CountFormatCodeFlag, option.ColorFlag, 327 option.QuietFlag, option.StatsFlag, 328 option.WaitTimeoutFlag, 329 option.LimitRecursion, option.CPUFlag: 330 331 return NewRemoveFlagNotSupportedNameError(expr) 332 default: 333 return NewInvalidFlagNameError(expr.Flag) 334 } 335 336 return nil 337 } 338 339 func ShowFlag(tx *Transaction, expr parser.ShowFlag) (string, error) { 340 s, ok := showFlag(tx, expr.Flag.Name) 341 if !ok { 342 return s, NewInvalidFlagNameError(expr.Flag) 343 } 344 345 return tx.Palette.Render(option.LableEffect, option.FlagSymbol(strings.ToUpper(expr.Flag.Name))+":") + " " + s, nil 346 } 347 348 func showFlag(tx *Transaction, flagName string) (string, bool) { 349 val, ok := tx.GetFlag(flagName) 350 if !ok { 351 return "", ok 352 } 353 354 var s string 355 356 switch strings.ToUpper(flagName) { 357 case option.RepositoryFlag: 358 p := val.(*value.String) 359 if len(p.Raw()) < 1 { 360 wd, _ := os.Getwd() 361 s = tx.Palette.Render(option.NullEffect, fmt.Sprintf("(current dir: %s)", wd)) 362 } else { 363 s = tx.Palette.Render(option.StringEffect, p.Raw()) 364 } 365 case option.DatetimeFormatFlag: 366 p := val.(*value.String) 367 if len(p.Raw()) < 1 { 368 s = tx.Palette.Render(option.NullEffect, "(not set)") 369 } else { 370 s = tx.Palette.Render(option.StringEffect, p.Raw()) 371 } 372 case option.JsonQueryFlag: 373 p := val.(*value.String) 374 if len(p.Raw()) < 1 { 375 s = tx.Palette.Render(option.NullEffect, "(empty)") 376 } else { 377 s = tx.Palette.Render(option.StringEffect, p.Raw()) 378 } 379 case option.ExportEncodingFlag: 380 switch tx.Flags.ExportOptions.Format { 381 case option.JSON, option.JSONL: 382 s = tx.Palette.Render(option.NullEffect, IgnoredFlagPrefix+val.(*value.String).Raw()) 383 default: 384 s = tx.Palette.Render(option.StringEffect, val.(*value.String).Raw()) 385 } 386 case option.ExportDelimiterFlag: 387 switch tx.Flags.ExportOptions.Format { 388 case option.CSV: 389 s = tx.Palette.Render(option.StringEffect, val.(*value.String).String()) 390 default: 391 s = tx.Palette.Render(option.NullEffect, IgnoredFlagPrefix+val.(*value.String).String()) 392 } 393 case option.ExportDelimiterPositionsFlag: 394 switch tx.Flags.ExportOptions.Format { 395 case option.FIXED: 396 s = tx.Palette.Render(option.StringEffect, val.(*value.String).Raw()) 397 default: 398 s = tx.Palette.Render(option.NullEffect, IgnoredFlagPrefix+val.(*value.String).Raw()) 399 } 400 case option.WithoutHeaderFlag: 401 switch tx.Flags.ExportOptions.Format { 402 case option.CSV, option.TSV, option.FIXED, option.GFM, option.ORG: 403 if tx.Flags.ExportOptions.Format == option.FIXED && tx.Flags.ExportOptions.SingleLine { 404 s = tx.Palette.Render(option.NullEffect, IgnoredFlagPrefix+val.(*value.Boolean).String()) 405 } else { 406 s = tx.Palette.Render(option.BooleanEffect, val.(*value.Boolean).String()) 407 } 408 default: 409 s = tx.Palette.Render(option.NullEffect, IgnoredFlagPrefix+val.(*value.Boolean).String()) 410 } 411 case option.LineBreakFlag: 412 if tx.Flags.ExportOptions.Format == option.FIXED && tx.Flags.ExportOptions.SingleLine { 413 s = tx.Palette.Render(option.NullEffect, IgnoredFlagPrefix+val.(*value.String).Raw()) 414 } else { 415 s = tx.Palette.Render(option.StringEffect, val.(*value.String).Raw()) 416 } 417 case option.EncloseAllFlag: 418 switch tx.Flags.ExportOptions.Format { 419 case option.CSV, option.TSV: 420 s = tx.Palette.Render(option.BooleanEffect, val.(*value.Boolean).String()) 421 default: 422 s = tx.Palette.Render(option.NullEffect, IgnoredFlagPrefix+val.(*value.Boolean).String()) 423 } 424 case option.JsonEscapeFlag: 425 switch tx.Flags.ExportOptions.Format { 426 case option.JSON, option.JSONL: 427 s = tx.Palette.Render(option.StringEffect, val.(*value.String).Raw()) 428 default: 429 s = tx.Palette.Render(option.NullEffect, IgnoredFlagPrefix+val.(*value.String).Raw()) 430 } 431 case option.PrettyPrintFlag: 432 switch tx.Flags.ExportOptions.Format { 433 case option.JSON, option.JSONL: 434 s = tx.Palette.Render(option.BooleanEffect, val.(*value.Boolean).String()) 435 default: 436 s = tx.Palette.Render(option.NullEffect, IgnoredFlagPrefix+val.(*value.Boolean).String()) 437 } 438 case option.EastAsianEncodingFlag, option.CountDiacriticalSignFlag, option.CountFormatCodeFlag: 439 switch tx.Flags.ExportOptions.Format { 440 case option.GFM, option.ORG, option.BOX, option.TEXT: 441 s = tx.Palette.Render(option.BooleanEffect, val.(*value.Boolean).String()) 442 default: 443 s = tx.Palette.Render(option.NullEffect, IgnoredFlagPrefix+val.(*value.Boolean).String()) 444 } 445 case option.DelimiterFlag: 446 s = tx.Palette.Render(option.StringEffect, val.(*value.String).String()) 447 case option.TimezoneFlag, option.ImportFormatFlag, option.DelimiterPositionsFlag, option.EncodingFlag, option.FormatFlag: 448 s = tx.Palette.Render(option.StringEffect, val.(*value.String).Raw()) 449 case option.LimitRecursion: 450 p := val.(*value.Integer) 451 if p.Raw() < 0 { 452 s = tx.Palette.Render(option.NullEffect, "(no limit)") 453 } else { 454 s = tx.Palette.Render(option.NumberEffect, p.String()) 455 } 456 case option.CPUFlag: 457 s = tx.Palette.Render(option.NumberEffect, val.(*value.Integer).String()) 458 case option.WaitTimeoutFlag: 459 s = tx.Palette.Render(option.NumberEffect, val.(*value.Float).String()) 460 case option.AnsiQuotesFlag, option.StrictEqualFlag, option.AllowUnevenFieldsFlag, 461 option.NoHeaderFlag, option.WithoutNullFlag, option.StripEndingLineBreakFlag, option.ScientificNotationFlag, 462 option.ColorFlag, option.QuietFlag, option.StatsFlag: 463 s = tx.Palette.Render(option.BooleanEffect, val.(*value.Boolean).String()) 464 } 465 466 return s, true 467 } 468 469 func ShowObjects(scope *ReferenceScope, expr parser.ShowObjects) (string, error) { 470 var s string 471 472 w := scope.Tx.CreateDocumentWriter() 473 474 switch strings.ToUpper(expr.Type.Literal) { 475 case ShowTables: 476 keys := scope.Tx.CachedViews.SortedKeys() 477 478 if len(keys) < 1 { 479 s = scope.Tx.Warn("No table is loaded") 480 } else { 481 createdFiles, updatedFiles := scope.Tx.UncommittedViews.UncommittedFiles() 482 483 for _, key := range keys { 484 if view, ok := scope.Tx.CachedViews.Load(strings.ToUpper(key)); ok { 485 fields := view.Header.TableColumnNames() 486 info := view.FileInfo 487 ufpath := strings.ToUpper(info.Path) 488 489 if _, ok := createdFiles[ufpath]; ok { 490 w.WriteColor("*Created* ", option.EmphasisEffect) 491 } else if _, ok := updatedFiles[ufpath]; ok { 492 w.WriteColor("*Updated* ", option.EmphasisEffect) 493 } 494 w.WriteColorWithoutLineBreak(info.Path, option.ObjectEffect) 495 writeFields(w, fields) 496 497 w.NewLine() 498 writeTableAttribute(w, scope.Tx.Flags, info) 499 w.ClearBlock() 500 w.NewLine() 501 } 502 } 503 504 uncommitted := len(createdFiles) + len(updatedFiles) 505 506 w.Title1 = "Loaded Tables" 507 if 0 < uncommitted { 508 w.Title2 = fmt.Sprintf("(Uncommitted: %s)", FormatCount(uncommitted, "Table")) 509 w.Title2Effect = option.EmphasisEffect 510 } 511 s = "\n" + w.String() + "\n" 512 } 513 case ShowViews: 514 views := scope.AllTemporaryTables() 515 516 if views.Len() < 1 { 517 s = scope.Tx.Warn("No view is declared") 518 } else { 519 keys := views.SortedKeys() 520 521 updatedViews := scope.Tx.UncommittedViews.UncommittedTempViews() 522 523 for _, key := range keys { 524 if view, ok := views.Load(key); ok { 525 fields := view.Header.TableColumnNames() 526 info := view.FileInfo 527 ufpath := strings.ToUpper(info.Path) 528 529 if _, ok := updatedViews[ufpath]; ok { 530 w.WriteColor("*Updated* ", option.EmphasisEffect) 531 } 532 w.WriteColorWithoutLineBreak(info.Path, option.ObjectEffect) 533 writeFields(w, fields) 534 w.ClearBlock() 535 w.NewLine() 536 } 537 } 538 539 uncommitted := len(updatedViews) 540 541 w.Title1 = "Views" 542 if 0 < uncommitted { 543 w.Title2 = fmt.Sprintf("(Uncommitted: %s)", FormatCount(uncommitted, "View")) 544 w.Title2Effect = option.EmphasisEffect 545 } 546 s = "\n" + w.String() + "\n" 547 } 548 case ShowCursors: 549 cursors := scope.AllCursors() 550 if cursors.Len() < 1 { 551 s = scope.Tx.Warn("No cursor is declared") 552 } else { 553 keys := cursors.SortedKeys() 554 555 for _, key := range keys { 556 if cur, ok := cursors.Load(key); ok { 557 isOpen := cur.IsOpen() 558 559 w.WriteColor(cur.Name, option.ObjectEffect) 560 w.BeginBlock() 561 562 w.NewLine() 563 w.WriteColorWithoutLineBreak("Status: ", option.LableEffect) 564 if isOpen == ternary.TRUE { 565 nor, _ := cur.Count() 566 inRange, _ := cur.IsInRange() 567 position, _ := cur.Pointer() 568 569 norStr := option.FormatInt(nor, ",") 570 571 w.WriteColorWithoutLineBreak("Open", option.TernaryEffect) 572 w.WriteColorWithoutLineBreak(" Number of Rows: ", option.LableEffect) 573 w.WriteColorWithoutLineBreak(norStr, option.NumberEffect) 574 w.WriteSpaces(10 - len(norStr)) 575 w.WriteColorWithoutLineBreak("Pointer: ", option.LableEffect) 576 switch inRange { 577 case ternary.FALSE: 578 w.WriteColorWithoutLineBreak("Out of Range", option.TernaryEffect) 579 case ternary.UNKNOWN: 580 w.WriteColorWithoutLineBreak(inRange.String(), option.TernaryEffect) 581 default: 582 w.WriteColorWithoutLineBreak(option.FormatInt(position, ","), option.NumberEffect) 583 } 584 } else { 585 w.WriteColorWithoutLineBreak("Closed", option.TernaryEffect) 586 } 587 588 w.NewLine() 589 if cur.query.SelectEntity != nil { 590 w.WriteColor("Query:", option.LableEffect) 591 writeQuery(w, cur.query.String()) 592 } else { 593 w.WriteColorWithoutLineBreak("Statement: ", option.LableEffect) 594 w.WriteColorWithoutLineBreak(cur.statement.String(), option.IdentifierEffect) 595 } 596 597 w.ClearBlock() 598 w.NewLine() 599 } 600 } 601 w.Title1 = "Cursors" 602 s = "\n" + w.String() + "\n" 603 } 604 case ShowFunctions: 605 scalars, aggs := scope.AllFunctions() 606 if scalars.Len() < 1 && aggs.Len() < 1 { 607 s = scope.Tx.Warn("No function is declared") 608 } else { 609 if 0 < scalars.Len() { 610 w.Clear() 611 writeFunctions(w, scalars) 612 w.Title1 = "Scalar Functions" 613 s += "\n" + w.String() 614 } 615 if 0 < aggs.Len() { 616 w.Clear() 617 writeFunctions(w, aggs) 618 w.Title1 = "Aggregate Functions" 619 s += "\n" + w.String() + "\n" 620 } else { 621 s += "\n" 622 } 623 } 624 case ShowStatements: 625 if scope.Tx.PreparedStatements.Len() < 1 { 626 s = scope.Tx.Warn("No statement is prepared") 627 } else { 628 keys := scope.Tx.PreparedStatements.SortedKeys() 629 630 for _, key := range keys { 631 if stmt, ok := scope.Tx.PreparedStatements.Load(key); ok { 632 w.WriteColor(stmt.Name, option.ObjectEffect) 633 w.BeginBlock() 634 635 w.NewLine() 636 w.WriteColorWithoutLineBreak("Placeholder Number: ", option.LableEffect) 637 w.WriteColorWithoutLineBreak(strconv.Itoa(stmt.HolderNumber), option.NumberEffect) 638 w.NewLine() 639 w.WriteColorWithoutLineBreak("Statement:", option.LableEffect) 640 writeQuery(w, stmt.StatementString) 641 642 w.ClearBlock() 643 w.NewLine() 644 } 645 } 646 w.Title1 = "Prepared Statements" 647 s = "\n" + w.String() + "\n" 648 649 } 650 case ShowFlags: 651 for _, flag := range option.FlagList { 652 symbol := option.FlagSymbol(flag) 653 s, _ := showFlag(scope.Tx, flag) 654 w.WriteSpaces(27 - len(symbol)) 655 w.WriteColorWithoutLineBreak(symbol, option.LableEffect) 656 w.WriteColorWithoutLineBreak(":", option.LableEffect) 657 w.WriteSpaces(1) 658 w.WriteWithoutLineBreak(s) 659 w.NewLine() 660 } 661 w.Title1 = "Flags" 662 s = "\n" + w.String() + "\n" 663 case ShowEnv: 664 env := os.Environ() 665 names := make([]string, 0, len(env)) 666 vars := make([]string, 0, len(env)) 667 nameWidth := 0 668 669 for _, e := range env { 670 words := strings.Split(e, "=") 671 name := string(parser.VariableSign) + string(parser.EnvironmentVariableSign) + words[0] 672 if nameWidth < len(name) { 673 nameWidth = len(name) 674 } 675 676 var val string 677 if 1 < len(words) { 678 val = strings.Join(words[1:], "=") 679 } 680 vars = append(vars, val) 681 names = append(names, name) 682 } 683 684 for i, name := range names { 685 w.WriteSpaces(nameWidth - len(name)) 686 w.WriteColorWithoutLineBreak(name, option.LableEffect) 687 w.WriteColorWithoutLineBreak(":", option.LableEffect) 688 w.WriteSpaces(1) 689 w.WriteWithoutLineBreak(vars[i]) 690 w.NewLine() 691 } 692 w.Title1 = "Environment Variables" 693 s = "\n" + w.String() + "\n" 694 case ShowRuninfo: 695 for _, ri := range RuntimeInformatinList { 696 label := string(parser.VariableSign) + string(parser.RuntimeInformationSign) + ri 697 p, _ := GetRuntimeInformation(scope.Tx, parser.RuntimeInformation{Name: ri}) 698 699 w.WriteSpaces(19 - len(label)) 700 w.WriteColorWithoutLineBreak(label, option.LableEffect) 701 w.WriteColorWithoutLineBreak(":", option.LableEffect) 702 w.WriteSpaces(1) 703 switch ri { 704 case WorkingDirectory, VersionInformation: 705 w.WriteColorWithoutLineBreak(p.(*value.String).Raw(), option.StringEffect) 706 case UncommittedInformation: 707 w.WriteColorWithoutLineBreak(p.(*value.Boolean).String(), option.BooleanEffect) 708 default: 709 w.WriteColorWithoutLineBreak(p.(*value.Integer).String(), option.NumberEffect) 710 } 711 w.NewLine() 712 } 713 w.Title1 = "Runtime Information" 714 s = "\n" + w.String() + "\n" 715 default: 716 return "", NewShowInvalidObjectTypeError(expr, expr.Type.String()) 717 } 718 719 return s, nil 720 } 721 722 func writeTableAttribute(w *doc.Writer, flags *option.Flags, info *FileInfo) { 723 encWidth := option.TextWidth(info.Encoding.String(), flags) 724 725 w.WriteColor("Format: ", option.LableEffect) 726 w.WriteWithoutLineBreak(info.Format.String()) 727 728 w.WriteSpaces(encWidth + 4 - option.TextWidth(info.Format.String(), flags)) 729 switch info.Format { 730 case option.CSV: 731 w.WriteColorWithoutLineBreak("Delimiter: ", option.LableEffect) 732 w.WriteWithoutLineBreak("'" + option.EscapeString(string(info.Delimiter)) + "'") 733 case option.TSV: 734 w.WriteColorWithoutLineBreak("Delimiter: ", option.LableEffect) 735 w.WriteColorWithoutLineBreak("'\\t'", option.NullEffect) 736 case option.FIXED: 737 dp := info.DelimiterPositions.String() 738 if info.SingleLine { 739 dp = "S" + dp 740 } 741 742 w.WriteColorWithoutLineBreak("Delimiter Positions: ", option.LableEffect) 743 w.WriteWithoutLineBreak(dp) 744 case option.JSON, option.JSONL: 745 escapeStr := option.JsonEscapeTypeToString(info.JsonEscape) 746 w.WriteColorWithoutLineBreak("Escape: ", option.LableEffect) 747 w.WriteWithoutLineBreak(escapeStr) 748 749 spaces := 9 - len(escapeStr) 750 if spaces < 2 { 751 spaces = 2 752 } 753 w.WriteSpaces(spaces) 754 755 w.WriteColorWithoutLineBreak("Query: ", option.LableEffect) 756 if len(info.JsonQuery) < 1 { 757 w.WriteColorWithoutLineBreak("(empty)", option.NullEffect) 758 } else { 759 w.WriteColorWithoutLineBreak(info.JsonQuery, option.NullEffect) 760 } 761 } 762 763 switch info.Format { 764 case option.CSV, option.TSV: 765 w.WriteSpaces(4 - (option.TextWidth(option.EscapeString(string(info.Delimiter)), flags))) 766 w.WriteColorWithoutLineBreak("Enclose All: ", option.LableEffect) 767 w.WriteWithoutLineBreak(strconv.FormatBool(info.EncloseAll)) 768 } 769 770 w.NewLine() 771 772 w.WriteColor("Encoding: ", option.LableEffect) 773 switch info.Format { 774 case option.JSON, option.JSONL: 775 w.WriteColorWithoutLineBreak(text.UTF8.String(), option.NullEffect) 776 default: 777 w.WriteWithoutLineBreak(info.Encoding.String()) 778 } 779 780 if !(info.Format == option.FIXED && info.SingleLine) { 781 w.WriteSpaces(encWidth + 2 - (option.TextWidth(info.Encoding.String(), flags))) 782 w.WriteColorWithoutLineBreak("LineBreak: ", option.LableEffect) 783 w.WriteWithoutLineBreak(info.LineBreak.String()) 784 } 785 786 switch info.Format { 787 case option.JSON, option.JSONL: 788 w.WriteSpaces(6 - (option.TextWidth(info.LineBreak.String(), flags))) 789 w.WriteColorWithoutLineBreak("Pretty Print: ", option.LableEffect) 790 w.WriteWithoutLineBreak(strconv.FormatBool(info.PrettyPrint)) 791 case option.CSV, option.TSV, option.FIXED, option.GFM, option.ORG: 792 if !(info.Format == option.FIXED && info.SingleLine) { 793 w.WriteSpaces(6 - (option.TextWidth(info.LineBreak.String(), flags))) 794 w.WriteColorWithoutLineBreak("Header: ", option.LableEffect) 795 w.WriteWithoutLineBreak(strconv.FormatBool(!info.NoHeader)) 796 } 797 } 798 } 799 800 func writeFields(w *doc.Writer, fields []string) { 801 w.BeginBlock() 802 w.NewLine() 803 w.WriteColor("Fields: ", option.LableEffect) 804 w.BeginSubBlock() 805 lastIdx := len(fields) - 1 806 for i, f := range fields { 807 escaped := option.EscapeIdentifier(f) 808 if i < lastIdx && !w.FitInLine(escaped+", ") { 809 w.NewLine() 810 } 811 w.WriteColor(escaped, option.AttributeEffect) 812 if i < lastIdx { 813 w.WriteWithoutLineBreak(", ") 814 } 815 } 816 w.EndSubBlock() 817 } 818 819 func writeFunctions(w *doc.Writer, funcs UserDefinedFunctionMap) { 820 keys := funcs.SortedKeys() 821 822 for _, key := range keys { 823 if fn, ok := funcs.Load(key); ok { 824 w.WriteColor(fn.Name.String(), option.ObjectEffect) 825 w.WriteWithoutLineBreak(" (") 826 827 if fn.IsAggregate { 828 w.WriteColorWithoutLineBreak(fn.Cursor.String(), option.IdentifierEffect) 829 if 0 < len(fn.Parameters) { 830 w.WriteWithoutLineBreak(", ") 831 } 832 } 833 834 for i, p := range fn.Parameters { 835 if 0 < i { 836 w.WriteWithoutLineBreak(", ") 837 } 838 if def, ok := fn.Defaults[p.Name]; ok { 839 w.WriteColorWithoutLineBreak(p.String(), option.AttributeEffect) 840 w.WriteWithoutLineBreak(" = ") 841 w.WriteColorWithoutLineBreak(def.String(), option.ValueEffect) 842 } else { 843 w.WriteColorWithoutLineBreak(p.String(), option.AttributeEffect) 844 } 845 } 846 847 w.WriteWithoutLineBreak(")") 848 w.ClearBlock() 849 w.NewLine() 850 } 851 } 852 } 853 854 func ShowFields(ctx context.Context, scope *ReferenceScope, expr parser.ShowFields) (string, error) { 855 var tableName = func(expr parser.QueryExpression) string { 856 switch e := expr.(type) { 857 case parser.Identifier: 858 return e.Literal 859 case parser.Stdin: 860 return e.String() 861 case HttpObject: 862 return "Remote Object" 863 case DataObject: 864 return "String Object" 865 default: 866 return "Inline Table" 867 } 868 } 869 870 if !strings.EqualFold(expr.Type.Literal, "FIELDS") { 871 return "", NewShowInvalidObjectTypeError(expr, expr.Type.Literal) 872 } 873 874 var status = ObjectFixed 875 876 queryScope := scope.CreateNode() 877 defer queryScope.CloseCurrentNode() 878 879 view, err := LoadViewFromTableIdentifier(ctx, queryScope, expr.Table, false, false) 880 if err != nil { 881 return "", err 882 } 883 884 if view.FileInfo.IsInMemoryTable() { 885 updatedViews := scope.Tx.UncommittedViews.UncommittedTempViews() 886 ufpath := view.FileInfo.IdentifiedPath() 887 888 if _, ok := updatedViews[ufpath]; ok { 889 status = ObjectUpdated 890 } 891 } else if view.FileInfo.IsFile() { 892 createdViews, updatedView := scope.Tx.UncommittedViews.UncommittedFiles() 893 ufpath := view.FileInfo.IdentifiedPath() 894 895 if _, ok := createdViews[ufpath]; ok { 896 status = ObjectCreated 897 } else if _, ok := updatedView[ufpath]; ok { 898 status = ObjectUpdated 899 } 900 } else { 901 status = ReadOnly 902 } 903 904 w := scope.Tx.CreateDocumentWriter() 905 w.WriteColorWithoutLineBreak("Type: ", option.LableEffect) 906 if view.FileInfo.IsTemporaryTable() { 907 w.WriteWithoutLineBreak("Temporary Table") 908 } else if view.FileInfo.IsStdin() { 909 w.WriteWithoutLineBreak("STDIN") 910 } else if view.FileInfo.IsFile() { 911 w.WriteWithoutLineBreak("File") 912 w.NewLine() 913 w.WriteColorWithoutLineBreak("Path: ", option.LableEffect) 914 w.WriteColorWithoutLineBreak(view.FileInfo.Path, option.ObjectEffect) 915 } else if view.FileInfo.IsRemoteObject() { 916 w.WriteWithoutLineBreak("Remote Object") 917 w.NewLine() 918 w.WriteColorWithoutLineBreak(" URL: ", option.LableEffect) 919 w.WriteColorWithoutLineBreak(view.FileInfo.Path, option.ObjectEffect) 920 } else if view.FileInfo.IsStringObject() { 921 w.WriteWithoutLineBreak("String Object") 922 } else if view.FileInfo.IsInlineTable() { 923 w.WriteWithoutLineBreak("Inline Table") 924 } 925 926 if !view.FileInfo.IsTemporaryTable() { 927 w.NewLine() 928 writeTableAttribute(w, scope.Tx.Flags, view.FileInfo) 929 } 930 931 if b, ok := ctx.Value("CallFromSubcommand").(bool); !ok || !b { 932 w.NewLine() 933 w.WriteColorWithoutLineBreak("Status: ", option.LableEffect) 934 switch status { 935 case ObjectCreated: 936 w.WriteColorWithoutLineBreak("Created", option.EmphasisEffect) 937 case ObjectUpdated: 938 w.WriteColorWithoutLineBreak("Updated", option.EmphasisEffect) 939 case ReadOnly: 940 w.WriteWithoutLineBreak("Read-Only") 941 default: 942 w.WriteWithoutLineBreak("Fixed") 943 } 944 } 945 946 w.NewLine() 947 writeFieldList(w, view.Header.TableColumnNames()) 948 949 w.Title1 = "Fields in" 950 tablePath := func() parser.QueryExpression { 951 if e, ok := expr.Table.(parser.FormatSpecifiedFunction); ok { 952 return e.Path 953 } 954 return expr.Table 955 }() 956 tableObject, err := NormalizeTableObject(ctx, scope, tablePath) 957 if err != nil { 958 return "", nil 959 } 960 w.Title2 = tableName(tableObject) 961 w.Title2Effect = option.IdentifierEffect 962 return "\n" + w.String() + "\n", nil 963 } 964 965 func writeFieldList(w *doc.Writer, fields []string) { 966 l := len(fields) 967 digits := len(strconv.Itoa(l)) 968 fieldNumbers := make([]string, 0, l) 969 for i := 0; i < l; i++ { 970 idxstr := strconv.Itoa(i + 1) 971 fieldNumbers = append(fieldNumbers, strings.Repeat(" ", digits-len(idxstr))+idxstr) 972 } 973 974 w.WriteColorWithoutLineBreak("Fields:", option.LableEffect) 975 w.NewLine() 976 w.WriteSpaces(2) 977 w.BeginSubBlock() 978 for i := 0; i < l; i++ { 979 w.WriteColor(fieldNumbers[i], option.NumberEffect) 980 w.Write(".") 981 w.WriteSpaces(1) 982 w.WriteColorWithoutLineBreak(fields[i], option.AttributeEffect) 983 w.NewLine() 984 } 985 } 986 987 func writeQuery(w *doc.Writer, s string) { 988 w.NewLine() 989 w.WriteSpaces(2) 990 w.BeginSubBlock() 991 w.WriteWithAutoLineBreakWithContinueMark(s) 992 w.EndSubBlock() 993 } 994 995 func SetEnvVar(ctx context.Context, scope *ReferenceScope, expr parser.SetEnvVar) error { 996 var p value.Primary 997 var err error 998 999 if ident, ok := expr.Value.(parser.Identifier); ok { 1000 p = value.NewString(ident.Literal) 1001 defer value.Discard(p) 1002 } else { 1003 p, err = Evaluate(ctx, scope, expr.Value) 1004 if err != nil { 1005 return err 1006 } 1007 } 1008 1009 var val string 1010 if s := value.ToString(p); !value.IsNull(s) { 1011 val = s.(*value.String).Raw() 1012 value.Discard(s) 1013 } 1014 return os.Setenv(expr.EnvVar.Name, val) 1015 } 1016 1017 func UnsetEnvVar(expr parser.UnsetEnvVar) error { 1018 return os.Unsetenv(expr.EnvVar.Name) 1019 } 1020 1021 func Chdir(ctx context.Context, scope *ReferenceScope, expr parser.Chdir) error { 1022 var dirpath string 1023 var err error 1024 1025 if ident, ok := expr.DirPath.(parser.Identifier); ok { 1026 dirpath = ident.Literal 1027 } else { 1028 p, err := Evaluate(ctx, scope, expr.DirPath) 1029 if err != nil { 1030 return err 1031 } 1032 s := value.ToString(p) 1033 if value.IsNull(s) { 1034 return NewInvalidPathError(expr, expr.DirPath.String(), "invalid directory path") 1035 } 1036 dirpath = s.(*value.String).Raw() 1037 value.Discard(s) 1038 } 1039 1040 if err = os.Chdir(dirpath); err != nil { 1041 if patherr, ok := err.(*os.PathError); ok { 1042 err = NewInvalidPathError(expr, patherr.Path, patherr.Err.Error()) 1043 } 1044 } 1045 return err 1046 } 1047 1048 func Pwd(expr parser.Pwd) (string, error) { 1049 dirpath, err := os.Getwd() 1050 if err != nil { 1051 if patherr, ok := err.(*os.PathError); ok { 1052 err = NewInvalidPathError(expr, patherr.Path, patherr.Err.Error()) 1053 } 1054 } 1055 return dirpath, err 1056 } 1057 1058 func Reload(ctx context.Context, tx *Transaction, expr parser.Reload) error { 1059 tx.operationMutex.Lock() 1060 defer tx.operationMutex.Unlock() 1061 1062 switch strings.ToUpper(expr.Type.Literal) { 1063 case ReloadConfig: 1064 if err := tx.Environment.Load(ctx, tx.WaitTimeout, tx.RetryDelay); err != nil { 1065 return NewLoadConfigurationError(expr, err.Error()) 1066 } 1067 1068 for _, v := range tx.Environment.DatetimeFormat { 1069 tx.Flags.DatetimeFormat = option.AppendStrIfNotExist(tx.Flags.DatetimeFormat, v) 1070 } 1071 1072 palette, err := color.GeneratePalette(tx.Environment.Palette) 1073 if err != nil { 1074 return NewLoadConfigurationError(expr, err.Error()) 1075 } 1076 tx.Palette.Merge(palette) 1077 1078 if tx.Session.Terminal() != nil { 1079 if err := tx.Session.Terminal().ReloadConfig(); err != nil { 1080 return NewLoadConfigurationError(expr, err.Error()) 1081 } 1082 } 1083 1084 default: 1085 return NewInvalidReloadTypeError(expr, expr.Type.Literal) 1086 } 1087 return nil 1088 } 1089 1090 func Syntax(ctx context.Context, scope *ReferenceScope, expr parser.Syntax) (string, error) { 1091 keys := make([]string, 0, len(expr.Keywords)) 1092 for _, key := range expr.Keywords { 1093 var keystr string 1094 if fr, ok := key.(parser.FieldReference); ok { 1095 if col, ok := fr.Column.(parser.Identifier); ok { 1096 keystr = col.Literal 1097 } 1098 } else { 1099 if p, err := Evaluate(ctx, scope, key); err == nil { 1100 if s := value.ToString(p); !value.IsNull(s) { 1101 keystr = s.(*value.String).Raw() 1102 value.Discard(s) 1103 } 1104 } 1105 } 1106 1107 if 0 < len(keystr) { 1108 words := strings.Split(option.TrimSpace(keystr), " ") 1109 for _, w := range words { 1110 w = option.TrimSpace(w) 1111 if 0 < len(w) { 1112 keys = append(keys, w) 1113 } 1114 } 1115 } 1116 } 1117 1118 store := syntax.NewStore() 1119 exps := store.Search(keys) 1120 1121 w := scope.Tx.CreateDocumentWriter() 1122 1123 for _, exp := range exps { 1124 w.WriteColor(exp.Label, option.LableEffect) 1125 w.NewLine() 1126 if len(exps) < 4 { 1127 w.BeginBlock() 1128 1129 if 0 < len(exp.Description.Template) { 1130 w.WriteWithAutoLineBreak(exp.Description.Format(scope.Tx.Palette)) 1131 w.NewLine() 1132 w.NewLine() 1133 } 1134 1135 for _, def := range exp.Grammar { 1136 w.Write(def.Name.Format(scope.Tx.Palette)) 1137 w.NewLine() 1138 w.BeginBlock() 1139 for i, gram := range def.Group { 1140 if i == 0 { 1141 w.Write(": ") 1142 } else { 1143 w.Write("| ") 1144 } 1145 w.BeginSubBlock() 1146 w.WriteWithAutoLineBreak(gram.Format(scope.Tx.Palette)) 1147 w.EndSubBlock() 1148 w.NewLine() 1149 } 1150 1151 if 0 < len(def.Description.Template) { 1152 if 0 < len(def.Group) { 1153 w.NewLine() 1154 } 1155 w.WriteWithAutoLineBreak(def.Description.Format(scope.Tx.Palette)) 1156 w.NewLine() 1157 } 1158 1159 w.EndBlock() 1160 w.NewLine() 1161 } 1162 w.EndBlock() 1163 } 1164 1165 if 0 < len(exp.Children) && (len(keys) < 1 || strings.EqualFold(exp.Label, strings.Join(keys, " "))) { 1166 w.BeginBlock() 1167 for _, child := range exp.Children { 1168 w.WriteColor(child.Label, option.LableEffect) 1169 w.NewLine() 1170 } 1171 } 1172 1173 w.ClearBlock() 1174 } 1175 1176 if len(keys) < 1 { 1177 w.Title1 = "Contents" 1178 } else { 1179 w.Title1 = "Search: " + strings.Join(keys, " ") 1180 } 1181 return "\n" + w.String() + "\n", nil 1182 1183 }