Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework parseFacets, disallow 'a as orderdesc: b' #1230

Closed
wants to merge 13 commits into from
Closed

Conversation

srh
Copy link

@srh srh commented Jul 23, 2017

A lot of parsing functions are implemented in the following style:

for it.Next() {
    if case1 {
        make sure case1 valid
        ...     
    } else if case2 {
        make sure case2 valid
        ...
    } else if case3 {
        make sure case3 valid
        ...
    } ...
}

This is really difficult to work with because each parsing case needs to handle any other possible parsing state. There's basically n^2 stuff to think about.

It also does a poor job of expressing the intent of what is supposed to be parsed. The reason is, all the code paths end up heading to the same place, and it's hard to tell which behaviors are intentional. For example, was this supposed to be valid: @facets(a as orderasc: b) Probably not. But is @facets() supposed to be treated the same as @facets, by setting AllKeys to true? I have no idea -- the code just spills into that case, but there's nothing to indicate whether the possibility was contemplated by the developers.

It's much better to make parsing functions in a manner such that each point in the function body corresponds to a particular kind of place in the parse tree. It's analogous to what happens when you replace an event loop with a stateful object with a goroutine.

It's also a good idea to make liberal use of helper functions like tryParseItemType and trySkipItemVal, or others as we might create.

It looks like due to the way the code was originally written, a lot of parsing code is using this for it.Next() style. I think whenever we work on an old parsing function which uses that style and has gotten too complicated, we should consider refactoring it like this PR does, depending on the time/effort/correctness trade-off.


So anyway,

This PR has one material change to the behavior of parseFacets. It disallows @facets(a as orderasc: b).

But there's also the question: Should @facets() still be treated like @facets? Or should it be treated like an empty list of facets? (My guess is we should change it to act like an empty list of facets, because I don't think users would appreciate having a special case.)


This change is Reviewable

@srh
Copy link
Author

srh commented Jul 23, 2017

Review status: 0 of 3 files reviewed at latest revision, 1 unresolved discussion.


