You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

143 lines
6.6 KiB
Plaintext

Todo:
- Operators
- BETWEEN
- LIKE/ILIKE?
- ISNULL / NOTNULL
- IS ...
- Condition modifiers
x allOf
x anyOf
x not
- Boolean logic
x anyOf(...)
x allOf(...)
Docs:
- Instruct users to report it if raqb generates invalid SQL, as this is considered a bug in raqb, regardless of the input (except when it's caused by `unsafeSQL`)
Considerations:
- Disallow non-lowercase column/table names entirely due to PostgreSQL weirdness? Or just cast them to lowercase?
----
foo = ANY (subquery)
foo = ANY (array)
foo = IN (subquery)
foo = IN (a, b, c)
alias: SOME -> ANY
----
Recommendations to make:
- Express moreThanOrEqualTo as not(lessThan(...))
- Express lessThanOrEqualTo as not(moreThan(...))
----
Ideas:
- Make placeholders typed so that we can warn the user when they try to pass in invalid things?
----
Things to check:
- How are NULLs handled in all cases/permutations? Is that handling correct?
- Maybe rename `sqlExpression` to `sqlFragment` or so, to avoid confusion with `expression`? Or is it `expression` that needs to be renamed to something else, like `predicate` or `assertion`?
----
Boolean operations:
_,
not {
equals,
moreThan,
lessThan {
_,
anyOf,
allOf {
ARRAY,
PLACEHOLDER<ARRAY>
}
LITERAL,
PLACEHOLDER<LITERAL>
}
}
Considerations:
- Allow nested not() modifiers? For composability with things that use not() themselves
{ foo: anyOf([ 30, moreThan(42) ]) } -> WHERE foo = 30 OR foo > 42
{ foo: anyOf([ 30, 42 ]) } -> WHERE foo = ANY(ARRAY[30, 42])
{ foo: anyOf([ moreThan(30), moreThan(42) ]) } -> WHERE foo > ANY(ARRAY[30, 42])
Group into condition types, and based on the count of each, determine how to represent that particular type, and then glue the types together
eg. WHERE foo > ANY(ARRAY[30, 42]) OR foo < 42 OR foo = ANY(ARRAY[30, 42])
Make $or/$and abstraction to make this gluing easier in multiple places
----
MARKER: Relation resolution
- Generate relation queries after executing their base query, because by then we have access to the necessary schema information (as we are in the execution phase)
- Eventually replace this with a mechanism where relation queries can be pre-generated, with special placeholder markers to be filled in later, to:
a) aid static analysis
b) allow alternative relation resolving implementations to queue follow-up queries with (arbitrary?) to-be-filled-in details
- Maybe use ["foo", placeholder, "bar"] format instead of a single string, when generating the query? That would be a bit hacky...
- Implement various kinds of JOINs with table-prefixed resultset column names, those column names being determined at query execution time
- Figure out a way to represent this in the pre-generated query, maybe the same mechanism as the relation query placeholder mechanism above?
- Different API designs for different JOIN types? eg. a `with(otherTableQuery)` for left joins, something similar for right joins, and a `combine(tableAQuery, tableBQuery)` for full joins and inner joins
- Implement through relation queries with the JOIN mechanism
- AND/OR as `all()` and `any()`
- Can also fix the placeholder thing by having a separarate kind of nodePlaceholder that gets filled in with a query node; and then only doing the astToQuery operation at the very last moment, after state has been collected from the database and all of those nodes can be filled in
- In this case, there should probably be a way to provide 'global' values for nodePlaceholders (applicable to the query + any derived ones like relations) and local ones (just the query itself)? Maybe this is already solved by using strings vs. symbols for the placeholder names? That would work the same as with normal placeholders, and therefore be consistent implementation-wise
-----------
Generic AST optimization infrastructure instead of inline modifications in the stringification phase
Pipeline would then look like:
Build AST -> Optimize AST -> Compile to SQL
Three AST optimization purposes:
- Normalization (required)
- Readability (optional)
- Performance (optional)
--------
Renames:
- remote -> foreign
- remoteSide? -> foreignSide
- sql -> unsafeSQL
Transforms should ideally be cached for a query, maybe auto-do this for top-level constructs like SELECT, so that a full query is immediately performantly reusable?
How to deal with such queries being specified as sub-expressions then? Maybe cross-query optimization needs to happen?
FIXME: Document the design for dealing with the potentially-cyclical operations dependencies within the module
FIXME: Linter rule for verifying that all requires inside of the affected folders (operations/ and validators/operations/) correctly pass in operations, to avoid polluting the AST with Validatem validators
Walker design:
- Allow a visitor to place a 'marker' on a given node, that can be referenced back to from another, more deeply-nested visitor (referencing the nearest, a la React context) - this is useful for hoisting things up in the AST. Need to figure out how to combine this with immutable AST walking, though, without pulling the carpet from under the currently-walked subtree.
- Possibly Babel's walker implementation might have a solution to this category of problem?
- Possibly cursors are a useful concept to use here? As mutable pointers to otherwise immutable data. This might cause unexpected behaviour though, with different items being walked than currently (newly) exist in the tree, as the walker would still be operating on the old tree -- unless a change higher-up would trigger a re-walk of that entire subtree, which would require all optimizations to be idempotent.
- Make sure to iterate again from whatever new AST subtree was returned from a visitor! So that other visitors get a chance.
- How to deal with a case where in notExpression(foo), foo returns a notExpression(bar), and the notExpression deduplicator does not happen anymore because the top-most notExpression was already previously visited? Maybe re-run the whole sequence of visitors repeatedly until the entire tree has stabilized? This also requires idempotence of visitors! And makes persisting/caching processed query ASTs even more important.
-------
Planned AST optimization phases (loosely ordered):
- Combine multiple `where(...)` into a single `where(allOf(...))`
- Flatten nested notCondition
- Flatten nested same-typed allOfCondition/anyOfCondition
- Group >1 same-typed conditionTypes into a single condition(_internalArray(...))
- Invert condition lists into expression lists
- Flatten nested notExpression
- Flatten nested same-typed allOfExpression/anyOfExpression
-----
MARKER:
- Refactor relation operations to new design
- Implement AST optimization infrastructure (incl. solving the immutable reference problem?)