Skip to content
This repository was archived by the owner on Sep 30, 2024. It is now read-only.

Commit ee93777

Browse files
authored
fix(search_jobs): fail validation for repo searches (#64300)
This closes a pretty big gap in the query validation for Search Jobs. We don't support repo search yet and searcher returned errors during execution, complaining about missing patterns. With this PR we fail during validation so users cannot even create these kinds of jobs. See new test cases. ## Test plan Updated unit tests
1 parent bf4eb26 commit ee93777

File tree

4 files changed

+66
-12
lines changed

4 files changed

+66
-12
lines changed

internal/search/job/jobutil/exhaustive_job.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,11 @@ func NewExhaustive(inputs *search.Inputs) (Exhaustive, error) {
9393
skipPartitioning: true,
9494
}
9595
} else if resultTypes.Has(result.TypeFile | result.TypePath) {
96-
planJob = NewTextSearchJob(b, inputs, resultTypes, repoOptions)
96+
var err error
97+
planJob, err = NewTextSearchJob(b, inputs, resultTypes, repoOptions)
98+
if err != nil {
99+
return Exhaustive{}, err
100+
}
97101
} else {
98102
// This should never happen because we checked for supported types above.
99103
return Exhaustive{}, errors.Errorf("internal error: unsupported result types %v", resultTypes)

internal/search/job/jobutil/exhaustive_job_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,32 @@ func TestNewExhaustive(t *testing.T) {
298298
(numRepos . 1)
299299
(pathRegexps . ["(?i)search.go"])
300300
(indexed . false))
301+
`),
302+
},
303+
{
304+
Name: "bytes -r:has.path(go.mod)",
305+
Query: "index:no bytes -r:has.path(go.mod)",
306+
WantPager: autogold.Expect(`
307+
(REPOPAGER
308+
(containsRefGlobs . false)
309+
(repoOpts.useIndex . no)
310+
(repoOpts.hasFileContent[0].path . go.mod)
311+
(repoOpts.hasFileContent[0].negated . true)
312+
(PARTIALREPOS
313+
(SEARCHERTEXTSEARCH
314+
(useFullDeadline . true)
315+
(patternInfo . TextPatternInfo{"bytes",filematchlimit:1000000})
316+
(numRepos . 0)
317+
(pathRegexps . ["(?i)bytes"])
318+
(indexed . false))))
319+
`),
320+
WantJob: autogold.Expect(`
321+
(SEARCHERTEXTSEARCH
322+
(useFullDeadline . true)
323+
(patternInfo . TextPatternInfo{"bytes",filematchlimit:1000000})
324+
(numRepos . 1)
325+
(pathRegexps . ["(?i)bytes"])
326+
(indexed . false))
301327
`),
302328
},
303329
}
@@ -352,6 +378,10 @@ func TestNewExhaustive_negative(t *testing.T) {
352378
{query: `index:no type:repo`},
353379
{query: `index:no type:symbol`},
354380
{query: `index:no foo select:file.owners`},
381+
// repo filter without pattern
382+
{query: `index:no -repo:has.path(go.mod)`},
383+
{query: `index:no repo:has.path(go.mod)`},
384+
{query: `index:no repo:foo`},
355385
}
356386

357387
for _, c := range tc {

internal/search/job/jobutil/job.go

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,10 @@ func NewBasicJob(inputs *search.Inputs, b query.Basic) (job.Job, error) {
129129
})
130130
}
131131

132-
searcherJob := NewTextSearchJob(b, inputs, resultTypes, repoOptions)
132+
searcherJob, err := NewTextSearchJob(b, inputs, resultTypes, repoOptions)
133+
if err != nil {
134+
return nil, err
135+
}
133136
addJob(searcherJob)
134137
}
135138
}
@@ -276,10 +279,13 @@ func NewBasicJob(inputs *search.Inputs, b query.Basic) (job.Job, error) {
276279
return basicJob, nil
277280
}
278281

279-
func NewTextSearchJob(b query.Basic, inputs *search.Inputs, types result.Types, options search.RepoOptions) job.Job {
282+
func NewTextSearchJob(b query.Basic, inputs *search.Inputs, types result.Types, options search.RepoOptions) (job.Job, error) {
280283
// searcher to use full deadline if timeout: set or we are not batch.
281284
useFullDeadline := b.GetTimeout() != nil || b.Count() != nil || inputs.Protocol != search.Batch
282-
patternInfo := toTextPatternInfo(b, types, inputs.Features, inputs.DefaultLimit())
285+
patternInfo, err := toTextPatternInfo(b, types, inputs.Features, inputs.DefaultLimit())
286+
if err != nil {
287+
return nil, err
288+
}
283289

284290
searcherJob := &searcher.TextSearchJob{
285291
PatternInfo: patternInfo,
@@ -294,7 +300,7 @@ func NewTextSearchJob(b query.Basic, inputs *search.Inputs, types result.Types,
294300
child: &reposPartialJob{searcherJob},
295301
repoOpts: options,
296302
containsRefGlobs: query.ContainsRefGlobs(b.ToParseTree()),
297-
}
303+
}, nil
298304
}
299305

300306
// orderRacingJobs ensures that searcher and repo search jobs only ever run
@@ -391,7 +397,10 @@ func NewFlatJob(searchInputs *search.Inputs, f query.Flat) (job.Job, error) {
391397
}
392398

393399
if resultTypes.Has(result.TypeStructural) {
394-
patternInfo := toTextPatternInfo(f.ToBasic(), resultTypes, searchInputs.Features, searchInputs.DefaultLimit())
400+
patternInfo, err := toTextPatternInfo(f.ToBasic(), resultTypes, searchInputs.Features, searchInputs.DefaultLimit())
401+
if err != nil {
402+
return nil, err
403+
}
395404
searcherArgs := &search.SearcherParameters{
396405
PatternInfo: patternInfo,
397406
UseFullDeadline: useFullDeadline,
@@ -671,7 +680,7 @@ func toSymbolSearchRequest(f query.Flat, feat *search.Features) (*searcher.Symbo
671680
}
672681

673682
// toTextPatternInfo converts a query to internal values that drive text search.
674-
func toTextPatternInfo(b query.Basic, resultTypes result.Types, feat *search.Features, defaultLimit int) *search.TextPatternInfo {
683+
func toTextPatternInfo(b query.Basic, resultTypes result.Types, feat *search.Features, defaultLimit int) (*search.TextPatternInfo, error) {
675684
// Handle file: and -file: filters.
676685
filesInclude, filesExclude := b.IncludeExcludeValues(query.FieldFile)
677686

@@ -691,8 +700,16 @@ func toTextPatternInfo(b query.Basic, resultTypes result.Types, feat *search.Fea
691700
selector, _ := filter.SelectPathFromString(b.FindValue(query.FieldSelect)) // Invariant: select is validated
692701
count := b.MaxResults(defaultLimit)
693702

703+
q := protocol.FromJobNode(b.Pattern)
704+
if p, ok := q.(*protocol.PatternNode); ok {
705+
if p.Value == "" && len(filesExclude) == 0 && len(filesInclude) == 0 &&
706+
len(langExclude) == 0 && len(langExclude) == 0 {
707+
return nil, errors.New("At least one of pattern and include/exclude patterns must be non-empty")
708+
}
709+
}
710+
694711
return &search.TextPatternInfo{
695-
Query: protocol.FromJobNode(b.Pattern),
712+
Query: q,
696713
IsStructuralPat: b.IsStructural(),
697714
IsCaseSensitive: b.IsCaseSensitive(),
698715
FileMatchLimit: int32(count),
@@ -707,7 +724,7 @@ func toTextPatternInfo(b query.Basic, resultTypes result.Types, feat *search.Fea
707724
CombyRule: b.FindValue(query.FieldCombyRule),
708725
Index: b.Index(),
709726
Select: selector,
710-
}
727+
}, nil
711728
}
712729

713730
func toLangFilters(aliases []string) []string {

internal/search/job/jobutil/job_test.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1393,10 +1393,10 @@ func TestToTextPatternInfo(t *testing.T) {
13931393
output: autogold.Expect(`{"Query":{"Value":"(?://).*?(?:literal).*?(?:slash)","IsNegated":false,"IsRegExp":true},"IsStructuralPat":false,"CombyRule":"","IsCaseSensitive":false,"FileMatchLimit":30,"Index":"yes","Select":[],"IncludePaths":null,"ExcludePaths":"","IncludeLangs":null,"ExcludeLangs":null,"PathPatternsAreCaseSensitive":false,"PatternMatchesContent":true,"PatternMatchesPath":true,"Languages":null}`),
13941394
}, {
13951395
input: `repo:contains.path(Dockerfile)`,
1396-
output: autogold.Expect(`{"Query":{"Value":"","IsNegated":false,"IsRegExp":true},"IsStructuralPat":false,"CombyRule":"","IsCaseSensitive":false,"FileMatchLimit":30,"Index":"yes","Select":[],"IncludePaths":null,"ExcludePaths":"","IncludeLangs":null,"ExcludeLangs":null,"PathPatternsAreCaseSensitive":false,"PatternMatchesContent":true,"PatternMatchesPath":true,"Languages":null}`),
1396+
output: autogold.Expect("Error"),
13971397
}, {
13981398
input: `repohasfile:Dockerfile`,
1399-
output: autogold.Expect(`{"Query":{"Value":"","IsNegated":false,"IsRegExp":true},"IsStructuralPat":false,"CombyRule":"","IsCaseSensitive":false,"FileMatchLimit":30,"Index":"yes","Select":[],"IncludePaths":null,"ExcludePaths":"","IncludeLangs":null,"ExcludeLangs":null,"PathPatternsAreCaseSensitive":false,"PatternMatchesContent":true,"PatternMatchesPath":true,"Languages":null}`),
1399+
output: autogold.Expect("Error"),
14001400
}, {
14011401
input: `repo:^github\.com/sgtest/go-diff$ make(:[1]) index:only patterntype:structural count:3`,
14021402
output: autogold.Expect(`{"Query":{"Value":"make(:[1])","IsNegated":false,"IsRegExp":false},"IsStructuralPat":true,"CombyRule":"","IsCaseSensitive":false,"FileMatchLimit":3,"Index":"only","Select":[],"IncludePaths":null,"ExcludePaths":"","IncludeLangs":null,"ExcludeLangs":null,"PathPatternsAreCaseSensitive":false,"PatternMatchesContent":true,"PatternMatchesPath":true,"Languages":null}`),
@@ -1434,7 +1434,10 @@ func TestToTextPatternInfo(t *testing.T) {
14341434
}
14351435
b := plan[0]
14361436
resultTypes := computeResultTypes(b, query.SearchTypeLiteral, defaultResultTypes)
1437-
p := toTextPatternInfo(b, resultTypes, &feat, limits.DefaultMaxSearchResults)
1437+
p, err := toTextPatternInfo(b, resultTypes, &feat, limits.DefaultMaxSearchResults)
1438+
if err != nil {
1439+
return "Error"
1440+
}
14381441
v, _ := json.Marshal(p)
14391442
return string(v)
14401443
}

0 commit comments

Comments
 (0)