gql/parser.go, line 1654 at r1 (raw file):

	if _, ok := tryParseItemType(it, itemRightRound); ok {
		// TODO: Pre-existing code did all keys on @facets(), instead of no keys.  Might be a

This TODO should be replaced with a comment, and either facets.AllKeys should be set to true or not.


Comments from Reviewable

@pawanrawal
Copy link
Contributor

:lgtm: Looks a good change. Makes the code more understandable and easier to maintain. Got some questions and comments.


Review status: 0 of 3 files reviewed at latest revision, 3 unresolved discussions.


gql/parser.go, line 1654 at r1 (raw file):

Previously, srh (Sam Hughes) wrote…

This TODO should be replaced with a comment, and either facets.AllKeys should be set to true or not.

To me sounds like an error or a mistake by the user. We should return an error.


gql/parser.go, line 1616 at r2 (raw file):

	// Now try to consume "as".
	if !trySkipItemVal(it, "as") {
		name1 = collectName(it, name1)

I think collectName doesn't need the val as the second argument. It can just start with it.Item().Val. Maybe add a TODO to check for that.


gql/parser.go, line 1706 at r2 (raw file):

		}
		if item, ok := tryParseItemType(it, itemComma); !ok {
			if len(facets.Keys) < 2 {

Why this check?


Comments from Reviewable

@srh
Copy link
Author

srh commented Jul 25, 2017

Review status: 0 of 3 files reviewed at latest revision, 3 unresolved discussions.


gql/parser.go, line 1654 at r1 (raw file):

Previously, pawanrawal (Pawan Rawal) wrote…

To me sounds like an error or a mistake by the user. We should return an error.

I think there's the potential that generated queries would produce @facets() when their list of facets is empty. I think users wouldn't want to have to work around the edge case of an error. What they'd want is consistent behavior, with an empty facets document for that edge. Otherwise, they'd have to check if their request will retrieve no facets and remove the @facets part entirely, and also add a special case to their handling of the query result.


gql/parser.go, line 1616 at r2 (raw file):

Previously, pawanrawal (Pawan Rawal) wrote…

I think collectName doesn't need the val as the second argument. It can just start with it.Item().Val. Maybe add a TODO to check for that.

We pass an expression val+item.Val in one of its other callers.


gql/parser.go, line 1706 at r2 (raw file):

Previously, pawanrawal (Pawan Rawal) wrote…

Why this check?

The function either returns an error (which means there was definitely a parse error), or it returns that it could not parse but you could try parseFilter instead. We know it's not a valid parseFilter after we've seen a comma. I'll add commenting to make this more clear.


Comments from Reviewable

@ashwin95r
Copy link
Contributor

Just have one comment. :lgtm:


Review status: 0 of 3 files reviewed at latest revision, 5 unresolved discussions.


gql/parser.go, line 1674 at r2 (raw file):

		{
			if facetItem.varName != "" {
				facetVar[facetItem.facetName] = facetItem.varName

Looks like, this will allow @facets(a as w1, a as w2) and @facets(a as w1, b as w1). Both of them should error out. Though I think they'll be caught later. Would be nice to have some tests covering this.


Comments from Reviewable

@srh
Copy link
Author

srh commented Jul 25, 2017

Review status: 0 of 3 files reviewed at latest revision, 4 unresolved discussions.


gql/parser.go, line 1674 at r2 (raw file):

Previously, ashwin95r (Ashwin Ramesh) wrote…

Looks like, this will allow @facets(a as w1, a as w2) and @facets(a as w1, b as w1). Both of them should error out. Though I think they'll be caught later. Would be nice to have some tests covering this.

The former will get caught later, duplicate variables. But the latter just overwrites the map key.

We allow duplicate facet keys for some reason. Since we have that, we can disallow multiple variable assignments in the deduplication logic. Or do we want to assign to both variables?


Comments from Reviewable

@ashwin95r
Copy link
Contributor

Review status: 0 of 3 files reviewed at latest revision, 4 unresolved discussions.


gql/parser.go, line 1674 at r2 (raw file):

Previously, srh (Sam Hughes) wrote…

The former will get caught later, duplicate variables. But the latter just overwrites the map key.

We allow duplicate facet keys for some reason. Since we have that, we can disallow multiple variable assignments in the deduplication logic. Or do we want to assign to both variables?

I think we should disallow assigning multiple variables to the same key. I can't think of any case where this would be necessary.

And the deduplication I think is to be nicer in case the user gave the same facet twice by mistake.


Comments from Reviewable

@pawanrawal
Copy link
Contributor

Review status: 0 of 3 files reviewed at latest revision, 3 unresolved discussions.


gql/parser.go, line 1654 at r1 (raw file):

Previously, srh (Sam Hughes) wrote…

I think there's the potential that generated queries would produce @facets() when their list of facets is empty. I think users wouldn't want to have to work around the edge case of an error. What they'd want is consistent behavior, with an empty facets document for that edge. Otherwise, they'd have to check if their request will retrieve no facets and remove the @facets part entirely, and also add a special case to their handling of the query result.

Ok, sounds good.


gql/parser.go, line 1616 at r2 (raw file):

Previously, srh (Sam Hughes) wrote…

We pass an expression val+item.Val in one of its other callers.

Ok


Comments from Reviewable

@srh
Copy link
Author

srh commented Jul 25, 2017

Review status: 0 of 4 files reviewed at latest revision, 2 unresolved discussions.


gql/parser.go, line 1654 at r1 (raw file):

Previously, pawanrawal (Pawan Rawal) wrote…

Ok, sounds good.

If we just change the parsing code, it looks like users will still have to handle the special case of there being no @facets document. It seems we do this sort of thing, removing empty result sets, a lot in general. So I don't know that I want to return an empty @facets document if that's not the sort of thing we usually do.


gql/parser.go, line 1674 at r2 (raw file):

Previously, ashwin95r (Ashwin Ramesh) wrote…

I think we should disallow assigning multiple variables to the same key. I can't think of any case where this would be necessary.

And the deduplication I think is to be nicer in case the user gave the same facet twice by mistake.

Okay, assigning multiple variable names for a key is disallowed now.


Comments from Reviewable

@srh
Copy link
Author

srh commented Jul 25, 2017

In master as of 83b4d74.

@srh srh closed this Jul 25, 2017
@srh srh deleted the sam/parsing branch July 25, 2017 23:07
@manishrjain
Copy link
Contributor

manishrjain commented Jul 27, 2017

How do we retrieve all the facets now, without knowing the keys?

Update: Looks like @facets would achieve that, while @facets() would return empty. That's nice!

jarifibrahim pushed a commit that referenced this pull request Mar 16, 2020
Important changes
```
 - Changes to overlap check in compaction.
 - Remove 'this entry should've been caught' log.
 - Changes to write stalling on levels 0 and 1.
 - Compression is disabled by default in Badger.
 - Bloom filter caching in a separate ristretto cache.
 - Compression/Encryption in background.
 - Disable cache by default in badger.
```

The following new changes are being added from badger
`git log ab4352b00a17...91c31ebe8c22`

```
91c31eb Disable cache by default (#1257)
eaf64c0 Add separate cache for bloom filters (#1260)
1bcbefc Add BypassDirLock option (#1243)
c6c1e5e Add support for watching nil prefix in subscribe API (#1246)
b13b927 Compress/Encrypt Blocks in the background (#1227)
bdb2b13 fix changelog for v2.0.2 (#1244)
8dbc982 Add Dkron to README (#1241)
3d95b94 Remove coveralls from Travis Build(#1219)
5b4c0a6 Fix ValueThreshold for in-memory mode (#1235)
617ed7c Initialize vlog before starting compactions in db.Open (#1226)
e908818 Update CHANGELOG for Badger 2.0.2 release. (#1230)
bce069c Fix int overflow for 32bit (#1216)
e029e93 Remove ExampleDB_Subscribe Test (#1214)
8734e3a Add missing package to README for badger.NewEntry (#1223)
78d405a Replace t.Fatal with require.NoError in tests (#1213)
c51748e Fix flaky TestPageBufferReader2 test (#1210)
eee1602 Change else-if statements to idiomatic switch statements. (#1207)
3e25d77 Rework concurrency semantics of valueLog.maxFid (#1184) (#1187)
4676ca9 Add support for caching bloomfilters (#1204)
c3333a5 Disable compression and set ZSTD Compression Level to 1 (#1191)
0acb3f6 Fix L0/L1 stall test (#1201)
7e5a956 Support disabling the cache completely. (#1183) (#1185)
82381ac Update ristretto to version  8f368f2 (#1195)
3747be5 Improve write stalling on level 0 and 1
5870b7b Run all tests on CI (#1189)
01a00cb Add Jaegar to list of projects (#1192)
9d6512b Use fastRand instead of locked-rand in skiplist (#1173)
2698bfc Avoid sync in inmemory mode (#1190)
2a90c66 Remove the 'this entry should've caught' log from value.go (#1170)
0a06173 Fix checkOverlap in compaction (#1166)
0f2e629 Fix windows build (#1177)
03af216 Fix commit sha for WithInMemory in CHANGELOG. (#1172)
23a73cd Update CHANGELOG for v2.0.1 release. (#1181)
465f28a Cast sz to uint32 to fix compilation on 32 bit (#1175)
ea01d38 Rename option builder from WithInmemory to WithInMemory. (#1169)
df99253 Remove ErrGCInMemoryMode in CHANGELOG. (#1171)
8dfdd6d Adding changes for 2.0.1 so far (#1168)
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

4 participants