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

Work on removing tuple elaboration #2381

Merged
merged 1 commit into from
Jan 16, 2022
Merged

Conversation

deusaquilus
Copy link
Collaborator

@deusaquilus deusaquilus commented Jan 16, 2022

Since we can expand a row from a quat we might not need tuple-elaboration in the first place. This would allow us to implement a more complete solution to #2340.

Query Expansion before Quats

In previous version of quill where AST had no typing information (i.e. the Quats), a query that looked like this:

case class Person(name: String, age: Int)
query[Person].filter(p => name == "Joe")

Could be translated to this:

SELECT p.? FROM Person p WHERE p.name = 'Joe'

The problem is that the tree does not know what the type of the identifier p is so which columns need to be selected from it is not known. Of course we could always do p.* but if the columns in the Person case-class are note the same ones as in the Person table the decoding of the query would fail. Therefore materializeQueryMeta was introduced to expand the query into:

query[Person].filter(p => name == "Joe").map(p => (p.name, p.age))

This then provided enough data to fully expand the query into:

SELECT p.name, p.age FROM Person p WHERE p.name = 'Joe'

It worked something like this:

NOTE: (Id -> Ident, Prop -> Property, Tup -> Tuple, BinaryOp -> BinaryOperation)

// materializeQueryMeta
query[Person].filter(p => p.name == "Joe")
  -> query[Person].filter(p => p.name == "Joe").map(x => (x.name, x.age)) 
// Parsing
query[Person].map(x => (x.name, x.age))
  -> Map(Filter(EntityQuery("Person"), Id("p"), BinaryOperation(Id("p"), BinaryOp.==, Constant("Joe"))), Ident("x"), Tup(Prop(Id("p"),"name"), Prop(Id("p"),"age"))
// SQL Query Tokenization:
// SELECT x.name AS _1, x.age AS _2 FROM Person x WHERE p.name = 'Joe'

Issues

This solution had several notable flaws. Firstly, since the actual object being decoded to is a Tuple2 (i.e. TupleX depending on the object), the real query that is generate would be:

SELECT p.name AS _1, p.age AS _2 FROM Person p WHERE p.name = 'Joe'

Normally this isn't a problem because the Quill decoders use column index as opposed to column name for decoding. However, for the Context.prepare function that returns a ResultSet as well as for ProtoQuill coproduct decodings (which rely on column names), this approach would not work. Also the _1 and _2 present an oddity for the user that is not aware of this query expansion mechanism. For this reason, a transformation to SqlQuery was introduced in order to remove all top-level aliases (the RemoveExtraAlias class used to do that). Still a better technique was desired, one that returns name and age columns for a given object as opposed to _1 and _2.

A Better Solution

Ultimately, Quats were introduced and this added type information to every query. That meant that every identifier now had information as to whether this field was a product or value, and what the fields inside it are if it were a product. That means that given this:

query[Person].filter(p => name == "Joe")
// SELECT p.? FROM Person p WHERE p.name = 'Joe'

It is known what fields are inside the p variable. The expansion goes something like this:

// Fig .1
// Parsing
query[Person] 
  -> EntityQuery("Person", Quat.Product("name"->Quat.Value, "age"->Quat.Value))
// Query Expansion
// (QP: Quat.Product("name"->Quat.Value, "age"->Quat.Value) )
  -> EntityQuery("Person", QP) 
  -> Map(Filter(EntityQuery("Person", QP), Id("p", QP), BinaryOperation(Id("p", QP), BinaryOp.==, Constant("Joe")), Ident("x"), Tup(Prop(Id("p", QP),"name"), Prop(Id("p", QP),"age"))
// SQL Query Tokenization:
// SELECT x.name AS _1, x.age AS _2 FROM Person x

This mechanism lives in ExpandNestedQueries (and ExpandSelection) where a SelectValue(Id("x", QP)) is expanded into List(SelectValue(Prop(Id("x", QP), "name")), Prop(Id("x", QP), "age"))). It was introduced in #1920 in order to correctly expand identifiers that materializeQueryMeta would not expand (e.g. identifiers in nested queries). This functionality existed before in some form but until #1920 it was not reliable.

This PR removes the materializeQueryMeta mechanism and relies on Quats to do the full identifier expansion as shown in Fig .1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant