How can I do select ... from (select ...) join (select ...) in Esqueleto?
I'm aware that I can use rawSql from Persistent, but I'd like to avoid that.
For the record, here is the full query:
select q.uuid, q.upvotes, q.downvotes, count(a.parent_uuid), max(a.isAccepted) as hasAccepted
from
(select post.uuid, post.title, sum(case when (vote.type = 2) then 1 else 0 end) as upvotes, sum(case when (vote.type = 3) then 1 else 0 end) as downvotes
from post left outer join vote on post.uuid = vote.post_id
where post.parent_uuid is null
group by post.uuid
order by post.created_on desc
) q
left outer join
(select post.parent_uuid, max(case when (vote.type = 1) then 1 else 0 end) as isAccepted
from post left outer join vote on post.uuid = vote.post_id
where post.parent_uuid is not null
group by post.id
) a
on a.parent_uuid = q.uuid
group by q.uuid
limit 10
I got here because I had the same question. I imagine the thing we want would be something like:
fromSelect
:: ( Database.Esqueleto.Internal.Language.From query expr backend a
, Database.Esqueleto.Internal.Language.From query expr backend b
)
=> (a -> query b)
-> (b -> query c)
-> query c
Unfortunately, from looking at Database.Esqueleto.Internal.Sql.FromClause:
-- | A part of a #FROM# clause.
data FromClause =
FromStart Ident EntityDef
| FromJoin FromClause JoinKind FromClause (Maybe (SqlExpr (Value Bool)))
| OnClause (SqlExpr (Value Bool))
I don't think there's any support for this in Esqueleto. It only seems to support simple table names and joins with on-clauses that have a boolean expression. I imagine the hardest part of adding support for this is handling table and column name aliases (as sql clause), since ^. expects an expr (Entity val) and an EntityField val typ. Simplest way is to change that to using String or Text for both operands, but that's not very type-safe. I'm not sure what the best option would be implementation-wise to make that type safe.
EDIT: Probably best to forget ^. and have fromSelect generate the aliases when providing the returned values of its first parameter as the arguments of its second parameter. Types would probably have to be altered to make room for these aliases. This is only contemplating from subqueries, not joins. That's another problem.
Related
I'd like to use Persistent/Esqueleto to implement count estimates.
One approach recommended in this article is to define a function like this
CREATE FUNCTION count_estimate(query text) RETURNS integer AS $$
DECLARE
rec record;
rows integer;
BEGIN
FOR rec IN EXECUTE 'EXPLAIN ' || query LOOP
rows := substring(rec."QUERY PLAN" FROM ' rows=([[:digit:]]+)');
EXIT WHEN rows IS NOT NULL;
END LOOP;
RETURN rows;
END;
$$ LANGUAGE plpgsql VOLATILE STRICT;
and then use it like this
SELECT count_estimate('SELECT * FROM companies WHERE status = ''Active''');
In order to use the count_estimate function, I'll need (I think?) to render the query that Peristent/Equeleto generates, however when I try rendering the query with renderQuerySelect, I get something like this
SELECT "companies"."id", "companies"."name", "companies"."status"
FROM "companies"
WHERE "companies"."status" IN (?)
; [PersistText "Active"]
This of course can't be stuffed into the count_estimate, because it will syntax error on the ? placeholder. I also can't naïvely replace the ? with "Active", because it will syntax error on that first double quote.
How do I render the query in a way that my count_estimate function will accept?
I tried something like this, but it fails at runtime
getEstimate :: (Text, [PersistValue]) -> DB [Single Int]
getEstimate (query, params) = rawSql [st|
SELECT count_estimate('#{query}');
|] params
I managed to figure it out (mostly).
It's a matter of escaping the single quotes in both the query and the PersistValue parameters. I'm doing it like this at the moment, but escaping will need to be added back in otherwise I think it creates a SQL injection vulnerability. I may also need to handle the other PersistValue constructors in some specific way, but I haven't run into problems there yet.
import qualified Data.Text as T
import qualified Database.Persist as P
getEstimate :: (Text, [PersistValue]) -> DB (Maybe Int)
getEstimate (query, params) = fmap unSingle . listToMaybe <$> rawSql [st|
SELECT count_estimate('#{T.replace "'" "''" query}');
|] (map replace' params)
where literal a = PersistLiteral_ P.Unescaped ("''" <> a <> "''")
replace' = \case
PersistText t -> literal $ encodeUtf8 t
PersistDay d -> literal $ encodeUtf8 $ pack $ showGregorian d
a -> a
I have a grammer snippet like this
expr: left=expr '=' right=expr #exprassign
| atom #expratom
;
atom: TEXT
| ID
;
TEXT: '\''(.)*?'\'';
ID:[A-Z]+;
In my visitor method
visitExprAtom()
How can I find getparent context which is current. Lets say my method works under left side say it fromleft other is fromright
I need to change my approaches when I learn where does it come from fromleft or fromright
Thanks.
EDIT:
Let's assume I got a code snippet.
var varcompany='C1';// Creates an variable named company initialated C1
SELECT * FROM TABLE
WHERE COMPANY = varcompany; //COMPANY ='C1'
Above expr grammar is parsing for COMPANY = varcompany
this approach my parser think for COMPANY as a variable and tries to find in my map for it. Or I use atom expression other Dsl scenerios and I need its acting differently. on SQL it must return like 'C1' but other scenerios it must return C1
I am trying to design an API for some database system in Haskell, and I would like to model the columns of this database in a way such that interactions between columns of different tables cannot get mixed up.
More precisely, imagine that you have a type to represent a table in a database, associated to some type:
type Table a = ...
and that you can extract the columns of the table, along with the type of the column:
type Column col = ...
Finally, there are various extractors. For example, if your table contains descriptions of frogs, a function would let you extract the column containing the weight of the frog:
extractCol :: Table Frog -> Column Weight
Here is the question: I would like to distinguish the origin of the columns so that users cannot do operations between tables. For example:
bullfrogTable = undefined :: Table Frog
toadTable = undefined :: Table Frog
bullfrogWeights = extractCol bullfrogTable
toadWeights = extractCol toadTable
-- Or some other columns from the toad table
toadWeights' = extractCol toadTable
-- This should compile
addWeights toadWeights' toadWeights
-- This should trigger a type error
addWeights bullfrogWeights toadWeights
I know how to achieve this in Scala (using path-dependent types, see [1]), and I have been thinking of 3 options in Haskell:
not using types, and just doing a check at runtime (the current solution)
the TypeInType extension to add a phantom type on the Table type itself, and pass this extra type to the columns. I am not keen on it, because the construction of such a type would be very complicated (tables are generated through complex DAG operations) and probably slow to compile in this context.
wrapping the operations using a forall construct similar to the ST monad, but in my case, I would like the extra tagging type to actually escape the construction.
I am happy to have a very limited valid scoping for the construction of the same columns (i.e. columns from table and (id table) not being mixable), and I mostly care about the DSL feel of the API rather than the safety.
[1] What is meant by Scala's path-dependent types?
My current solution
Here is what I ended up doing, using RankNTypes.
I still want to give users the ability to use columns how they see fit without having some strong type checks, and opt in if they want some stronger type guarantees: this is a DSL for data scientist who will not know the power of the Haskell side
Tables are still tagged by their content:
type Table a = ...
and columns are now tagged with some extra reference types, on top of the type of the data they contain:
type Column ref col = ...
Projections from tables to columns are either tagged or untagged. In practice, this is hidden behind a lens-like DSL.
extractCol :: Table Frog -> Column Frog Weight
data TaggedTable ref a = TaggedTable { _ttTable :: Table a }
extractColTagged :: Table ref Frog -> Column ref Weight
withTag :: Table a -> (forall ref. TaggedTable ref a -> b) -> b
withTag tb f = f (TaggedTable tb)
Now I can write some code as following:
let doubleToadWeights = withTag toadTable $ \ttoadTable ->
let toadWeights = extractColTagged ttoadTable in
addWeights toadWeights toadWeights
and this will not compile, as desired:
let doubleToadWeights =
toadTable `withTag` \ttoads ->
bullfrogTable `withTag` \tbullfrogs ->
let toadWeights = extractColTagged ttoads
bullfrogWeights = extractColTagged tbullfrogs
in addWeights toadWeights bullfrogWeights -- Type error
From a DSL perspective, I believe it is not as straightforward as what one could achieve with Scala, but the type error message is understandable, which is paramount for me.
Haskell does not (as far as I know) have path dependent types, but you can get some of the way by using rank 2 types. For instance the ST monad has a dummy type parameter s that is used to prevent leakage between invocations of runST:
runST :: (forall s . ST s a) -> a
Within an ST action you can have an STRef:
newSTRef :: a -> ST s (STRef s a)
But the STRef you get carries the s type parameter, so it isn't allowed to escape from the runST.
I want to use a calculated value in the WHERE clause and in an ORDER BY expression. In plain sql it would look like
SELECT some, colums, (some arbitrary math) AS calc_value FROM table WHERE calc_value <= ? ORDER BY calc_value
If I try in JPQL
entitymanager.createQuery("SELECT e, (some arbitrary math) AS calc_value FROM Entity e WHERE calc_value <= :param ORDER BY calc_value", Entity.class);
it fails. Obviously, because the return of the query is the tuple of Entity and calc_value (i.e. Double).
Is there a way of getting this into one query, using strong typed return values (i.e. Entity.class, as the calculated value doesn't matter).
I've had a similar problem and didn't resolve the problem to fetch it into the correct object:
Tried all constructor combinations for the object - no luck.
tried Tuple.class - no luck.
Finally I used this approach and then fetched oject[0] into my real Java-Object:
TypedQuery<Object[]> q = em.createQuery("select v, (2 * 4) as temp from MyObject v order by temp", Object[].class);
List<Object[]> results = q.getResultList();
for (Object[] o : results)
MyObject mo = (MyObject) o[0];
I have a database that looks like
Alex,Anna,Peter
Alex
Alex,Peter
Alfons
Algebra,Geometry
Algebra,Physics
However I am only interested in the first expression before the comma. Meaning my perfect answer would be:
Alex
Alex
Alex
Alfons
Algebra
Algebra
So far I found the SPLIT function but it still returns me a bunch of values I am really not interested in. How to make it run efficiently?
Thanks
SELECT first(split(s,','))
FROM
(SELECT 'Alex,Anna,Peter' AS s),
(SELECT 'Algebra,Geometry' AS s);
Outputs:
Alex
Algebra
SPLIT could be expensive, since it translates string into repeated field, so here are two more alternative solutions:
SELECT IFNULL(LEFT(s, INSTR(s, ',') - 1), s)
FROM
(SELECT 'Alex,Anna,Peter' AS s),
(SELECT 'Algebra,Geometry' AS s),
(SELECT 'Alfons' AS s)
and
SELECT REGEXP_EXTRACT(s, r'([^,]*)')
FROM
(SELECT 'Alex,Anna,Peter' AS s),
(SELECT 'Algebra,Geometry' AS s),
(SELECT 'Alfons' AS s)