github.com/mithrandie/csvq@v1.18.1/lib/terminal/completer_readline.go (about) 1 //go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows 2 3 package terminal 4 5 import ( 6 "os" 7 "path/filepath" 8 "sort" 9 "strings" 10 "unicode" 11 12 "github.com/mithrandie/csvq/lib/constant" 13 "github.com/mithrandie/csvq/lib/option" 14 "github.com/mithrandie/csvq/lib/parser" 15 "github.com/mithrandie/csvq/lib/query" 16 17 "github.com/mithrandie/go-text" 18 "github.com/mithrandie/readline-csvq" 19 "github.com/mithrandie/ternary" 20 ) 21 22 const ( 23 dummySubquery = "____subquery____" 24 dummyTableObject = "____table_object____" 25 dummyTable = "____table____" 26 ) 27 28 var statementPrefix = []string{ 29 "WITH", 30 "SELECT", 31 "INSERT", 32 "UPDATE", 33 "REPLACE", 34 "DELETE", 35 "CREATE", 36 "ALTER", 37 "DECLARE", 38 "PREPARE", 39 "VAR", 40 "SET", 41 "UNSET", 42 "ADD", 43 "REMOVE", 44 "ECHO", 45 "PRINT", 46 "PRINTF", 47 "CHDIR", 48 "EXECUTE", 49 "SHOW", 50 "SOURCE", 51 "SYNTAX", 52 "RELOAD", 53 } 54 55 var singleCommandStatement = []string{ 56 "COMMIT", 57 "ROLLBACK", 58 "EXIT", 59 "PWD", 60 } 61 62 var delimiterCandidates = []string{ 63 "','", 64 "'\\t'", 65 } 66 67 var delimiterPositionsCandidates = []string{ 68 "'SPACES'", 69 "'S[]'", 70 "'[]'", 71 } 72 73 var joinCandidates = []string{ 74 "JOIN", 75 "CROSS", 76 "INNER", 77 "FULL", 78 "LEFT", 79 "RIGHT", 80 "NATURAL", 81 } 82 83 var tableObjectCandidates = []string{ 84 "CSV()", 85 "FIXED()", 86 "JSON()", 87 "JSONL()", 88 "LTSV()", 89 } 90 91 var exportEncodingsCandidates = []string{ 92 "SJIS", 93 "UTF16", 94 "UTF16BE", 95 "UTF16BEM", 96 "UTF16LE", 97 "UTF16LEM", 98 "UTF8", 99 "UTF8M", 100 } 101 102 type ReadlineListener struct { 103 scanner parser.Scanner 104 } 105 106 func skipInputtingEnclosure(line []rune, pos int) []rune { 107 tail := line[pos:] 108 line = append(line[:pos-1], tail...) 109 return line 110 } 111 112 func completeEnclosure(line []rune, pos int, rightEnclosure rune) []rune { 113 tail := append([]rune{rightEnclosure}, line[pos:]...) 114 line = append(line[:pos], tail...) 115 return line 116 } 117 118 func (l ReadlineListener) OnChange(line []rune, pos int, key rune) ([]rune, int, bool) { 119 switch { 120 case readline.IsQuotationMark(key): 121 if !readline.LiteralIsEnclosed(key, line) { 122 if pos < len(line) && key == line[pos] { 123 return skipInputtingEnclosure(line, pos), pos, true 124 } else { 125 return completeEnclosure(line, pos, key), pos, true 126 } 127 } 128 case readline.IsBracket(key): 129 if !readline.BracketIsEnclosed(key, line) { 130 return completeEnclosure(line, pos, readline.RightBracket[key]), pos, true 131 } 132 case readline.IsRightBracket(key): 133 if pos < len(line) && readline.IsRightBracket(line[pos]) && readline.BracketIsEnclosedByRightBracket(key, line) { 134 return skipInputtingEnclosure(line, pos), pos, true 135 } 136 } 137 138 return line, pos, false 139 } 140 141 type Completer struct { 142 completer *readline.PrefixCompleter 143 scope *query.ReferenceScope 144 145 flagList []string 146 runinfoList []string 147 funcs []string 148 aggFuncs []string 149 analyticFuncs []string 150 151 constants []string 152 153 statementList []string 154 userFuncs []string 155 userAggFuncs []string 156 userFuncList []string 157 viewList []string 158 cursorList []string 159 funcList []string 160 aggFuncList []string 161 analyticFuncList []string 162 varList []string 163 envList []string 164 enclosedEnvList []string 165 allColumns []string 166 tableColumns map[string][]string 167 168 tokens []parser.Token 169 lastIdx int 170 selectIntoEnabled bool 171 172 isInAndAfterSelect bool 173 } 174 175 func NewCompleter(scope *query.ReferenceScope) *Completer { 176 completer := &Completer{ 177 completer: readline.NewPrefixCompleter(), 178 scope: scope, 179 tableColumns: make(map[string][]string), 180 } 181 182 completer.flagList = make([]string, 0, len(option.FlagList)) 183 for _, v := range option.FlagList { 184 completer.flagList = append(completer.flagList, option.FlagSymbol(v)) 185 } 186 completer.runinfoList = make([]string, 0, len(query.RuntimeInformatinList)) 187 for _, v := range query.RuntimeInformatinList { 188 completer.runinfoList = append(completer.runinfoList, option.RuntimeInformationSymbol(v)) 189 } 190 191 sort.Strings(completer.flagList) 192 sort.Strings(completer.runinfoList) 193 194 completer.funcs = make([]string, 0, len(query.Functions)+3) 195 for k := range query.Functions { 196 completer.funcs = append(completer.funcs, k) 197 } 198 completer.funcs = append(completer.funcs, "CALL") 199 completer.funcs = append(completer.funcs, "NOW") 200 completer.funcs = append(completer.funcs, "JSON_OBJECT") 201 202 completer.aggFuncs = make([]string, 0, len(query.AggregateFunctions)+2) 203 completer.analyticFuncs = make([]string, 0, len(query.AnalyticFunctions)+len(query.AggregateFunctions)) 204 for k := range query.AggregateFunctions { 205 completer.aggFuncs = append(completer.aggFuncs, k) 206 completer.analyticFuncs = append(completer.analyticFuncs, k) 207 } 208 completer.aggFuncs = append(completer.aggFuncs, "LISTAGG") 209 completer.aggFuncs = append(completer.aggFuncs, "JSON_AGG") 210 for k := range query.AnalyticFunctions { 211 completer.analyticFuncs = append(completer.analyticFuncs, k) 212 } 213 214 completer.constants = make([]string, 0, constant.Count()) 215 for category, valueMap := range constant.Definition { 216 for name := range valueMap { 217 completer.constants = append(completer.constants, category+parser.ConstantDelimiter+name) 218 } 219 } 220 sort.Strings(completer.constants) 221 222 completer.tokens = make([]parser.Token, 0, 20) 223 224 return completer 225 } 226 227 func (c *Completer) Do(line []rune, pos int, index int) (readline.CandidateList, int) { 228 return c.completer.Do(line, pos, index) 229 } 230 231 func (c *Completer) Update() { 232 c.updateStatements() 233 c.updateViews() 234 c.updateCursors() 235 c.updateFunctions() 236 c.updateVariables() 237 c.updateEnvironmentVariables() 238 c.updateAllColumns() 239 240 completer := readline.NewPrefixCompleter() 241 statements := readline.PcItemDynamic(c.Statements) 242 statements.AppendOnly = true 243 completer.SetChildren([]readline.PrefixCompleterInterface{statements}) 244 c.completer = completer 245 } 246 247 func (c *Completer) updateStatements() { 248 c.statementList = make([]string, 0, 10) 249 c.scope.Tx.PreparedStatements.Range(func(key, value interface{}) bool { 250 c.statementList = append(c.statementList, value.(*query.PreparedStatement).Name) 251 return true 252 }) 253 sort.Strings(c.statementList) 254 } 255 256 func (c *Completer) updateViews() { 257 views := c.scope.AllTemporaryTables() 258 viewKeys := views.SortedKeys() 259 c.viewList = make([]string, 0, len(viewKeys)) 260 for _, key := range viewKeys { 261 if view, ok := views.Load(key); ok { 262 c.viewList = append(c.viewList, view.FileInfo.Path) 263 } 264 } 265 } 266 267 func (c *Completer) updateCursors() { 268 cursors := c.scope.AllCursors() 269 cursorKeys := cursors.SortedKeys() 270 c.cursorList = make([]string, 0, len(cursorKeys)) 271 for _, key := range cursorKeys { 272 if cur, ok := cursors.Load(key); ok { 273 c.cursorList = append(c.cursorList, cur.Name) 274 } 275 } 276 } 277 278 func (c *Completer) updateFunctions() { 279 userfuncs, userAggFuncs := c.scope.AllFunctions() 280 c.userFuncs = make([]string, 0, userfuncs.Len()) 281 c.userAggFuncs = make([]string, 0, userAggFuncs.Len()) 282 283 funcKeys := make([]string, 0, len(c.funcs)+userfuncs.Len()) 284 for _, v := range c.funcs { 285 funcKeys = append(funcKeys, v+"()") 286 } 287 userfuncs.Range(func(key, value interface{}) bool { 288 f := value.(*query.UserDefinedFunction) 289 funcKeys = append(funcKeys, f.Name.String()+"()") 290 c.userFuncs = append(c.userFuncs, f.Name.String()) 291 return true 292 }) 293 sort.Strings(funcKeys) 294 c.funcList = funcKeys 295 296 aggFuncKeys := make([]string, 0, len(c.aggFuncs)+userAggFuncs.Len()) 297 for _, v := range c.aggFuncs { 298 aggFuncKeys = append(aggFuncKeys, v+"()") 299 } 300 userAggFuncs.Range(func(key, value interface{}) bool { 301 f := value.(*query.UserDefinedFunction) 302 aggFuncKeys = append(aggFuncKeys, f.Name.String()+"()") 303 c.userAggFuncs = append(c.userAggFuncs, f.Name.String()) 304 return true 305 }) 306 sort.Strings(aggFuncKeys) 307 c.aggFuncList = aggFuncKeys 308 309 c.userFuncList = append(c.userFuncs, c.userAggFuncs...) 310 sort.Strings(c.userFuncList) 311 312 analyticFuncKeys := make([]string, 0, len(c.analyticFuncs)+userAggFuncs.Len()) 313 for _, v := range c.analyticFuncs { 314 analyticFuncKeys = append(analyticFuncKeys, v+"() OVER ()") 315 } 316 userAggFuncs.Range(func(key, value interface{}) bool { 317 f := value.(*query.UserDefinedFunction) 318 analyticFuncKeys = append(analyticFuncKeys, f.Name.String()+"() OVER ()") 319 return true 320 }) 321 sort.Strings(analyticFuncKeys) 322 c.analyticFuncList = analyticFuncKeys 323 } 324 325 func (c *Completer) updateVariables() { 326 vars := c.scope.AllVariables() 327 varKeys := vars.SortedKeys() 328 329 c.varList = make([]string, 0, len(varKeys)) 330 for _, k := range varKeys { 331 c.varList = append(c.varList, option.VariableSymbol(k)) 332 } 333 } 334 335 func (c *Completer) updateEnvironmentVariables() { 336 env := os.Environ() 337 c.envList = make([]string, 0, len(env)) 338 c.enclosedEnvList = make([]string, 0, len(env)) 339 for _, e := range env { 340 words := strings.Split(e, "=") 341 c.envList = append(c.envList, option.EnvironmentVariableSymbol(words[0])) 342 c.enclosedEnvList = append(c.enclosedEnvList, option.EnclosedEnvironmentVariableSymbol(words[0])) 343 } 344 sort.Strings(c.envList) 345 sort.Strings(c.enclosedEnvList) 346 } 347 348 func (c *Completer) updateAllColumns() { 349 c.allColumns = c.AllColumnList() 350 } 351 352 func (c *Completer) GetStatementPrefix(line string, origLine string, index int) readline.CandidateList { 353 prefix := statementPrefix 354 if 0 < len(c.cursorList) || 0 < len(c.userFuncList) || 0 < len(c.viewList) || 0 < len(c.varList) || 0 < len(c.statementList) { 355 prefix = append(prefix, "DISPOSE") 356 } 357 if 0 < len(c.cursorList) { 358 prefix = append(prefix, 359 "OPEN", 360 "CLOSE", 361 "FETCH", 362 ) 363 } 364 365 var cands readline.CandidateList 366 for _, p := range prefix { 367 cands = append(cands, c.candidate(p, true)) 368 } 369 for _, p := range singleCommandStatement { 370 cands = append(cands, c.candidate(p, false)) 371 } 372 cands = append(cands, c.SearchValues(line, origLine, index)...) 373 374 cands.Sort() 375 return cands 376 } 377 378 func (c *Completer) Statements(line string, origLine string, index int) readline.CandidateList { 379 origRunes := []rune(origLine) 380 c.UpdateTokens(line, string(origRunes[:index])) 381 382 token := parser.EOF 383 if (1 == len(c.tokens) && unicode.IsSpace(origRunes[index-1])) || 1 < len(c.tokens) { 384 token = c.tokens[0].Token 385 } 386 387 switch token { 388 case parser.WITH: 389 return c.WithArgs(line, origLine, index) 390 case parser.SELECT: 391 return c.SelectArgs(line, origLine, index) 392 case parser.INSERT: 393 return c.InsertArgs(line, origLine, index) 394 case parser.UPDATE: 395 return c.UpdateArgs(line, origLine, index) 396 case parser.REPLACE: 397 return c.ReplaceArgs(line, origLine, index) 398 case parser.DELETE: 399 return c.DeleteArgs(line, origLine, index) 400 case parser.CREATE: 401 return c.CreateArgs(line, origLine, index) 402 case parser.ALTER: 403 return c.AlterArgs(line, origLine, index) 404 case parser.DECLARE, parser.VAR: 405 return c.DeclareArgs(line, origLine, index) 406 case parser.PREPARE: 407 return c.PrepareArgs(line, origLine, index) 408 case parser.SET: 409 return c.SetArgs(line, origLine, index) 410 case parser.UNSET: 411 return c.candidateList(c.environmentVariableList(line), false) 412 case parser.ADD: 413 return c.AddFlagArgs(line, origLine, index) 414 case parser.REMOVE: 415 return c.RemoveFlagArgs(line, origLine, index) 416 case parser.ECHO: 417 return c.SearchValues(line, origLine, index) 418 case parser.PRINT: 419 return c.SearchValues(line, origLine, index) 420 case parser.PRINTF: 421 return c.UsingArgs(line, origLine, index) 422 case parser.CHDIR: 423 return c.SearchDirs(line, origLine, index) 424 case parser.EXECUTE: 425 return c.UsingArgs(line, origLine, index) 426 case parser.SHOW: 427 return c.ShowArgs(line, origLine, index) 428 case parser.SOURCE: 429 return c.SearchExecutableFiles(line, origLine, index) 430 case parser.RELOAD: 431 if 0 < len(line) && len(c.tokens) == 2 || len(line) < 1 && len(c.tokens) == 1 { 432 return readline.CandidateList{c.candidate("CONFIG", false)} 433 } else { 434 return nil 435 } 436 case parser.DISPOSE: 437 return c.DisposeArgs(line, origLine, index) 438 case parser.OPEN: 439 return c.UsingArgs(line, origLine, index) 440 case parser.CLOSE: 441 return c.candidateList(c.cursorList, false) 442 case parser.FETCH: 443 return c.FetchArgs(line, origLine, index) 444 case parser.COMMIT: 445 return nil 446 case parser.ROLLBACK: 447 return nil 448 case parser.EXIT: 449 return nil 450 case parser.PWD: 451 return nil 452 case parser.SYNTAX: 453 return nil 454 case parser.EOF: 455 return c.GetStatementPrefix(line, origLine, index) 456 default: 457 if 0 < len(c.tokens) { 458 switch { 459 case c.isTableObject(c.tokens[0]): 460 return c.TableObjectArgs(line, origLine, index) 461 case c.isFunction(c.tokens[0]): 462 return c.FunctionArgs(line, origLine, index) 463 } 464 } 465 } 466 467 cands := c.SearchValues(line, origLine, index) 468 cands.Sort() 469 return cands 470 } 471 472 func (c *Completer) TableObjectArgs(line string, origLine string, index int) readline.CandidateList { 473 commaCnt := 0 474 for _, t := range c.tokens { 475 if t.Token == ',' { 476 commaCnt++ 477 } 478 } 479 480 var cands readline.CandidateList 481 482 switch strings.ToUpper(c.tokens[0].Literal) { 483 case "LTSV": 484 switch commaCnt { 485 case 0: 486 if c.tokens[c.lastIdx].Token == '(' { 487 cands = c.SearchAllTables(line, origLine, index) 488 } 489 case 1: 490 if c.tokens[c.lastIdx].Token == ',' { 491 cands = c.candidateList(c.encodingList(), false) 492 } 493 case 2: 494 if c.tokens[c.lastIdx].Token == ',' { 495 cands = c.candidateList([]string{ternary.TRUE.String(), ternary.FALSE.String()}, false) 496 } 497 } 498 default: 499 switch commaCnt { 500 case 0: 501 if c.tokens[c.lastIdx].Token == '(' { 502 switch strings.ToUpper(c.tokens[0].Literal) { 503 case option.CSV.String(): 504 cands = c.candidateList(delimiterCandidates, false) 505 case option.FIXED.String(): 506 cands = c.candidateList(delimiterPositionsCandidates, false) 507 } 508 } 509 case 1: 510 if c.tokens[c.lastIdx].Token == ',' { 511 cands = c.SearchAllTables(line, origLine, index) 512 } 513 case 2: 514 if c.tokens[c.lastIdx].Token == ',' { 515 switch strings.ToUpper(c.tokens[0].Literal) { 516 case option.CSV.String(), option.FIXED.String(): 517 cands = c.candidateList(c.encodingList(), false) 518 } 519 } 520 case 3, 4: 521 if c.tokens[c.lastIdx].Token == ',' { 522 switch strings.ToUpper(c.tokens[0].Literal) { 523 case option.CSV.String(), option.FIXED.String(): 524 cands = c.candidateList([]string{ternary.TRUE.String(), ternary.FALSE.String()}, false) 525 } 526 } 527 } 528 } 529 530 cands = append(cands, c.SearchValues(line, origLine, index)...) 531 return cands 532 } 533 534 func (c *Completer) FunctionArgs(line string, origLine string, index int) readline.CandidateList { 535 if c.tokens[0].Token == parser.SUBSTRING { 536 return c.substringArgs(line, origLine, index) 537 } else { 538 return c.functionArgs(line, origLine, index) 539 } 540 } 541 542 func (c *Completer) substringArgs(line string, origLine string, index int) readline.CandidateList { 543 return c.completeArgs( 544 line, 545 origLine, 546 index, 547 func(i int) (keywords []string, customList readline.CandidateList, breakLoop bool) { 548 customList = append(customList, c.SearchValues(line, origLine, index)...) 549 customList.Sort() 550 551 switch c.tokens[i].Token { 552 case parser.FOR: 553 //Do nothing 554 case parser.FROM: 555 if i < c.lastIdx { 556 keywords = append(keywords, "FOR") 557 } 558 case parser.SUBSTRING: 559 if i < c.lastIdx-1 { 560 keywords = append(keywords, "FROM") 561 } 562 default: 563 return keywords, customList, false 564 } 565 566 return keywords, customList, true 567 }, 568 ) 569 } 570 571 func (c *Completer) functionArgs(line string, origLine string, index int) readline.CandidateList { 572 return c.completeArgs( 573 line, 574 origLine, 575 index, 576 func(i int) (keywords []string, customList readline.CandidateList, breakLoop bool) { 577 switch c.tokens[i].Token { 578 case parser.BETWEEN: 579 if i == c.lastIdx { 580 customList = append(customList, c.candidateList([]string{ 581 "UNBOUNDED PRECEDING", 582 "CURRENT ROW", 583 }, true)...) 584 } else if i == c.lastIdx-1 { 585 switch c.tokens[c.lastIdx].Token { 586 case parser.UNBOUNDED: 587 customList = c.candidateList([]string{"PRECEDING"}, true) 588 case parser.CURRENT: 589 customList = c.candidateList([]string{"ROW"}, true) 590 default: 591 customList = c.candidateList([]string{ 592 "PRECEDING", 593 "FOLLOWING", 594 }, true) 595 } 596 } else if c.tokens[c.lastIdx].Token == parser.PRECEDING || 597 c.tokens[c.lastIdx].Token == parser.FOLLOWING || 598 c.tokens[c.lastIdx].Token == parser.ROW { 599 600 keywords = append(keywords, "AND") 601 } else if c.tokens[c.lastIdx].Token == parser.AND { 602 customList = append(customList, c.candidateList([]string{ 603 "UNBOUNDED FOLLOWING", 604 "CURRENT ROW", 605 }, false)...) 606 } else { 607 switch c.tokens[c.lastIdx].Token { 608 case parser.UNBOUNDED: 609 customList = c.candidateList([]string{"FOLLOWING"}, false) 610 case parser.CURRENT: 611 customList = c.candidateList([]string{"ROW"}, false) 612 default: 613 customList = c.candidateList([]string{ 614 "PRECEDING", 615 "FOLLOWING", 616 }, false) 617 } 618 } 619 case parser.ROWS: 620 if i == c.lastIdx { 621 customList = append(customList, c.candidateList([]string{ 622 "UNBOUNDED PRECEDING", 623 "CURRENT ROW", 624 }, false)...) 625 626 customList = append(customList, c.candidate("BETWEEN", true)) 627 } else if i == c.lastIdx-1 { 628 switch c.tokens[c.lastIdx].Token { 629 case parser.CURRENT: 630 customList = c.candidateList([]string{"ROW"}, false) 631 default: 632 customList = c.candidateList([]string{"PRECEDING"}, false) 633 } 634 } 635 case parser.ORDER: 636 if i == c.lastIdx { 637 keywords = append(keywords, "BY") 638 } else { 639 if i < c.lastIdx-1 && c.tokens[c.lastIdx].Token != ',' { 640 canUseWindowingClause := false 641 642 switch c.tokens[c.lastIdx].Token { 643 case parser.ASC, parser.DESC: 644 customList = append(customList, c.candidateList([]string{ 645 "NULLS FIRST", 646 "NULLS LAST", 647 }, false)...) 648 canUseWindowingClause = true 649 case parser.NULLS: 650 customList = append(customList, c.candidateList([]string{ 651 "FIRST", 652 "LAST", 653 }, false)...) 654 case parser.FIRST, parser.LAST: 655 canUseWindowingClause = true 656 default: 657 customList = append(customList, c.candidateList([]string{ 658 "ASC", 659 "DESC", 660 "NULLS FIRST", 661 "NULLS LAST", 662 }, false)...) 663 customList = append(customList, c.SearchValues(line, origLine, index)...) 664 665 canUseWindowingClause = true 666 } 667 668 if canUseWindowingClause { 669 funcName := strings.ToUpper(c.tokens[0].Literal) 670 if funcName == "FIRST_VALUE" || 671 funcName == "LAST_VALUE" || 672 funcName == "NTH_VALUE" || 673 (funcName != "LISTAGG" && funcName != "JSON_AGG" && query.InStrSliceWithCaseInsensitive(funcName, c.aggFuncs)) || 674 query.InStrSliceWithCaseInsensitive(funcName, c.userAggFuncs) { 675 676 customList = append(customList, c.candidate("ROWS", true)) 677 } 678 } 679 } else { 680 customList = append(customList, c.SearchValues(line, origLine, index)...) 681 } 682 } 683 case parser.PARTITION: 684 if i == c.lastIdx { 685 keywords = append(keywords, 686 "BY", 687 ) 688 } else { 689 if i < c.lastIdx-1 && c.tokens[c.lastIdx].Token != ',' { 690 keywords = append(keywords, 691 "ORDER BY", 692 ) 693 } 694 customList = append(customList, c.SearchValues(line, origLine, index)...) 695 } 696 case parser.OVER: 697 if i == c.lastIdx-1 && c.tokens[c.lastIdx].Token == '(' { 698 keywords = append(keywords, 699 "PARTITION BY", 700 "ORDER BY", 701 ) 702 } 703 default: 704 if i == 0 { 705 if 0 < len(line) && i == c.lastIdx-1 { 706 if query.InStrSliceWithCaseInsensitive(c.tokens[i].Literal, c.aggFuncs) || 707 query.InStrSliceWithCaseInsensitive(c.tokens[i].Literal, c.userAggFuncs) { 708 keywords = append(keywords, "DISTINCT") 709 } 710 } 711 customList = append(customList, c.SearchValues(line, origLine, index)...) 712 } else { 713 return nil, nil, false 714 } 715 } 716 717 if customList != nil { 718 customList.Sort() 719 } 720 return keywords, customList, true 721 }, 722 ) 723 } 724 725 func (c *Completer) WithArgs(line string, origLine string, index int) readline.CandidateList { 726 var searchQuery = func() int { 727 blockLevel := 0 728 for i := 0; i < len(c.tokens); i++ { 729 switch c.tokens[i].Token { 730 case ')': 731 blockLevel++ 732 case '(': 733 blockLevel-- 734 case parser.SELECT, parser.INSERT, parser.UPDATE, parser.REPLACE, parser.DELETE: 735 if blockLevel == 0 { 736 return i 737 } 738 } 739 } 740 return -1 741 } 742 743 if queryStart := searchQuery(); -1 < queryStart { 744 c.tokens = c.tokens[queryStart:] 745 c.SetLastIndex(line) 746 747 switch c.tokens[0].Token { 748 case parser.SELECT: 749 return c.SelectArgs(line, origLine, index) 750 case parser.INSERT: 751 return c.InsertArgs(line, origLine, index) 752 case parser.UPDATE: 753 return c.UpdateArgs(line, origLine, index) 754 case parser.REPLACE: 755 return c.ReplaceArgs(line, origLine, index) 756 case parser.DELETE: 757 return c.DeleteArgs(line, origLine, index) 758 } 759 } 760 761 blockLevel := 0 762 763 return c.completeArgs( 764 line, 765 origLine, 766 index, 767 func(i int) (keywords []string, customList readline.CandidateList, breakLoop bool) { 768 switch c.tokens[i].Token { 769 case ')': 770 blockLevel++ 771 if blockLevel == 1 && i == c.lastIdx { 772 keywords = append(keywords, "AS") 773 } 774 case '(': 775 blockLevel-- 776 if blockLevel == -1 && i == c.lastIdx && c.tokens[c.lastIdx-1].Token == parser.AS { 777 keywords = append(keywords, "SELECT") 778 } 779 case parser.WITH: 780 if blockLevel == 0 { 781 switch { 782 case i == c.lastIdx || c.tokens[c.lastIdx].Token == ',': 783 keywords = append(keywords, "RECURSIVE") 784 case c.tokens[c.lastIdx].Token == parser.IDENTIFIER: 785 if c.tokens[c.lastIdx].Literal == dummySubquery && c.tokens[c.lastIdx-1].Token == parser.AS { 786 keywords = append(keywords, 787 "SELECT", 788 "INSERT", 789 "UPDATE", 790 "REPLACE", 791 "DELETE", 792 ) 793 } else { 794 keywords = append(keywords, "AS") 795 } 796 } 797 } 798 default: 799 return nil, nil, false 800 } 801 return keywords, customList, true 802 }, 803 ) 804 } 805 806 func (c *Completer) combineTableAlias(fromIdx int) { 807 combined := make([]parser.Token, 0, cap(c.tokens)) 808 temp := make([]parser.Token, 0, cap(c.tokens)) 809 blockLevel := 0 810 for i := 0; i < len(c.tokens); i++ { 811 if i <= fromIdx { 812 combined = append(combined, c.tokens[i]) 813 continue 814 } 815 816 if 0 != blockLevel { 817 temp = append(temp, c.tokens[i]) 818 819 switch c.tokens[i].Token { 820 case '(': 821 blockLevel++ 822 case ')': 823 blockLevel-- 824 if 0 == blockLevel { 825 combined = append(combined, parser.Token{Token: parser.IDENTIFIER, Literal: dummyTable, Quoted: true}) 826 temp = temp[:0] 827 } 828 } 829 continue 830 } 831 832 if c.tokens[i].Token == '(' { 833 blockLevel++ 834 temp = append(temp, c.tokens[i]) 835 continue 836 } 837 838 combined = append(combined, c.tokens[i]) 839 840 if c.tokens[i].Token == parser.IDENTIFIER { 841 combined[len(combined)-1].Quoted = false 842 if i+2 <= c.lastIdx && c.tokens[i+1].Token == parser.AS && c.tokens[i+2].Token == parser.IDENTIFIER { 843 i = i + 2 844 combined[len(combined)-1].Quoted = true 845 } else if i+1 <= c.lastIdx && c.tokens[i+1].Token == parser.IDENTIFIER { 846 i = i + 1 847 combined[len(combined)-1].Quoted = true 848 } 849 } 850 } 851 if 0 < len(temp) { 852 combined = append(combined, temp...) 853 } 854 855 c.tokens = combined 856 } 857 858 func (c *Completer) allTableCandidates(line string, origLine string, index int) readline.CandidateList { 859 list := c.candidateList(append(tableObjectCandidates, "JSON_TABLE()"), false) 860 list.Sort() 861 list = append(list, c.SearchAllTables(line, origLine, index)...) 862 return list 863 } 864 865 func (c *Completer) allTableCandidatesForUpdate(line string, origLine string, index int) readline.CandidateList { 866 list := c.candidateList(tableObjectCandidates, false) 867 list.Sort() 868 list = append(list, c.SearchAllTables(line, origLine, index)...) 869 return list 870 } 871 872 func (c *Completer) allTableCandidatesWithSpaceForUpdate(line string, origLine string, index int) readline.CandidateList { 873 list := c.candidateList(tableObjectCandidates, true) 874 list.Sort() 875 list = append(list, c.SearchAllTablesWithSpace(line, origLine, index)...) 876 return list 877 } 878 879 func (c *Completer) fromClause(i int, line string, origLine string, index int) (tables readline.CandidateList, customList readline.CandidateList, restrict bool) { 880 c.combineTableAlias(i) 881 c.SetLastIndex(line) 882 883 var isInUsing = func() bool { 884 for j := len(c.tokens) - 1; j > i; j-- { 885 switch c.tokens[j].Token { 886 case parser.IDENTIFIER, ',': 887 //OK 888 case '(': 889 return c.tokens[j-1].Token == parser.USING 890 default: 891 return false 892 } 893 } 894 return false 895 } 896 897 var isInOn = func() bool { 898 blockLevel := 0 899 for j := len(c.tokens) - 1; j > i; j-- { 900 switch c.tokens[j].Token { 901 case '(': 902 blockLevel-- 903 case ')': 904 blockLevel++ 905 case parser.JOIN: 906 if blockLevel <= 0 { 907 return false 908 } 909 case parser.ON: 910 if blockLevel <= 0 { 911 return true 912 } 913 } 914 } 915 return false 916 } 917 918 var joinConditionRequired = func() bool { 919 if c.tokens[c.lastIdx].Token == '(' { 920 return false 921 } 922 923 baseIdx := c.lastIdx - 1 924 if c.tokens[baseIdx].Token == parser.LATERAL { 925 baseIdx-- 926 } 927 928 if c.tokens[baseIdx].Token != parser.JOIN { 929 return false 930 } 931 if c.tokens[baseIdx-1].Token == parser.CROSS || 932 c.tokens[baseIdx-1].Token == parser.NATURAL || 933 c.tokens[baseIdx-2].Token == parser.NATURAL || 934 c.tokens[baseIdx-3].Token == parser.NATURAL { 935 return false 936 } 937 return true 938 } 939 940 if isInUsing() { 941 restrict = true 942 return 943 } 944 945 if isInOn() { 946 customList = append(customList, c.whereClause(line, origLine, index)...) 947 if c.tokens[c.lastIdx].Token == parser.ON { 948 restrict = true 949 } else { 950 customList = append(customList, c.candidateList(joinCandidates, true)...) 951 } 952 return 953 } 954 955 switch c.tokens[c.lastIdx].Token { 956 case parser.LATERAL: 957 restrict = true 958 case parser.CROSS, parser.INNER, parser.OUTER: 959 customList = append(customList, c.candidate("JOIN", true)) 960 restrict = true 961 case parser.LEFT, parser.RIGHT, parser.FULL: 962 customList = append(customList, c.candidateList([]string{"JOIN", "OUTER"}, true)...) 963 restrict = true 964 case parser.NATURAL: 965 customList = append(customList, c.candidateList([]string{"INNER", "LEFT", "RIGHT"}, true)...) 966 restrict = true 967 case parser.AS, parser.USING: 968 restrict = true 969 } 970 971 if joinConditionRequired() { 972 customList = append(customList, c.candidate("ON", true)) 973 974 if c.tokens[c.lastIdx-2].Token != parser.FULL && 975 c.tokens[c.lastIdx-3].Token != parser.FULL { 976 977 customList = append(customList, c.candidate("USING ()", true)) 978 } 979 980 if c.tokens[c.lastIdx].Quoted == false { 981 customList = append(customList, c.candidate("AS", true)) 982 } 983 restrict = true 984 } else { 985 canUseLateral := false 986 switch c.tokens[c.lastIdx].Token { 987 case ',': 988 canUseLateral = true 989 case parser.JOIN: 990 canUseLateral = true 991 switch c.tokens[c.lastIdx-1].Token { 992 case parser.RIGHT, parser.FULL: 993 canUseLateral = false 994 case parser.OUTER: 995 switch c.tokens[c.lastIdx-2].Token { 996 case parser.RIGHT, parser.FULL: 997 canUseLateral = false 998 } 999 } 1000 1001 } 1002 1003 if canUseLateral { 1004 customList = append(customList, c.candidate("LATERAL", true)) 1005 } 1006 1007 switch c.tokens[c.lastIdx].Token { 1008 case '(': 1009 customList = append(customList, c.candidate("SELECT", true)) 1010 fallthrough 1011 case parser.FROM, parser.JOIN, ',': 1012 tables = c.allTableCandidates(line, origLine, index) 1013 restrict = true 1014 } 1015 } 1016 1017 if restrict { 1018 return 1019 } 1020 1021 if c.tokens[c.lastIdx].Token == parser.IDENTIFIER { 1022 customList = append(customList, c.candidateList(joinCandidates, true)...) 1023 1024 if c.tokens[c.lastIdx].Quoted == false { 1025 customList = append(customList, c.candidate("AS", true)) 1026 } 1027 } 1028 return 1029 } 1030 1031 func (c *Completer) whereClause(line string, origLine string, index int) readline.CandidateList { 1032 cands := c.SearchValues(line, origLine, index) 1033 if 0 < len(line) { 1034 cands = append(cands, c.candidate("JSON_ROW()", false)) 1035 } 1036 return cands 1037 } 1038 1039 func (c *Completer) SelectArgs(line string, origLine string, index int) readline.CandidateList { 1040 isSelectInto := false 1041 if c.selectIntoEnabled { 1042 for i := len(c.tokens) - 1; i >= 0; i-- { 1043 if c.tokens[i].Token == parser.INTO { 1044 isSelectInto = true 1045 break 1046 } 1047 } 1048 } 1049 1050 return c.completeArgs( 1051 line, 1052 origLine, 1053 index, 1054 func(i int) (keywords []string, customList readline.CandidateList, breakLoop bool) { 1055 switch c.tokens[i].Token { 1056 case parser.FOR: 1057 if i == c.lastIdx { 1058 customList = append(customList, c.candidateList([]string{ 1059 "UPDATE", 1060 }, false)...) 1061 customList.Sort() 1062 } 1063 return nil, customList, true 1064 case parser.LIMIT: 1065 if i < c.lastIdx { 1066 switch c.tokens[c.lastIdx].Token { 1067 case parser.PERCENT: 1068 customList = append(customList, c.candidateList([]string{ 1069 "OFFSET", 1070 }, true)...) 1071 customList = append(customList, c.candidateList([]string{ 1072 "WITH TIES", 1073 "FOR UPDATE", 1074 }, false)...) 1075 case parser.WITH: 1076 customList = append(customList, c.candidate("TIES", false)) 1077 case parser.TIES: 1078 customList = append(customList, c.candidateList([]string{ 1079 "OFFSET", 1080 }, true)...) 1081 customList = append(customList, c.candidate("FOR UPDATE", false)) 1082 default: 1083 customList = append(customList, c.candidateList([]string{ 1084 "OFFSET", 1085 }, true)...) 1086 customList = append(customList, c.candidateList([]string{ 1087 "PERCENT", 1088 "WITH TIES", 1089 "FOR UPDATE", 1090 }, false)...) 1091 customList = append(customList, c.SearchValues(line, origLine, index)...) 1092 } 1093 } else { 1094 customList = append(customList, c.SearchValues(line, origLine, index)...) 1095 } 1096 customList.Sort() 1097 return keywords, customList, true 1098 case parser.FETCH: 1099 if i == c.lastIdx { 1100 afterOffset := false 1101 1102 CompleterSelectArgsSearchOffsetLoop: 1103 for j := c.lastIdx - 1; j >= 0; j-- { 1104 switch c.tokens[j].Token { 1105 case parser.OFFSET: 1106 afterOffset = true 1107 break CompleterSelectArgsSearchOffsetLoop 1108 case parser.ORDER, parser.HAVING, parser.GROUP, parser.FROM, parser.SELECT: 1109 break CompleterSelectArgsSearchOffsetLoop 1110 } 1111 } 1112 1113 if afterOffset { 1114 keywords = append(keywords, "NEXT") 1115 } else { 1116 keywords = append(keywords, "FIRST") 1117 } 1118 } else { 1119 switch c.tokens[c.lastIdx].Token { 1120 case parser.ROW, parser.ROWS, parser.PERCENT: 1121 customList = append(customList, c.candidateList([]string{ 1122 "ONLY", 1123 "WITH TIES", 1124 "FOR UPDATE", 1125 }, false)...) 1126 case parser.WITH: 1127 customList = append(customList, c.candidateList([]string{ 1128 "TIES", 1129 }, false)...) 1130 case parser.ONLY, parser.TIES: 1131 customList = append(customList, c.candidateList([]string{ 1132 "FOR UPDATE", 1133 }, false)...) 1134 case parser.FIRST, parser.NEXT: 1135 //Do nothing 1136 default: 1137 if c.tokens[c.lastIdx].Literal == "1" { 1138 keywords = append(keywords, "ROW") 1139 } else { 1140 keywords = append(keywords, "ROWS") 1141 } 1142 keywords = append(keywords, "PERCENT") 1143 } 1144 } 1145 1146 if customList != nil { 1147 customList.Sort() 1148 } 1149 return keywords, customList, true 1150 case parser.OFFSET: 1151 if i < c.lastIdx { 1152 afterLimit := false 1153 1154 CompleterSelectArgsSearchLimitLoop: 1155 for j := c.lastIdx - 1; j >= 0; j-- { 1156 switch c.tokens[j].Token { 1157 case parser.LIMIT: 1158 afterLimit = true 1159 break CompleterSelectArgsSearchLimitLoop 1160 case parser.ORDER, parser.HAVING, parser.GROUP, parser.FROM, parser.SELECT: 1161 break CompleterSelectArgsSearchLimitLoop 1162 } 1163 } 1164 1165 if !afterLimit { 1166 customList = append(customList, c.candidateList([]string{ 1167 "FETCH", 1168 }, true)...) 1169 } 1170 1171 customList = append(customList, c.candidateList([]string{ 1172 "FOR UPDATE", 1173 }, false)...) 1174 if c.tokens[c.lastIdx].Token != parser.ROW && c.tokens[c.lastIdx].Token != parser.ROWS { 1175 if c.tokens[c.lastIdx].Literal == "1" { 1176 customList = append(customList, c.candidateList([]string{ 1177 "ROW", 1178 }, false)...) 1179 } else { 1180 customList = append(customList, c.candidateList([]string{ 1181 "ROWS", 1182 }, false)...) 1183 } 1184 } 1185 } 1186 customList = append(customList, c.SearchValues(line, origLine, index)...) 1187 customList.Sort() 1188 return nil, customList, true 1189 case parser.ORDER: 1190 if i == c.lastIdx { 1191 keywords = append(keywords, "BY") 1192 } else if i < c.lastIdx-1 && c.tokens[c.lastIdx].Token != ',' { 1193 switch c.tokens[c.lastIdx].Token { 1194 case parser.ASC, parser.DESC: 1195 customList = append(customList, c.candidateList([]string{ 1196 "NULLS FIRST", 1197 "NULLS LAST", 1198 "FOR UPDATE", 1199 }, false)...) 1200 customList = append(customList, c.candidateList([]string{ 1201 "OFFSET", 1202 "FETCH", 1203 "LIMIT", 1204 }, true)...) 1205 case parser.NULLS: 1206 customList = append(customList, c.candidateList([]string{ 1207 "FIRST", 1208 "LAST", 1209 }, false)...) 1210 case parser.FIRST, parser.LAST: 1211 customList = append(customList, c.candidateList([]string{ 1212 "OFFSET", 1213 "FETCH", 1214 "LIMIT", 1215 }, true)...) 1216 customList = append(customList, c.candidateList([]string{ 1217 "FOR UPDATE", 1218 }, false)...) 1219 default: 1220 customList = append(customList, c.candidateList([]string{ 1221 "ASC", 1222 "DESC", 1223 "NULLS FIRST", 1224 "NULLS LAST", 1225 "FOR UPDATE", 1226 }, false)...) 1227 customList = append(customList, c.candidateList([]string{ 1228 "OFFSET", 1229 "FETCH", 1230 "LIMIT", 1231 }, true)...) 1232 customList = append(customList, c.SearchValues(line, origLine, index)...) 1233 } 1234 } else { 1235 customList = append(customList, c.SearchValues(line, origLine, index)...) 1236 } 1237 1238 if customList != nil { 1239 customList.Sort() 1240 } 1241 return keywords, customList, true 1242 case parser.ALL: 1243 if i == c.lastIdx && 0 <= c.lastIdx-1 { 1244 switch c.tokens[i-1].Token { 1245 case parser.UNION, parser.EXCEPT, parser.INTERSECT: 1246 keywords = append(keywords, "SELECT") 1247 return keywords, nil, true 1248 } 1249 } 1250 case parser.UNION, parser.EXCEPT, parser.INTERSECT: 1251 if i == c.lastIdx { 1252 keywords = append(keywords, 1253 "ALL", 1254 "SELECT", 1255 ) 1256 } 1257 return keywords, nil, true 1258 case parser.HAVING: 1259 customList = append(customList, c.whereClause(line, origLine, index)...) 1260 customList = append(customList, c.aggregateFunctionCandidateList(line)...) 1261 if i < c.lastIdx { 1262 customList = append(customList, c.candidateList([]string{ 1263 "ORDER BY", 1264 "OFFSET", 1265 "FETCH", 1266 "LIMIT", 1267 }, true)...) 1268 if !isSelectInto { 1269 customList = append(customList, c.candidateList([]string{ 1270 "UNION", 1271 "EXCEPT", 1272 "INTERSECT", 1273 }, true)...) 1274 } 1275 customList = append(customList, c.candidateList([]string{ 1276 "FOR UPDATE", 1277 }, false)...) 1278 } 1279 customList.Sort() 1280 return keywords, customList, true 1281 case parser.GROUP: 1282 if i == c.lastIdx { 1283 keywords = append(keywords, "BY") 1284 } else { 1285 customList = append(customList, c.whereClause(line, origLine, index)...) 1286 if i < c.lastIdx-1 && c.tokens[c.lastIdx].Token != ',' { 1287 customList = append(customList, c.candidateList([]string{ 1288 "HAVING", 1289 "ORDER BY", 1290 "OFFSET", 1291 "FETCH", 1292 "LIMIT", 1293 }, true)...) 1294 if !isSelectInto { 1295 customList = append(customList, c.candidateList([]string{ 1296 "UNION", 1297 "EXCEPT", 1298 "INTERSECT", 1299 }, true)...) 1300 } 1301 customList = append(customList, c.candidateList([]string{ 1302 "FOR UPDATE", 1303 }, false)...) 1304 } 1305 customList.Sort() 1306 } 1307 return keywords, customList, true 1308 case parser.WHERE: 1309 customList = append(customList, c.whereClause(line, origLine, index)...) 1310 if i < c.lastIdx { 1311 customList = append(customList, c.candidateList([]string{ 1312 "GROUP BY", 1313 "HAVING", 1314 "ORDER BY", 1315 "OFFSET", 1316 "FETCH", 1317 "LIMIT", 1318 }, true)...) 1319 if !isSelectInto { 1320 customList = append(customList, c.candidateList([]string{ 1321 "UNION", 1322 "EXCEPT", 1323 "INTERSECT", 1324 }, true)...) 1325 } 1326 customList = append(customList, c.candidateList([]string{ 1327 "FOR UPDATE", 1328 }, false)...) 1329 } 1330 customList.Sort() 1331 return keywords, customList, true 1332 case parser.FROM: 1333 tables, clist, restrict := c.fromClause(i, line, origLine, index) 1334 if !restrict { 1335 if i < c.lastIdx && c.tokens[c.lastIdx].Token != ',' { 1336 clist = append(clist, c.candidateList([]string{ 1337 "WHERE", 1338 "GROUP BY", 1339 "HAVING", 1340 "ORDER BY", 1341 "OFFSET", 1342 "FETCH", 1343 "LIMIT", 1344 }, true)...) 1345 if !isSelectInto { 1346 clist = append(clist, c.candidateList([]string{ 1347 "UNION", 1348 "EXCEPT", 1349 "INTERSECT", 1350 }, true)...) 1351 } 1352 clist = append(clist, c.candidateList([]string{ 1353 "FOR UPDATE", 1354 }, false)...) 1355 } 1356 } 1357 clist.Sort() 1358 return nil, append(tables, clist...), true 1359 case parser.INTO: 1360 switch c.tokens[c.lastIdx].Token { 1361 case parser.INTO, ',': 1362 customList = append(customList, c.candidateList(c.varList, false)...) 1363 case parser.VARIABLE: 1364 customList = append(customList, c.candidateList([]string{ 1365 "FROM", 1366 "WHERE", 1367 "GROUP BY", 1368 "HAVING", 1369 "ORDER BY", 1370 "OFFSET", 1371 "FETCH", 1372 "LIMIT", 1373 }, true)...) 1374 customList = append(customList, c.candidateList([]string{ 1375 "FOR UPDATE", 1376 }, false)...) 1377 } 1378 if customList != nil { 1379 customList.Sort() 1380 } 1381 return keywords, customList, true 1382 case parser.SELECT: 1383 if i == c.lastIdx { 1384 if 0 < len(line) { 1385 keywords = append(keywords, "DISTINCT") 1386 } 1387 } else { 1388 lastIdx := c.lastIdx 1389 if i+1 < len(c.tokens) && c.tokens[i+1].Token == parser.DISTINCT { 1390 lastIdx-- 1391 } 1392 if i < lastIdx && c.tokens[c.lastIdx].Token != ',' { 1393 customList = append(customList, c.candidateList([]string{ 1394 "AS", 1395 "FROM", 1396 "WHERE", 1397 "GROUP BY", 1398 "HAVING", 1399 "ORDER BY", 1400 "OFFSET", 1401 "FETCH", 1402 "LIMIT", 1403 "UNION", 1404 "EXCEPT", 1405 "INTERSECT", 1406 }, true)...) 1407 customList = append(customList, c.candidateList([]string{ 1408 "FOR UPDATE", 1409 }, false)...) 1410 if c.selectIntoEnabled { 1411 customList = append(customList, c.candidateList([]string{ 1412 "INTO", 1413 }, true)...) 1414 } 1415 } 1416 } 1417 customList = append(customList, c.SearchValues(line, origLine, index)...) 1418 customList.Sort() 1419 return keywords, customList, true 1420 } 1421 return nil, nil, false 1422 }, 1423 ) 1424 } 1425 1426 func (c *Completer) InsertArgs(line string, origLine string, index int) readline.CandidateList { 1427 return c.completeArgs( 1428 line, 1429 origLine, 1430 index, 1431 func(i int) (keywords []string, customList readline.CandidateList, breakLoop bool) { 1432 switch c.tokens[i].Token { 1433 case parser.SELECT: 1434 return nil, c.SelectArgs(line, origLine, index), true 1435 case parser.VALUES: 1436 customList = c.SearchValues(line, origLine, index) 1437 if 0 < len(line) { 1438 customList = append(customList, c.candidate("JSON_ROW()", false)) 1439 } 1440 customList.Sort() 1441 return nil, customList, true 1442 case parser.INTO: 1443 if i == c.lastIdx { 1444 customList = c.allTableCandidatesWithSpaceForUpdate(line, origLine, index) 1445 } else { 1446 if c.tokens[c.lastIdx-1].Token == parser.INTO || c.tokens[c.lastIdx].Token == ')' { 1447 keywords = append(keywords, "VALUES", "SELECT") 1448 } 1449 } 1450 return keywords, customList, true 1451 case parser.INSERT: 1452 if i == c.lastIdx { 1453 keywords = append(keywords, "INTO") 1454 } 1455 return keywords, nil, true 1456 } 1457 return nil, nil, false 1458 }, 1459 ) 1460 } 1461 1462 func (c *Completer) UpdateArgs(line string, origLine string, index int) readline.CandidateList { 1463 return c.completeArgs( 1464 line, 1465 origLine, 1466 index, 1467 func(i int) (keywords []string, customList readline.CandidateList, breakLoop bool) { 1468 switch c.tokens[i].Token { 1469 case parser.WHERE: 1470 customList = append(customList, c.whereClause(line, origLine, index)...) 1471 customList.Sort() 1472 return nil, customList, true 1473 case parser.FROM: 1474 tables, clist, restrict := c.fromClause(i, line, origLine, index) 1475 if !restrict { 1476 if i < c.lastIdx && c.tokens[c.lastIdx].Token != ',' { 1477 clist = append(clist, c.candidate("WHERE", true)) 1478 } 1479 } 1480 clist.Sort() 1481 return nil, append(tables, clist...), true 1482 case parser.SET: 1483 if c.tokens[c.lastIdx].Token != parser.SET && c.tokens[c.lastIdx].Token != ',' && 1484 c.tokens[c.lastIdx-1].Token != parser.SET && c.tokens[c.lastIdx-1].Token != ',' { 1485 1486 customList = append(customList, c.SearchValues(line, origLine, index)...) 1487 customList.Sort() 1488 if c.tokens[c.lastIdx].Token != '=' { 1489 keywords = append(keywords, "FROM", "WHERE") 1490 } 1491 } 1492 return keywords, customList, true 1493 case parser.UPDATE: 1494 if c.tokens[c.lastIdx].Token == parser.UPDATE || c.tokens[c.lastIdx].Token == ',' { 1495 customList = c.allTableCandidatesForUpdate(line, origLine, index) 1496 } else { 1497 keywords = append(keywords, "SET") 1498 } 1499 return keywords, customList, true 1500 } 1501 return nil, nil, false 1502 }, 1503 ) 1504 } 1505 1506 func (c *Completer) ReplaceArgs(line string, origLine string, index int) readline.CandidateList { 1507 usingExists := false 1508 1509 return c.completeArgs( 1510 line, 1511 origLine, 1512 index, 1513 func(i int) (keywords []string, customList readline.CandidateList, breakLoop bool) { 1514 switch c.tokens[i].Token { 1515 case parser.SELECT: 1516 return nil, c.SelectArgs(line, origLine, index), true 1517 case parser.VALUES: 1518 customList = c.SearchValues(line, origLine, index) 1519 if 0 < len(line) { 1520 customList = append(customList, c.candidate("JSON_ROW()", false)) 1521 } 1522 customList.Sort() 1523 return nil, customList, true 1524 case parser.USING: 1525 usingExists = true 1526 case parser.INTO: 1527 if i == c.lastIdx { 1528 customList = c.allTableCandidatesWithSpaceForUpdate(line, origLine, index) 1529 } else if c.tokens[c.lastIdx-1].Token == parser.INTO || (!usingExists && c.tokens[c.lastIdx].Token == ')') { 1530 customList = append(customList, c.candidate("USING ()", true)) 1531 } else if usingExists && c.tokens[c.lastIdx].Token == ')' { 1532 keywords = append(keywords, "VALUES", "SELECT") 1533 } 1534 return keywords, customList, true 1535 case parser.REPLACE: 1536 if i == c.lastIdx { 1537 keywords = append(keywords, "INTO") 1538 } 1539 return keywords, nil, true 1540 } 1541 return nil, nil, false 1542 }, 1543 ) 1544 } 1545 1546 func (c *Completer) DeleteArgs(line string, origLine string, index int) readline.CandidateList { 1547 return c.completeArgs( 1548 line, 1549 origLine, 1550 index, 1551 func(i int) (keywords []string, customList readline.CandidateList, breakLoop bool) { 1552 switch c.tokens[i].Token { 1553 case parser.WHERE: 1554 customList = append(customList, c.whereClause(line, origLine, index)...) 1555 customList.Sort() 1556 return nil, customList, true 1557 case parser.FROM: 1558 if c.tokens[i-1].Token == parser.DELETE { 1559 if c.tokens[c.lastIdx].Token == parser.FROM || c.tokens[c.lastIdx].Token == ',' { 1560 customList = c.allTableCandidates(line, origLine, index) 1561 } else { 1562 customList = append(customList, c.candidate("WHERE", true)) 1563 } 1564 } else { 1565 tables, clist, restrict := c.fromClause(i, line, origLine, index) 1566 if !restrict { 1567 if i < c.lastIdx && c.tokens[c.lastIdx].Token != ',' { 1568 clist = append(clist, c.candidate("WHERE", true)) 1569 } 1570 } 1571 clist.Sort() 1572 customList = append(tables, clist...) 1573 } 1574 return keywords, customList, true 1575 case parser.DELETE: 1576 if c.tokens[c.lastIdx].Token != ',' { 1577 keywords = append(keywords, "FROM") 1578 } 1579 return keywords, nil, true 1580 } 1581 return nil, nil, false 1582 }, 1583 ) 1584 } 1585 1586 func (c *Completer) CreateArgs(line string, origLine string, index int) readline.CandidateList { 1587 var isPrepositionOfSelect = func(i int) bool { 1588 for j := i; j >= 0; j-- { 1589 if c.tokens[j].Token == ';' || c.tokens[j].Token == parser.TABLE { 1590 break 1591 } 1592 if c.tokens[j].Token == parser.SELECT { 1593 return false 1594 } 1595 } 1596 return true 1597 } 1598 1599 return c.completeArgs( 1600 line, 1601 origLine, 1602 index, 1603 func(i int) (keywords []string, customList readline.CandidateList, breakLoop bool) { 1604 switch c.tokens[i].Token { 1605 case parser.SELECT: 1606 return nil, c.SelectArgs(line, origLine, index), true 1607 case parser.AS: 1608 if isPrepositionOfSelect(i) { 1609 return []string{"SELECT"}, nil, true 1610 } 1611 case parser.TABLE: 1612 if i == c.lastIdx { 1613 return []string{"IF NOT EXISTS"}, nil, true 1614 } 1615 1616 if c.lastIdx == i+1 && c.tokens[c.lastIdx].Token == parser.IF { 1617 return []string{"NOT EXISTS"}, nil, true 1618 } 1619 if c.lastIdx == i+2 && c.tokens[c.lastIdx].Token == parser.NOT && c.tokens[c.lastIdx-1].Token == parser.IF { 1620 return []string{"EXISTS"}, nil, true 1621 } 1622 1623 if (c.tokens[c.lastIdx].Token == ')' && c.BracketIsEnclosed()) || 1624 i == c.lastIdx-1 { 1625 return []string{"AS", "SELECT"}, nil, true 1626 } 1627 case parser.CREATE: 1628 if i == c.lastIdx { 1629 return []string{"TABLE"}, nil, true 1630 } 1631 } 1632 return nil, nil, false 1633 }, 1634 ) 1635 } 1636 1637 func (c *Completer) AlterArgs(line string, origLine string, index int) readline.CandidateList { 1638 operations := []string{ 1639 "ADD", 1640 "DROP", 1641 "RENAME", 1642 "SET", 1643 } 1644 1645 addPositions := []string{ 1646 "FIRST", 1647 "LAST", 1648 "AFTER", 1649 "BEFORE", 1650 } 1651 1652 var columnsInTable = func(i int, appendSpace bool) readline.CandidateList { 1653 var tableName string 1654 for j := i; j >= 0; j-- { 1655 if c.tokens[j].Token == parser.TABLE { 1656 tableName = c.tokens[j+1].Literal 1657 break 1658 } 1659 } 1660 1661 var clist readline.CandidateList 1662 if 0 < len(tableName) { 1663 clist = c.identifierList(c.ColumnList(tableName, c.scope.Tx.Flags.Repository), appendSpace) 1664 } 1665 return clist 1666 } 1667 1668 return c.completeArgs( 1669 line, 1670 origLine, 1671 index, 1672 func(i int) (keywords []string, customList readline.CandidateList, breakLoop bool) { 1673 switch c.tokens[i].Token { 1674 case parser.AFTER, parser.BEFORE: 1675 return nil, columnsInTable(i, false), true 1676 case parser.ADD: 1677 prevToken := c.tokens[c.lastIdx].Token 1678 1679 if i+1 < len(c.tokens) && c.tokens[i+1].Token == '(' { 1680 if c.BracketIsEnclosed() { 1681 if prevToken == ')' { 1682 return nil, c.candidateList(addPositions, true), true 1683 } 1684 } else { 1685 switch { 1686 case prevToken == '(' || prevToken == ',': 1687 //Column 1688 case c.tokens[c.lastIdx-1].Token == '(' || c.tokens[c.lastIdx-1].Token == ',': 1689 return []string{"DEFAULT"}, nil, true 1690 default: 1691 return nil, c.SearchValues(line, origLine, index), true 1692 } 1693 } 1694 } else { 1695 switch i { 1696 case c.lastIdx: 1697 //Column 1698 case c.lastIdx - 1: 1699 return []string{"DEFAULT"}, c.candidateList(addPositions, true), true 1700 default: 1701 return nil, c.SearchValues(line, origLine, index), true 1702 } 1703 } 1704 case parser.DROP: 1705 if i+1 < len(c.tokens) && c.tokens[i+1].Token == '(' { 1706 if !c.BracketIsEnclosed() { 1707 return nil, columnsInTable(i, false), true 1708 } 1709 } else { 1710 switch i { 1711 case c.lastIdx: 1712 return nil, columnsInTable(i, false), true 1713 } 1714 } 1715 case parser.RENAME: 1716 switch i { 1717 case c.lastIdx: 1718 return nil, columnsInTable(i, true), true 1719 case c.lastIdx - 1: 1720 return []string{"TO"}, nil, true 1721 } 1722 case parser.SET: 1723 switch i { 1724 case c.lastIdx: 1725 return query.FileAttributeList, nil, true 1726 case c.lastIdx - 1: 1727 return []string{"TO"}, nil, true 1728 case c.lastIdx - 2: 1729 switch strings.ToUpper(c.tokens[c.lastIdx-1].Literal) { 1730 case query.TableFormat: 1731 return nil, c.candidateList(c.tableFormatList(), false), true 1732 case query.TableDelimiter: 1733 return nil, c.candidateList(delimiterCandidates, false), true 1734 case query.TableDelimiterPositions: 1735 return nil, c.candidateList(delimiterPositionsCandidates, false), true 1736 case query.TableEncoding: 1737 return nil, c.candidateList(exportEncodingsCandidates, false), true 1738 case query.TableLineBreak: 1739 return nil, c.candidateList(c.lineBreakList(), false), true 1740 case query.TableJsonEscape: 1741 return nil, c.candidateList(c.jsonEscapeTypeList(), false), true 1742 case query.TableHeader, query.TableEncloseAll, query.TablePrettyPrint: 1743 return nil, c.candidateList([]string{ternary.TRUE.String(), ternary.FALSE.String()}, false), true 1744 } 1745 } 1746 case parser.TABLE: 1747 switch i { 1748 case c.lastIdx: 1749 return nil, c.allTableCandidatesWithSpaceForUpdate(line, origLine, index), true 1750 case c.lastIdx - 1: 1751 return operations, nil, true 1752 } 1753 case parser.ALTER: 1754 if i == c.lastIdx { 1755 return []string{"TABLE"}, nil, true 1756 } 1757 default: 1758 return nil, nil, false 1759 } 1760 return nil, nil, true 1761 }, 1762 ) 1763 } 1764 1765 func (c *Completer) DeclareArgs(line string, origLine string, index int) readline.CandidateList { 1766 return c.completeArgs( 1767 line, 1768 origLine, 1769 index, 1770 func(i int) (keywords []string, customList readline.CandidateList, breakLoop bool) { 1771 switch c.tokens[i].Token { 1772 case parser.SELECT: 1773 return nil, c.SelectArgs(line, origLine, index), true 1774 case parser.CURSOR: 1775 if i == c.lastIdx { 1776 return []string{"FOR"}, nil, true 1777 } else if i == c.lastIdx-1 && c.tokens[c.lastIdx].Token == parser.FOR { 1778 return []string{"SELECT"}, c.candidateList(c.statementList, false), true 1779 } 1780 case parser.VIEW: 1781 switch c.tokens[c.lastIdx].Token { 1782 case parser.AS: 1783 return []string{"SELECT"}, nil, true 1784 case parser.VIEW, ')': 1785 return []string{"AS"}, nil, true 1786 } 1787 case parser.AGGREGATE, parser.FUNCTION: 1788 case parser.VAR: 1789 switch c.tokens[c.lastIdx].Token { 1790 case parser.VARIABLE, ',': 1791 default: 1792 return nil, c.SearchValues(line, origLine, index), true 1793 } 1794 case parser.DECLARE: 1795 if i == c.lastIdx-1 && c.tokens[c.lastIdx].Token != parser.VARIABLE { 1796 obj := []string{ 1797 "CURSOR", 1798 "VIEW", 1799 "FUNCTION", 1800 "AGGREGATE", 1801 } 1802 return obj, nil, true 1803 } else if i < c.lastIdx-1 && c.tokens[i+1].Token == parser.VARIABLE { 1804 switch c.tokens[c.lastIdx].Token { 1805 case parser.VARIABLE, ',': 1806 default: 1807 return nil, c.SearchValues(line, origLine, index), true 1808 } 1809 } 1810 default: 1811 return nil, nil, false 1812 } 1813 1814 if c.tokens[c.lastIdx].Token == parser.SUBSTITUTION_OP { 1815 return nil, c.SearchValues(line, origLine, index), true 1816 } 1817 1818 return nil, nil, true 1819 }, 1820 ) 1821 } 1822 1823 func (c *Completer) PrepareArgs(line string, origLine string, index int) readline.CandidateList { 1824 return c.completeArgs( 1825 line, 1826 origLine, 1827 index, 1828 func(i int) (keywords []string, customList readline.CandidateList, breakLoop bool) { 1829 switch c.tokens[i].Token { 1830 case parser.PREPARE: 1831 if i == c.lastIdx-1 { 1832 return []string{"FROM"}, nil, true 1833 } 1834 default: 1835 return nil, nil, false 1836 } 1837 1838 return nil, nil, true 1839 }, 1840 ) 1841 } 1842 1843 func (c *Completer) FetchArgs(line string, origLine string, index int) readline.CandidateList { 1844 positions := []string{ 1845 "NEXT", 1846 "PRIOR", 1847 "FIRST", 1848 "LAST", 1849 "ABSOLUTE", 1850 "RELATIVE", 1851 } 1852 1853 return c.completeArgs( 1854 line, 1855 origLine, 1856 index, 1857 func(i int) (keywords []string, customList readline.CandidateList, breakLoop bool) { 1858 switch c.tokens[i].Token { 1859 case parser.INTO: 1860 return nil, c.SearchValues(line, origLine, index), true 1861 case parser.NEXT, parser.PRIOR, parser.FIRST, parser.LAST: 1862 switch i { 1863 case c.lastIdx: 1864 return c.cursorList, nil, true 1865 case c.lastIdx - 1: 1866 return []string{"INTO"}, nil, true 1867 } 1868 return nil, nil, true 1869 case parser.ABSOLUTE, parser.RELATIVE: 1870 switch i { 1871 case c.lastIdx: 1872 return nil, c.SearchValuesWithSpace(line, origLine, index), true 1873 default: 1874 if c.tokens[c.lastIdx].Token == parser.IDENTIFIER { 1875 return []string{"INTO"}, nil, true 1876 } else { 1877 return c.cursorList, nil, true 1878 } 1879 } 1880 case parser.FETCH: 1881 switch i { 1882 case c.lastIdx: 1883 return c.cursorList, c.candidateList(positions, true), true 1884 case c.lastIdx - 1: 1885 return []string{"INTO"}, nil, true 1886 } 1887 return nil, nil, true 1888 } 1889 return nil, nil, false 1890 }, 1891 ) 1892 } 1893 1894 func (c *Completer) SetArgs(line string, origLine string, index int) readline.CandidateList { 1895 return c.completeArgs( 1896 line, 1897 origLine, 1898 index, 1899 func(i int) (keywords []string, customList readline.CandidateList, breakLoop bool) { 1900 switch c.tokens[i].Token { 1901 case parser.TO: 1902 if i == c.lastIdx && c.tokens[c.lastIdx-1].Token == parser.FLAG { 1903 switch strings.ToUpper(c.tokens[c.lastIdx-1].Literal) { 1904 case option.RepositoryFlag: 1905 return nil, c.SearchDirs(line, origLine, index), true 1906 case option.TimezoneFlag: 1907 return nil, c.candidateList([]string{"Local", "UTC"}, false), true 1908 case option.ImportFormatFlag: 1909 return nil, c.candidateList(c.importFormatList(), false), true 1910 case option.DelimiterFlag, option.ExportDelimiterFlag: 1911 return nil, c.candidateList(delimiterCandidates, false), true 1912 case option.DelimiterPositionsFlag, option.ExportDelimiterPositionsFlag: 1913 return nil, c.candidateList(delimiterPositionsCandidates, false), true 1914 case option.EncodingFlag: 1915 return nil, c.candidateList(c.encodingList(), false), true 1916 case option.ExportEncodingFlag: 1917 return nil, c.candidateList(exportEncodingsCandidates, false), true 1918 case option.AnsiQuotesFlag, option.StrictEqualFlag, option.AllowUnevenFieldsFlag, 1919 option.NoHeaderFlag, option.WithoutNullFlag, 1920 option.WithoutHeaderFlag, option.EncloseAllFlag, option.PrettyPrintFlag, 1921 option.ScientificNotationFlag, 1922 option.StripEndingLineBreakFlag, option.EastAsianEncodingFlag, 1923 option.CountDiacriticalSignFlag, option.CountFormatCodeFlag, 1924 option.ColorFlag, option.QuietFlag, option.StatsFlag: 1925 return nil, c.candidateList([]string{ternary.TRUE.String(), ternary.FALSE.String()}, false), true 1926 case option.FormatFlag: 1927 return nil, c.candidateList(c.tableFormatList(), false), true 1928 case option.LineBreakFlag: 1929 return nil, c.candidateList(c.lineBreakList(), false), true 1930 case option.JsonEscapeFlag: 1931 return nil, c.candidateList(c.jsonEscapeTypeList(), false), true 1932 } 1933 } 1934 return nil, c.SearchValues(line, origLine, index), true 1935 case parser.SET: 1936 switch i { 1937 case c.lastIdx: 1938 return nil, append(c.candidateList(c.flagList, true), c.candidateList(c.environmentVariableList(line), true)...), true 1939 case c.lastIdx - 1: 1940 return []string{"TO"}, nil, true 1941 } 1942 return nil, nil, true 1943 } 1944 return nil, nil, false 1945 }, 1946 ) 1947 } 1948 1949 func (c *Completer) UsingArgs(line string, origLine string, index int) readline.CandidateList { 1950 return c.completeArgs( 1951 line, 1952 origLine, 1953 index, 1954 func(i int) (keywords []string, customList readline.CandidateList, breakLoop bool) { 1955 switch c.tokens[i].Token { 1956 case parser.USING: 1957 if c.tokens[c.lastIdx-1].Token == parser.USING || c.tokens[c.lastIdx-1].Token == ',' { 1958 keywords = []string{"AS"} 1959 } 1960 return keywords, c.SearchValues(line, origLine, index), true 1961 case parser.EXECUTE, parser.PRINTF, parser.OPEN: 1962 if i == c.lastIdx { 1963 switch c.tokens[i].Token { 1964 case parser.EXECUTE: 1965 keywords = c.statementList 1966 case parser.OPEN: 1967 keywords = c.cursorList 1968 } 1969 } else { 1970 keywords = append(keywords, "USING") 1971 } 1972 return keywords, c.SearchValues(line, origLine, index), true 1973 } 1974 return nil, nil, false 1975 }, 1976 ) 1977 } 1978 1979 func (c *Completer) AddFlagArgs(line string, origLine string, index int) readline.CandidateList { 1980 return c.completeArgs( 1981 line, 1982 origLine, 1983 index, 1984 func(i int) (keywords []string, customList readline.CandidateList, breakLoop bool) { 1985 switch c.tokens[i].Token { 1986 case parser.TO: 1987 return nil, c.candidateList([]string{option.FlagSymbol(option.DatetimeFormatFlag)}, false), true 1988 case parser.ADD: 1989 if i < c.lastIdx { 1990 keywords = append(keywords, "TO") 1991 } 1992 return keywords, c.SearchValues(line, origLine, index), true 1993 } 1994 return nil, nil, false 1995 }, 1996 ) 1997 } 1998 1999 func (c *Completer) RemoveFlagArgs(line string, origLine string, index int) readline.CandidateList { 2000 return c.completeArgs( 2001 line, 2002 origLine, 2003 index, 2004 func(i int) (keywords []string, customList readline.CandidateList, breakLoop bool) { 2005 switch c.tokens[i].Token { 2006 case parser.FROM: 2007 return nil, c.candidateList([]string{option.FlagSymbol(option.DatetimeFormatFlag)}, false), true 2008 case parser.REMOVE: 2009 if i < c.lastIdx { 2010 keywords = append(keywords, "FROM") 2011 } 2012 return keywords, c.SearchValues(line, origLine, index), true 2013 } 2014 return nil, nil, false 2015 }, 2016 ) 2017 2018 } 2019 2020 func (c *Completer) DisposeArgs(line string, origLine string, index int) readline.CandidateList { 2021 return c.completeArgs( 2022 line, 2023 origLine, 2024 index, 2025 func(i int) (keywords []string, customList readline.CandidateList, breakLoop bool) { 2026 switch c.tokens[i].Token { 2027 case parser.CURSOR: 2028 switch i { 2029 case c.lastIdx: 2030 return nil, c.candidateList(c.cursorList, false), true 2031 } 2032 case parser.FUNCTION: 2033 switch i { 2034 case c.lastIdx: 2035 return nil, c.candidateList(c.userFuncList, false), true 2036 } 2037 case parser.VIEW: 2038 switch i { 2039 case c.lastIdx: 2040 return nil, c.candidateList(c.viewList, false), true 2041 } 2042 case parser.PREPARE: 2043 switch i { 2044 case c.lastIdx: 2045 return nil, c.candidateList(c.statementList, false), true 2046 } 2047 case parser.DISPOSE: 2048 switch i { 2049 case c.lastIdx: 2050 var items []string 2051 if 0 < len(c.cursorList) { 2052 items = append(items, "CURSOR") 2053 } 2054 if 0 < len(c.userFuncList) { 2055 items = append(items, "FUNCTION") 2056 } 2057 if 0 < len(c.viewList) { 2058 items = append(items, "VIEW") 2059 } 2060 if 0 < len(c.statementList) { 2061 items = append(items, "PREPARE") 2062 } 2063 sort.Strings(items) 2064 list := append(c.candidateList(items, true), c.candidateList(c.varList, false)...) 2065 return nil, list, true 2066 } 2067 default: 2068 return nil, nil, false 2069 } 2070 return nil, nil, true 2071 }, 2072 ) 2073 } 2074 2075 func (c *Completer) ShowArgs(line string, origLine string, index int) readline.CandidateList { 2076 var showChild = func() readline.CandidateList { 2077 cands := c.candidateList(query.ShowObjectList, false) 2078 cands = append(cands, c.candidate("FIELDS", true)) 2079 cands.Sort() 2080 cands = append(cands, c.candidateList(c.flagList, false)...) 2081 return cands 2082 } 2083 2084 return c.completeArgs( 2085 line, 2086 origLine, 2087 index, 2088 func(i int) (keywords []string, customList readline.CandidateList, breakLoop bool) { 2089 switch c.tokens[i].Token { 2090 case parser.FROM: 2091 switch i { 2092 case c.lastIdx: 2093 return nil, c.allTableCandidatesForUpdate(line, origLine, index), true 2094 } 2095 case parser.SHOW: 2096 switch i { 2097 case c.lastIdx: 2098 return nil, showChild(), true 2099 case c.lastIdx - 1: 2100 if c.tokens[c.lastIdx].Token == parser.IDENTIFIER && strings.ToUpper(c.tokens[c.lastIdx].Literal) == "FIELDS" { 2101 return []string{"FROM"}, nil, true 2102 } 2103 } 2104 default: 2105 return nil, nil, false 2106 } 2107 return nil, nil, true 2108 }, 2109 ) 2110 } 2111 2112 func (c *Completer) SearchAllTablesWithSpace(line string, origLine string, index int) readline.CandidateList { 2113 cands := c.SearchAllTables(line, origLine, index) 2114 for i := range cands { 2115 cands[i].AppendSpace = true 2116 } 2117 return cands 2118 } 2119 2120 func (c *Completer) SearchAllTables(line string, _ string, _ int) readline.CandidateList { 2121 tableKeys := c.scope.Tx.CachedViews.SortedKeys() 2122 files := c.ListFiles(line, []string{option.CsvExt, option.TsvExt, option.JsonExt, option.JsonlExt, option.LtsvExt, option.TextExt}, c.scope.Tx.Flags.Repository) 2123 2124 defaultDir := c.scope.Tx.Flags.Repository 2125 if len(defaultDir) < 1 { 2126 defaultDir, _ = os.Getwd() 2127 } 2128 2129 items := make([]string, 0, len(tableKeys)+len(files)+len(c.viewList)) 2130 tablePath := make(map[string]bool) 2131 for _, k := range tableKeys { 2132 if view, ok := c.scope.Tx.CachedViews.Load(strings.ToUpper(k)); ok { 2133 lpath := view.FileInfo.Path 2134 tablePath[lpath] = true 2135 if filepath.Dir(lpath) == defaultDir { 2136 items = append(items, filepath.Base(lpath)) 2137 } else { 2138 items = append(items, lpath) 2139 } 2140 } 2141 } 2142 2143 items = append(items, c.viewList...) 2144 sort.Strings(items) 2145 2146 for _, f := range files { 2147 if f != "." && f != ".." { 2148 abs := f 2149 if !filepath.IsAbs(abs) { 2150 abs = filepath.Join(defaultDir, abs) 2151 } 2152 if _, ok := tablePath[abs]; ok { 2153 continue 2154 } 2155 } 2156 items = append(items, f) 2157 } 2158 2159 cands := make(readline.CandidateList, 0, len(items)) 2160 for _, t := range items { 2161 cands = append(cands, readline.Candidate{Name: []rune(t), FormatAsIdentifier: true, AppendSpace: false}) 2162 } 2163 return cands 2164 } 2165 2166 func (c *Completer) SearchExecutableFiles(line string, origLine string, index int) readline.CandidateList { 2167 cands := c.SearchValues(line, origLine, index) 2168 files := c.ListFiles(line, []string{option.SqlExt, option.CsvqProcExt}, "") 2169 return append(cands, c.identifierList(files, false)...) 2170 } 2171 2172 func (c *Completer) SearchDirs(line string, origLine string, index int) readline.CandidateList { 2173 cands := c.SearchValues(line, origLine, index) 2174 files := c.ListFiles(line, nil, "") 2175 return append(cands, c.identifierList(files, false)...) 2176 } 2177 2178 func (c *Completer) SearchValuesWithSpace(line string, origLine string, index int) readline.CandidateList { 2179 cands := c.SearchValues(line, origLine, index) 2180 for i := range cands { 2181 cands[i].AppendSpace = true 2182 } 2183 return cands 2184 } 2185 2186 func (c *Completer) SearchValues(line string, origLine string, index int) readline.CandidateList { 2187 if cands := c.EncloseQuotation(line, origLine, index); cands != nil { 2188 return cands 2189 } 2190 2191 searchWord := strings.ToUpper(line) 2192 2193 if 0 < len(c.cursorList) { 2194 if cands := c.CursorStatus(line, origLine, index); 0 < len(cands) { 2195 return cands 2196 } 2197 } 2198 2199 var cands readline.CandidateList 2200 2201 if c.isInAndAfterSelect { 2202 cands = append(cands, c.aggregateFunctionCandidateList(line)...) 2203 cands = append(cands, c.analyticFunctionCandidateList(line)...) 2204 } 2205 2206 if len(searchWord) < 1 { 2207 return cands 2208 } 2209 2210 var list []string 2211 if 1 < len(line) { 2212 list = append(list, c.runinfoList...) 2213 list = append(list, c.environmentVariableList(line)...) 2214 } 2215 list = append(list, c.varList...) 2216 list = append(list, c.funcList...) 2217 list = append(list, 2218 "TRUE", 2219 "FALSE", 2220 "UNKNOWN", 2221 "NULL", 2222 ) 2223 list = append(list, c.constants...) 2224 2225 for _, s := range list { 2226 if strings.HasPrefix(strings.ToUpper(s), searchWord) { 2227 cands = append(cands, readline.Candidate{Name: []rune(s), FormatAsIdentifier: false, AppendSpace: false}) 2228 } 2229 } 2230 2231 list = list[:0] 2232 list = append(list, 2233 "AND", 2234 "OR", 2235 "NOT", 2236 "IS", 2237 "BETWEEN", 2238 "LIKE", 2239 "IN", 2240 "ANY", 2241 "ALL", 2242 "EXISTS", 2243 "CASE", 2244 ) 2245 2246 if 0 < len(c.cursorList) { 2247 list = append(list, "CURSOR") 2248 } 2249 2250 if 0 <= c.lastIdx && c.tokens[c.lastIdx].Token == '(' { 2251 list = append(list, "SELECT") 2252 } 2253 2254 for _, s := range list { 2255 if strings.HasPrefix(strings.ToUpper(s), searchWord) { 2256 cands = append(cands, readline.Candidate{Name: []rune(s), FormatAsIdentifier: false, AppendSpace: true}) 2257 } 2258 } 2259 2260 if caseCands := c.CaseExpression(line, origLine, index); 0 < len(caseCands) { 2261 cands = append(cands, caseCands...) 2262 } 2263 2264 return cands 2265 } 2266 2267 func (c *Completer) CursorStatus(line string, origLine string, index int) readline.CandidateList { 2268 return c.completeArgs( 2269 line, 2270 origLine, 2271 index, 2272 func(i int) (keywords []string, customList readline.CandidateList, breakLoop bool) { 2273 switch c.tokens[i].Token { 2274 case parser.IN: 2275 switch i { 2276 case c.lastIdx: 2277 if (0 < c.lastIdx-3 && 2278 c.tokens[c.lastIdx-3].Token == parser.CURSOR && 2279 c.tokens[c.lastIdx-1].Token == parser.IS) || 2280 0 < c.lastIdx-4 && 2281 c.tokens[c.lastIdx-4].Token == parser.CURSOR && 2282 c.tokens[c.lastIdx-2].Token == parser.IS && 2283 c.tokens[c.lastIdx-1].Token == parser.NOT { 2284 return nil, c.candidateList([]string{"RANGE"}, false), true 2285 } 2286 } 2287 case parser.NOT: 2288 switch i { 2289 case c.lastIdx: 2290 if 0 < c.lastIdx-3 && 2291 c.tokens[c.lastIdx-3].Token == parser.CURSOR && 2292 c.tokens[c.lastIdx-1].Token == parser.IS { 2293 return nil, c.candidateList([]string{ 2294 "IN RANGE", 2295 "OPEN", 2296 }, false), true 2297 } 2298 } 2299 case parser.IS: 2300 switch i { 2301 case c.lastIdx: 2302 if 0 < c.lastIdx-2 && c.tokens[c.lastIdx-2].Token == parser.CURSOR { 2303 return []string{"NOT"}, c.candidateList([]string{ 2304 "IN RANGE", 2305 "OPEN", 2306 }, false), true 2307 } 2308 } 2309 case parser.CURSOR: 2310 switch i { 2311 case c.lastIdx: 2312 return c.cursorList, nil, true 2313 case c.lastIdx - 1: 2314 return []string{"IS"}, c.candidateList([]string{"COUNT"}, false), true 2315 } 2316 case parser.IDENTIFIER, parser.COUNT, parser.OPEN, parser.RANGE: 2317 return nil, nil, false 2318 } 2319 return nil, nil, true 2320 }, 2321 ) 2322 } 2323 2324 func (c *Completer) caseExpressionIsNotEnclosed() bool { 2325 if 0 < len(c.tokens) && c.tokens[0].Token == parser.CASE { 2326 return false 2327 } 2328 2329 var blockLevel = 0 2330 for i := 0; i < len(c.tokens); i++ { 2331 switch c.tokens[i].Token { 2332 case parser.CASE: 2333 blockLevel++ 2334 case parser.END: 2335 blockLevel-- 2336 } 2337 } 2338 return 0 < blockLevel 2339 } 2340 2341 func (c *Completer) CaseExpression(line string, origLine string, index int) readline.CandidateList { 2342 caseExperEnclosed := true 2343 2344 return c.completeArgs( 2345 line, 2346 origLine, 2347 index, 2348 func(i int) (keywords []string, customList readline.CandidateList, breakLoop bool) { 2349 if caseExperEnclosed { 2350 if caseExperEnclosed = !c.caseExpressionIsNotEnclosed(); caseExperEnclosed { 2351 return nil, nil, true 2352 } 2353 } 2354 2355 switch c.tokens[i].Token { 2356 case parser.ELSE: 2357 if i < c.lastIdx { 2358 return nil, c.filteredCandidateList(line, []string{"END"}, false), true 2359 } 2360 return nil, nil, true 2361 case parser.THEN: 2362 if i < c.lastIdx { 2363 return []string{"WHEN", "ELSE"}, c.filteredCandidateList(line, []string{"END"}, false), true 2364 } 2365 return nil, nil, true 2366 case parser.WHEN: 2367 if i < c.lastIdx { 2368 return []string{"THEN"}, nil, true 2369 } 2370 return nil, nil, true 2371 case parser.CASE: 2372 return []string{"WHEN"}, nil, true 2373 } 2374 return nil, nil, false 2375 }, 2376 ) 2377 } 2378 2379 func (c *Completer) EncloseQuotation(line string, origLine string, _ int) readline.CandidateList { 2380 runes := []rune(line) 2381 if 0 < len(runes) && readline.IsQuotationMark(runes[0]) && !readline.LiteralIsEnclosed(runes[0], []rune(origLine)) { 2382 return c.candidateList([]string{string(append([]rune(line), runes[0]))}, false) 2383 } 2384 2385 return nil 2386 } 2387 2388 func (c *Completer) ListFiles(path string, includeExt []string, repository string) []string { 2389 list := make([]string, 0, 10) 2390 2391 if 0 < len(path) && (path[0] == '"' || path[0] == '\'' || path[0] == '`') { 2392 path = path[1:] 2393 } 2394 searchWord := strings.ToUpper(path) 2395 2396 var defaultDir string 2397 if len(path) < 1 || (!filepath.IsAbs(path) && path != "." && path != ".." && filepath.Base(path) == path) { 2398 if 0 < len(repository) { 2399 defaultDir = repository 2400 } else { 2401 defaultDir, _ = os.Getwd() 2402 } 2403 path = defaultDir 2404 2405 for _, v := range []string{".", ".."} { 2406 if len(searchWord) < 1 || strings.HasPrefix(strings.ToUpper(v), searchWord) { 2407 list = append(list, v) 2408 } 2409 } 2410 } 2411 2412 if _, err := os.Stat(path); err != nil { 2413 path = filepath.Dir(path) 2414 } 2415 2416 if files, err := os.ReadDir(path); err == nil { 2417 2418 for _, f := range files { 2419 if f.Name()[0] == '.' { 2420 continue 2421 } 2422 2423 if !f.IsDir() && (len(includeExt) < 1 || !query.InStrSliceWithCaseInsensitive(filepath.Ext(f.Name()), includeExt)) { 2424 continue 2425 } 2426 2427 fpath := f.Name() 2428 if len(defaultDir) < 1 { 2429 if path == "." || path == "."+string(os.PathSeparator) { 2430 fpath = "." + string(os.PathSeparator) + fpath 2431 } else { 2432 fpath = filepath.Join(path, fpath) 2433 } 2434 } 2435 if f.IsDir() { 2436 fpath = fpath + string(os.PathSeparator) 2437 } 2438 if len(searchWord) < 1 || strings.HasPrefix(strings.ToUpper(fpath), searchWord) { 2439 list = append(list, fpath) 2440 } 2441 } 2442 } 2443 2444 return list 2445 } 2446 2447 func (c *Completer) AllColumnList() []string { 2448 m := make(map[string]bool) 2449 c.scope.Blocks[0].TemporaryTables.Range(func(key, value interface{}) bool { 2450 col := c.columnList(value.(*query.View)) 2451 for _, s := range col { 2452 if _, ok := m[s]; !ok { 2453 m[s] = true 2454 } 2455 } 2456 return true 2457 }) 2458 2459 c.scope.Tx.CachedViews.Range(func(key, value interface{}) bool { 2460 col := c.columnList(value.(*query.View)) 2461 for _, s := range col { 2462 if _, ok := m[s]; !ok { 2463 m[s] = true 2464 } 2465 } 2466 return true 2467 }) 2468 2469 list := make([]string, 0, len(m)) 2470 for k := range m { 2471 list = append(list, k) 2472 } 2473 sort.Strings(list) 2474 return list 2475 } 2476 2477 func (c *Completer) ColumnList(tableName string, repository string) []string { 2478 if list, ok := c.tableColumns[tableName]; ok { 2479 return list 2480 } 2481 2482 if view, ok := c.scope.Blocks[0].TemporaryTables.Load(strings.ToUpper(tableName)); ok { 2483 list := c.columnList(view) 2484 c.tableColumns[tableName] = list 2485 return list 2486 } 2487 2488 if fpath, err := query.CreateFilePath(parser.Identifier{Literal: tableName}, repository); err == nil { 2489 if view, ok := c.scope.Tx.CachedViews.Load(strings.ToUpper(fpath)); ok { 2490 list := c.columnList(view) 2491 c.tableColumns[tableName] = list 2492 return list 2493 } 2494 } 2495 if fpath, err := query.SearchFilePathFromAllTypes(parser.Identifier{Literal: tableName}, repository); err == nil { 2496 if view, ok := c.scope.Tx.CachedViews.Load(strings.ToUpper(fpath)); ok { 2497 list := c.columnList(view) 2498 c.tableColumns[tableName] = list 2499 return list 2500 } 2501 } 2502 2503 return nil 2504 } 2505 2506 func (*Completer) columnList(view *query.View) []string { 2507 var list []string 2508 for _, h := range view.Header { 2509 list = append(list, h.Column) 2510 } 2511 return list 2512 } 2513 2514 func (c *Completer) completeArgs( 2515 line string, 2516 origLine string, 2517 index int, 2518 fn func(i int) (keywords []string, customList readline.CandidateList, breakLoop bool), 2519 ) readline.CandidateList { 2520 if cands := c.EncloseQuotation(line, origLine, index); cands != nil { 2521 return cands 2522 } 2523 2524 baseInterval := 1 2525 if 0 < len(line) { 2526 baseInterval++ 2527 } 2528 if 0 < len(c.tokens) && 0 < len(line) { 2529 c.tokens[len(c.tokens)-1].Token = parser.IDENTIFIER 2530 } 2531 2532 var keywords []string 2533 var customList readline.CandidateList 2534 var breakLoop bool 2535 2536 for i := len(c.tokens) - 1; i >= 0; i-- { 2537 if keywords, customList, breakLoop = fn(i); breakLoop { 2538 break 2539 } 2540 } 2541 2542 cands := c.filteredCandidateList(line, keywords, true) 2543 cands.Sort() 2544 if 0 < len(customList) { 2545 cands = append(cands, customList...) 2546 } 2547 return cands 2548 } 2549 2550 func (c *Completer) UpdateTokens(line string, origLine string) { 2551 c.tokens = c.tokens[:0] 2552 s := new(parser.Scanner) 2553 s.Init(origLine, "", false, c.scope.Tx.Flags.AnsiQuotes) 2554 for { 2555 t, _ := s.Scan() 2556 if t.Token == parser.EOF { 2557 break 2558 } 2559 c.tokens = append(c.tokens, t) 2560 } 2561 2562 c.setCursorIsInAndAfterSelect() 2563 2564 if 0 < len(c.tokens) { 2565 c.tokens = c.tokens[c.searchStartIndex():] 2566 } 2567 2568 c.combineSubqueryTokens() 2569 c.combineTableObject() 2570 c.combineFunction() 2571 c.SetLastIndex(line) 2572 } 2573 2574 func (c *Completer) SetLastIndex(line string) { 2575 c.lastIdx = len(c.tokens) - 1 2576 if 0 < len(c.tokens) && 0 < len(line) { 2577 c.tokens[len(c.tokens)-1].Token = parser.IDENTIFIER 2578 c.lastIdx-- 2579 } 2580 } 2581 2582 func (c *Completer) setCursorIsInAndAfterSelect() { 2583 c.isInAndAfterSelect = false 2584 2585 blockLevel := 0 2586 2587 InAndAfterSelectLoop: 2588 for i := len(c.tokens) - 1; i >= 0; i-- { 2589 switch c.tokens[i].Token { 2590 case ';', 2591 parser.FROM, parser.WHERE, parser.GROUP, parser.HAVING, parser.LIMIT, parser.FETCH, parser.OFFSET, 2592 parser.INTO: 2593 2594 break InAndAfterSelectLoop 2595 case '(': 2596 blockLevel-- 2597 case ')': 2598 blockLevel++ 2599 case parser.SELECT: 2600 if blockLevel <= 0 { 2601 c.isInAndAfterSelect = true 2602 break InAndAfterSelectLoop 2603 } 2604 case parser.BY: 2605 if blockLevel <= 0 && 0 < i && c.tokens[i-1].Token == parser.ORDER { 2606 c.isInAndAfterSelect = true 2607 break InAndAfterSelectLoop 2608 } 2609 } 2610 } 2611 } 2612 2613 func (c *Completer) searchStartIndex() int { 2614 idx := 0 2615 blockLevel := 0 2616 isStatement := false 2617 2618 StartIndexLoop: 2619 for i := len(c.tokens) - 1; i >= 0; i-- { 2620 switch c.tokens[i].Token { 2621 case ';': 2622 idx = i + 1 2623 isStatement = true 2624 break StartIndexLoop 2625 case '(': 2626 blockLevel-- 2627 switch { 2628 case blockLevel < 0: 2629 switch { 2630 case i+1 < len(c.tokens) && c.tokens[i+1].Token == parser.SELECT: 2631 idx = i + 1 2632 break StartIndexLoop 2633 case 0 <= i-1 && (c.isTableObject(c.tokens[i-1]) || c.isFunction(c.tokens[i-1])): 2634 idx = i - 1 2635 break StartIndexLoop 2636 } 2637 } 2638 case ')': 2639 blockLevel++ 2640 } 2641 } 2642 2643 if 0 < len(c.varList) && 2644 (idx == 0 || isStatement) && 2645 (idx < len(c.tokens) && (c.tokens[idx].Token == parser.SELECT || c.tokens[idx].Token == parser.WITH)) { 2646 c.selectIntoEnabled = true 2647 } else { 2648 c.selectIntoEnabled = false 2649 } 2650 return idx 2651 } 2652 2653 func (c *Completer) combineSubqueryTokens() { 2654 combined := make([]parser.Token, 0, cap(c.tokens)) 2655 blockLevel := 0 2656 for i := 0; i < len(c.tokens); i++ { 2657 if 0 < blockLevel { 2658 switch c.tokens[i].Token { 2659 case '(': 2660 blockLevel++ 2661 case ')': 2662 blockLevel-- 2663 if blockLevel == 0 { 2664 combined = append(combined, parser.Token{Token: parser.IDENTIFIER, Literal: dummySubquery}) 2665 } 2666 } 2667 continue 2668 } 2669 2670 if c.tokens[i].Token == '(' && i+1 < len(c.tokens) && c.tokens[i+1].Token == parser.SELECT { 2671 blockLevel++ 2672 i++ 2673 } else { 2674 combined = append(combined, c.tokens[i]) 2675 } 2676 2677 } 2678 c.tokens = combined 2679 } 2680 2681 func (c *Completer) combineTableObject() { 2682 combined := make([]parser.Token, 0, cap(c.tokens)) 2683 blockLevel := 0 2684 tableIdx := 0 2685 for i := 0; i < len(c.tokens); i++ { 2686 if 0 < blockLevel { 2687 switch c.tokens[i].Token { 2688 case '(': 2689 blockLevel++ 2690 case ')': 2691 blockLevel-- 2692 if blockLevel == 0 { 2693 lit := dummyTableObject 2694 if 0 < tableIdx { 2695 lit = c.tokens[tableIdx].Literal 2696 } 2697 combined = append(combined, parser.Token{Token: parser.IDENTIFIER, Literal: lit}) 2698 } 2699 case ',': 2700 if tableIdx == 0 && blockLevel == 1 { 2701 tableIdx = i + 1 2702 } 2703 } 2704 continue 2705 } 2706 2707 if 1 < i && c.isTableObject(c.tokens[i]) && i+1 < len(c.tokens) && c.tokens[i+1].Token == '(' { 2708 blockLevel++ 2709 i++ 2710 } else { 2711 combined = append(combined, c.tokens[i]) 2712 } 2713 2714 } 2715 c.tokens = combined 2716 } 2717 2718 func (c *Completer) combineFunction() { 2719 combined := make([]parser.Token, 0, cap(c.tokens)) 2720 blockLevel := 0 2721 funcName := "" 2722 for i := 0; i < len(c.tokens); i++ { 2723 if 0 < blockLevel { 2724 switch c.tokens[i].Token { 2725 case '(': 2726 blockLevel++ 2727 case ')': 2728 blockLevel-- 2729 if blockLevel == 0 { 2730 if i+2 < len(c.tokens) && c.tokens[i+1].Token == parser.OVER && c.tokens[i+2].Token == '(' { 2731 blockLevel++ 2732 i = i + 2 2733 } else if i+4 < len(c.tokens) && c.tokens[i+3].Token == parser.OVER && c.tokens[i+4].Token == '(' { 2734 blockLevel++ 2735 i = i + 4 2736 } else { 2737 combined = append(combined, parser.Token{Token: parser.FUNCTION, Literal: funcName}) 2738 } 2739 } 2740 } 2741 continue 2742 } 2743 2744 if 0 < i && c.isFunction(c.tokens[i]) && i+1 < len(c.tokens) && c.tokens[i+1].Token == '(' { 2745 funcName = c.tokens[i].Literal 2746 blockLevel++ 2747 i++ 2748 } else { 2749 combined = append(combined, c.tokens[i]) 2750 } 2751 2752 } 2753 c.tokens = combined 2754 } 2755 2756 func (c *Completer) isTableObject(token parser.Token) bool { 2757 switch token.Token { 2758 case parser.CSV, parser.JSON, parser.JSONL, parser.FIXED, parser.LTSV, parser.JSON_TABLE: 2759 return true 2760 } 2761 return false 2762 } 2763 2764 func (c *Completer) isFunction(token parser.Token) bool { 2765 if token.Token == parser.IDENTIFIER { 2766 if _, ok := query.Functions[strings.ToUpper(token.Literal)]; ok { 2767 return true 2768 } 2769 return query.InStrSliceWithCaseInsensitive(token.Literal, c.userFuncList) 2770 } 2771 2772 return token.Token == parser.SUBSTRING || 2773 token.Token == parser.JSON_OBJECT || 2774 token.Token == parser.IF || 2775 token.Token == parser.AGGREGATE_FUNCTION || 2776 token.Token == parser.COUNT || 2777 token.Token == parser.LIST_FUNCTION || 2778 token.Token == parser.ANALYTIC_FUNCTION || 2779 token.Token == parser.FUNCTION_NTH || 2780 token.Token == parser.FUNCTION_WITH_INS 2781 } 2782 2783 func (c *Completer) BracketIsEnclosed() bool { 2784 var blockLevel = 0 2785 for i := 0; i < len(c.tokens); i++ { 2786 switch c.tokens[i].Token { 2787 case '(': 2788 blockLevel++ 2789 case ')': 2790 blockLevel-- 2791 } 2792 } 2793 return blockLevel < 1 2794 } 2795 2796 func (c *Completer) candidateList(list []string, appendSpace bool) readline.CandidateList { 2797 cands := make(readline.CandidateList, 0, len(list)) 2798 for _, v := range list { 2799 cands = append(cands, c.candidate(v, appendSpace)) 2800 } 2801 return cands 2802 } 2803 2804 func (c *Completer) identifierList(list []string, appendSpace bool) readline.CandidateList { 2805 cands := make(readline.CandidateList, 0, len(list)) 2806 for _, v := range list { 2807 cands = append(cands, c.identifier(v, appendSpace)) 2808 } 2809 return cands 2810 } 2811 2812 func (c *Completer) filteredCandidateList(line string, list []string, appendSpace bool) readline.CandidateList { 2813 searchWord := strings.ToUpper(line) 2814 2815 cands := make(readline.CandidateList, 0, len(list)) 2816 for _, v := range list { 2817 if len(searchWord) < 1 || strings.HasPrefix(strings.ToUpper(v), searchWord) { 2818 cands = append(cands, c.candidate(v, appendSpace)) 2819 } 2820 } 2821 return cands 2822 } 2823 2824 func (c *Completer) candidate(candidate string, appendSpace bool) readline.Candidate { 2825 return readline.Candidate{Name: []rune(candidate), FormatAsIdentifier: false, AppendSpace: appendSpace} 2826 } 2827 2828 func (c *Completer) identifier(candidate string, appendSpace bool) readline.Candidate { 2829 return readline.Candidate{Name: []rune(candidate), FormatAsIdentifier: true, AppendSpace: appendSpace} 2830 } 2831 2832 func (c *Completer) aggregateFunctionCandidateList(line string) readline.CandidateList { 2833 if len(line) < 1 { 2834 return nil 2835 } 2836 return c.filteredCandidateList(line, c.aggFuncList, false) 2837 } 2838 2839 func (c *Completer) analyticFunctionCandidateList(line string) readline.CandidateList { 2840 var cands readline.CandidateList 2841 if 0 <= c.lastIdx && c.tokens[c.lastIdx].Token == parser.FUNCTION { 2842 if query.InStrSliceWithCaseInsensitive(c.tokens[c.lastIdx].Literal, []string{ 2843 "FIRST_VALUE", 2844 "LAST_VALUE", 2845 "NTH_VALUE", 2846 "LAG_VALUE", 2847 "LEAD_VALUE", 2848 }) { 2849 cands = append(cands, c.candidate("IGNORE NULLS", true)) 2850 } 2851 2852 if query.InStrSliceWithCaseInsensitive(c.tokens[c.lastIdx].Literal, c.analyticFuncs) { 2853 cands = append(cands, c.candidate("OVER", true)) 2854 } 2855 } 2856 2857 if 0 < len(line) { 2858 cands = append(cands, c.filteredCandidateList(line, c.analyticFuncList, false)...) 2859 } 2860 return cands 2861 } 2862 2863 func (c *Completer) environmentVariableList(line string) []string { 2864 if 2 < len(line) && strings.HasPrefix(line, option.EnvironmentVariableSign+"`") { 2865 return c.enclosedEnvList 2866 } 2867 return c.envList 2868 } 2869 2870 func (c *Completer) tableFormatList() []string { 2871 list := make([]string, 0, len(option.FormatLiteral)) 2872 for _, v := range option.FormatLiteral { 2873 list = append(list, v) 2874 } 2875 sort.Strings(list) 2876 return list 2877 } 2878 2879 func (c *Completer) importFormatList() []string { 2880 list := make([]string, 0, len(option.ImportFormats)) 2881 for _, v := range option.ImportFormats { 2882 list = append(list, option.FormatLiteral[v]) 2883 } 2884 sort.Strings(list) 2885 return list 2886 } 2887 2888 func (c *Completer) encodingList() []string { 2889 list := make([]string, 0, len(text.EncodingLiteral)) 2890 for _, v := range text.EncodingLiteral { 2891 list = append(list, v) 2892 } 2893 sort.Strings(list) 2894 return list 2895 } 2896 2897 func (c *Completer) lineBreakList() []string { 2898 list := make([]string, 0, len(text.LineBreakLiteral)) 2899 for _, v := range text.LineBreakLiteral { 2900 list = append(list, v) 2901 } 2902 sort.Strings(list) 2903 return list 2904 } 2905 2906 func (c *Completer) jsonEscapeTypeList() []string { 2907 list := make([]string, 0, len(option.JsonEscapeTypeLiteral)) 2908 for _, v := range option.JsonEscapeTypeLiteral { 2909 list = append(list, v) 2910 } 2911 sort.Strings(list) 2912 return list 2913 }