Skip to content

Commit

Permalink
backend/clickhouse: subquery level fix
Browse files Browse the repository at this point in the history
  • Loading branch information
roman-bodavskiy committed Dec 4, 2024
1 parent 9378202 commit b4e418b
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ import Kernel.Storage.ClickhouseV2.Internal.Types

instance (FromJSON (ColumnsType 'NOT_AGG (Columns 'NOT_AGG t)), ClickhouseTable t) => ClickhouseColumns 'NOT_AGG (Columns 'NOT_AGG t) where
type ColumnsType 'NOT_AGG (Columns 'NOT_AGG t) = t Identity
showClickhouseColumns _ _ = "*"
parseColumns _ _ = eitherResult . A.fromJSON
showClickhouseColumns _ _ _ = "*"
parseColumns _ _ val _ = eitherResult . A.fromJSON $ val

-- should be all AGG columns or all NOT_AGG columns
instance (ClickhouseValue v) => ClickhouseColumns a (Column a t v) where
Expand Down Expand Up @@ -77,117 +77,125 @@ parseColumns1 ::
ClickhouseValue v1 =>
Column a t v1 ->
A.Value ->
SubQueryLevel ->
Either String v1
parseColumns1 c1 json = do
parseColumns1 c1 json l = do
mapResult <- eitherResult . A.fromJSON @(A.KeyMap (Value NotSpecified)) $ json
parseValueFromMap @a @t @v1 1 c1 mapResult
parseValueFromMap @a @t @v1 1 c1 mapResult l

parseColumns2 ::
forall a t v1 v2.
(C2 ClickhouseValue v1 v2) =>
T2 (Column a t) v1 v2 ->
A.Value ->
SubQueryLevel ->
Either String (v1, v2)
parseColumns2 (c1, c2) json = do
parseColumns2 (c1, c2) json l = do
mapResult <- eitherResult . A.fromJSON @(A.KeyMap (Value NotSpecified)) $ json
v1 <- parseValueFromMap @a @t @v1 1 c1 mapResult
v2 <- parseValueFromMap @a @t @v2 2 c2 mapResult
v1 <- parseValueFromMap @a @t @v1 1 c1 mapResult l
v2 <- parseValueFromMap @a @t @v2 2 c2 mapResult l
pure (v1, v2)

parseColumns3 ::
forall a t v1 v2 v3.
(C3 ClickhouseValue v1 v2 v3) =>
T3 (Column a t) v1 v2 v3 ->
A.Value ->
SubQueryLevel ->
Either String (v1, v2, v3)
parseColumns3 (c1, c2, c3) json = do
parseColumns3 (c1, c2, c3) json l = do
mapResult <- eitherResult . A.fromJSON @(A.KeyMap (Value NotSpecified)) $ json
v1 <- parseValueFromMap @a @t @v1 1 c1 mapResult
v2 <- parseValueFromMap @a @t @v2 2 c2 mapResult
v3 <- parseValueFromMap @a @t @v3 3 c3 mapResult
v1 <- parseValueFromMap @a @t @v1 1 c1 mapResult l
v2 <- parseValueFromMap @a @t @v2 2 c2 mapResult l
v3 <- parseValueFromMap @a @t @v3 3 c3 mapResult l
pure (v1, v2, v3)

parseColumns4 ::
forall a t v1 v2 v3 v4.
(C4 ClickhouseValue v1 v2 v3 v4) =>
T4 (Column a t) v1 v2 v3 v4 ->
A.Value ->
SubQueryLevel ->
Either String (v1, v2, v3, v4)
parseColumns4 (c1, c2, c3, c4) json = do
parseColumns4 (c1, c2, c3, c4) json l = do
mapResult <- eitherResult . A.fromJSON @(A.KeyMap (Value NotSpecified)) $ json
v1 <- parseValueFromMap @a @t @v1 1 c1 mapResult
v2 <- parseValueFromMap @a @t @v2 2 c2 mapResult
v3 <- parseValueFromMap @a @t @v3 3 c3 mapResult
v4 <- parseValueFromMap @a @t @v4 4 c4 mapResult
v1 <- parseValueFromMap @a @t @v1 1 c1 mapResult l
v2 <- parseValueFromMap @a @t @v2 2 c2 mapResult l
v3 <- parseValueFromMap @a @t @v3 3 c3 mapResult l
v4 <- parseValueFromMap @a @t @v4 4 c4 mapResult l
pure (v1, v2, v3, v4)

parseColumns5 ::
forall a t v1 v2 v3 v4 v5.
(C5 ClickhouseValue v1 v2 v3 v4 v5) =>
T5 (Column a t) v1 v2 v3 v4 v5 ->
A.Value ->
SubQueryLevel ->
Either String (v1, v2, v3, v4, v5)
parseColumns5 (c1, c2, c3, c4, c5) json = do
parseColumns5 (c1, c2, c3, c4, c5) json l = do
mapResult <- eitherResult . A.fromJSON @(A.KeyMap (Value NotSpecified)) $ json
v1 <- parseValueFromMap @a @t @v1 1 c1 mapResult
v2 <- parseValueFromMap @a @t @v2 2 c2 mapResult
v3 <- parseValueFromMap @a @t @v3 3 c3 mapResult
v4 <- parseValueFromMap @a @t @v4 4 c4 mapResult
v5 <- parseValueFromMap @a @t @v5 5 c5 mapResult
v1 <- parseValueFromMap @a @t @v1 1 c1 mapResult l
v2 <- parseValueFromMap @a @t @v2 2 c2 mapResult l
v3 <- parseValueFromMap @a @t @v3 3 c3 mapResult l
v4 <- parseValueFromMap @a @t @v4 4 c4 mapResult l
v5 <- parseValueFromMap @a @t @v5 5 c5 mapResult l
pure (v1, v2, v3, v4, v5)

parseColumns6 ::
forall a t v1 v2 v3 v4 v5 v6.
(C6 ClickhouseValue v1 v2 v3 v4 v5 v6) =>
T6 (Column a t) v1 v2 v3 v4 v5 v6 ->
A.Value ->
SubQueryLevel ->
Either String (v1, v2, v3, v4, v5, v6)
parseColumns6 (c1, c2, c3, c4, c5, c6) json = do
parseColumns6 (c1, c2, c3, c4, c5, c6) json l = do
mapResult <- eitherResult . A.fromJSON @(A.KeyMap (Value NotSpecified)) $ json
v1 <- parseValueFromMap @a @t @v1 1 c1 mapResult
v2 <- parseValueFromMap @a @t @v2 2 c2 mapResult
v3 <- parseValueFromMap @a @t @v3 3 c3 mapResult
v4 <- parseValueFromMap @a @t @v4 4 c4 mapResult
v5 <- parseValueFromMap @a @t @v5 5 c5 mapResult
v6 <- parseValueFromMap @a @t @v6 6 c6 mapResult
v1 <- parseValueFromMap @a @t @v1 1 c1 mapResult l
v2 <- parseValueFromMap @a @t @v2 2 c2 mapResult l
v3 <- parseValueFromMap @a @t @v3 3 c3 mapResult l
v4 <- parseValueFromMap @a @t @v4 4 c4 mapResult l
v5 <- parseValueFromMap @a @t @v5 5 c5 mapResult l
v6 <- parseValueFromMap @a @t @v6 6 c6 mapResult l
pure (v1, v2, v3, v4, v5, v6)

-- FIXME should parse Numbers also
parseValueFromMap ::
forall a t v.
(ClickhouseValue v) =>
Int ->
ColumnNumber ->
Column a t v ->
A.KeyMap (Value NotSpecified) ->
SubQueryLevel ->
Either String v
parseValueFromMap n column mapResult = do
parseValueFromMap n column mapResult l = do
let columnName = showColumn column
val <- case A.lookup (fromString $ getColumnSynonym n) mapResult of
Nothing -> Left $ "Key \"" <> getColumnSynonym n <> "\" for column \"" <> columnName <> "\" did not found"
val <- case A.lookup (fromString $ getColumnSynonym n l) mapResult of
Nothing -> Left $ "Key \"" <> getColumnSynonym n l <> "\" for column \"" <> columnName <> "\" did not found"
Just val -> pure $ coerce @(Value NotSpecified) @(Value v) val
either (\err -> Left $ "Failed to parse key \"" <> getColumnSynonym n <> "\" for column \"" <> columnName <> "\": " <> err) pure $
either (\err -> Left $ "Failed to parse key \"" <> getColumnSynonym n l <> "\" for column \"" <> columnName <> "\": " <> err) pure $
getExcept $ fromClickhouseValue @v val

zipColumnsWithSynonyms1 :: Column a t v1 -> String
zipColumnsWithSynonyms1 :: Column a t v1 -> SubQueryLevel -> String
zipColumnsWithSynonyms1 c1 = zipColumns [showColumn c1]

zipColumnsWithSynonyms2 :: T2 (Column a t) v1 v2 -> String
zipColumnsWithSynonyms2 :: T2 (Column a t) v1 v2 -> SubQueryLevel -> String
zipColumnsWithSynonyms2 (c1, c2) = zipColumns [showColumn c1, showColumn c2]

zipColumnsWithSynonyms3 :: T3 (Column a t) v1 v2 v3 -> String
zipColumnsWithSynonyms3 :: T3 (Column a t) v1 v2 v3 -> SubQueryLevel -> String
zipColumnsWithSynonyms3 (c1, c2, c3) = zipColumns [showColumn c1, showColumn c2, showColumn c3]

zipColumnsWithSynonyms4 :: T4 (Column a t) v1 v2 v3 v4 -> String
zipColumnsWithSynonyms4 :: T4 (Column a t) v1 v2 v3 v4 -> SubQueryLevel -> String
zipColumnsWithSynonyms4 (c1, c2, c3, c4) = zipColumns [showColumn c1, showColumn c2, showColumn c3, showColumn c4]

zipColumnsWithSynonyms5 :: T5 (Column a t) v1 v2 v3 v4 v5 -> String
zipColumnsWithSynonyms5 :: T5 (Column a t) v1 v2 v3 v4 v5 -> SubQueryLevel -> String
zipColumnsWithSynonyms5 (c1, c2, c3, c4, c5) = zipColumns [showColumn c1, showColumn c2, showColumn c3, showColumn c4, showColumn c5]

zipColumnsWithSynonyms6 :: T6 (Column a t) v1 v2 v3 v4 v5 v6 -> String
zipColumnsWithSynonyms6 :: T6 (Column a t) v1 v2 v3 v4 v5 v6 -> SubQueryLevel -> String
zipColumnsWithSynonyms6 (c1, c2, c3, c4, c5, c6) = zipColumns [showColumn c1, showColumn c2, showColumn c3, showColumn c4, showColumn c5, showColumn c6]

zipColumns :: [String] -> String
zipColumns columns = List.intercalate ", " $ zipWith (\n column -> column <> " as " <> getColumnSynonym n) [1 ..] columns
zipColumns :: [String] -> SubQueryLevel -> String
zipColumns columns l = List.intercalate ", " $ zipWith (\n column -> column <> " as " <> getColumnSynonym n l) [1 ..] columns

getColumnSynonym :: Int -> String
getColumnSynonym n = "res" <> show n
getColumnSynonym :: ColumnNumber -> SubQueryLevel -> String
getColumnSynonym n 0 = "res" <> show n
getColumnSynonym n l = "res" <> show n <> "_sub" <> show l
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ instance
NO_SELECT_MODIFIER -> ""
SELECT_FINAL_MODIFIER -> " FINAL "
"SELECT "
<> RawQuery (showClickhouseColumns @a @cols (Proxy @a) cols)
<> RawQuery (showClickhouseColumns @a @cols (Proxy @a) cols q.subQueryLevelQ)
<> " FROM "
-- <> fromString tableName
<> toClickhouseQuery @(AvailableColumns db t acols) q.tableQ
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ data Select a db table cols gr ord acols where

class ClickhouseColumns (a :: IsAggregated) cols where
type ColumnsType a cols
showClickhouseColumns :: Proxy a -> cols -> String
parseColumns :: Proxy a -> cols -> A.Value -> Either String (ColumnsType a cols)
showClickhouseColumns :: Proxy a -> cols -> SubQueryLevel -> String
parseColumns :: Proxy a -> cols -> A.Value -> SubQueryLevel -> Either String (ColumnsType a cols)

newtype RawQuery = RawQuery {getRawQuery :: String}
deriving newtype (IsString, Semigroup, Monoid)
Expand Down Expand Up @@ -179,6 +179,7 @@ instance (ClickhouseTable t, C6 ClickhouseValue v1 v2 v3 v4 v5 v6) => IsOrderCol
data Q db table cols ord acols = (ClickhouseDb db) =>
Q
{ tableQ :: AvailableColumns db table acols,
subQueryLevelQ :: SubQueryLevel,
whereQ :: Maybe (cols -> Where table),
limitQ :: Maybe Limit,
offsetQ :: Maybe Offset,
Expand Down Expand Up @@ -260,3 +261,9 @@ type C4 (c :: Type -> Constraint) x1 x2 x3 x4 = (c x1, c x2, c x3, c x4)
type C5 (c :: Type -> Constraint) x1 x2 x3 x4 x5 = (c x1, c x2, c x3, c x4, c x5)

type C6 (c :: Type -> Constraint) x1 x2 x3 x4 x5 x6 = (c x1, c x2, c x3, c x4, c x5, c x6)

newtype SubQueryLevel = SubQueryLevel {getSubQueryLevel :: Int}
deriving newtype (Show, Num, Eq)

newtype ColumnNumber = ColumnNumber {getColumnNumber :: Int}
deriving newtype (Show, Num, Enum, Eq)
19 changes: 10 additions & 9 deletions lib/mobility-core/src/Kernel/Storage/ClickhouseV2/Operators.hs
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ all_ ::
forall db table.
(ClickhouseDb db, ClickhouseTable table) =>
FieldModifications table ->
AvailableAllColumns db table
all_ tableMod = AvailableColumns $ AllColumns (mkTableColumns @table tableMod)
(AvailableAllColumns db table, SubQueryLevel)
all_ tableMod = (AvailableColumns $ AllColumns (mkTableColumns @table tableMod), 0)

subSelect_ ::
forall a db table subcols gr ord acols.
Expand All @@ -132,18 +132,18 @@ subSelect_ ::
ResetGroupColumns subcols
) =>
Select a db table subcols gr ord acols ->
AvailableSubSelectColumns db table subcols
-- Select a db table cols gr ord
subSelect_ = AvailableColumns . SubSelectColumns
(AvailableSubSelectColumns db table subcols, SubQueryLevel)
subSelect_ s@(Select _cols _gr q) = (AvailableColumns . SubSelectColumns $ s, q.subQueryLevelQ + 1)

filter_ ::
ClickhouseDb db =>
(AvailableColumnsType acols -> cols -> Clause table) ->
AvailableColumns db table acols ->
(AvailableColumns db table acols, SubQueryLevel) ->
Q db table cols NotOrdered acols
filter_ filterClause table =
filter_ filterClause (table, level) =
Q
{ tableQ = table,
subQueryLevelQ = level,
whereQ = Just $ Where . filterClause (getAvailableColumnsValue table),
limitQ = Nothing,
offsetQ = Nothing,
Expand All @@ -152,11 +152,12 @@ filter_ filterClause table =

emptyFilter ::
ClickhouseDb db =>
AvailableColumns db table acols ->
(AvailableColumns db table acols, SubQueryLevel) ->
Q db table cols NotOrdered acols
emptyFilter table =
emptyFilter (table, level) =
Q
{ tableQ = table,
subQueryLevelQ = level,
whereQ = Nothing,
limitQ = Nothing,
offsetQ = Nothing,
Expand Down
4 changes: 2 additions & 2 deletions lib/mobility-core/src/Kernel/Storage/ClickhouseV2/Queries.hs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ findAll ::
(HasClickhouseEnv db m, ClickhouseQuery (Select a db t cols gr ord acols)) =>
Select a db t cols gr ord acols ->
m [ColumnsType a cols]
findAll selectClause@(Select cols _ _) = do
findAll selectClause@(Select cols _ q) = do
let rawQuery = toClickhouseQuery @(Select a db t cols gr ord acols) selectClause
logDebug $ "clickhouse raw query v2: " <> T.pack rawQuery.getRawQuery
resJSON <- runRawQuery @db @A.Value @m (Proxy @db) rawQuery
Expand All @@ -51,7 +51,7 @@ findAll selectClause@(Select cols _ _) = do
pure []
Right val@(A.Array xs) -> do
logDebug $ "clickhouse raw query v2 json result: " <> show val
case mapM (parseColumns @a @cols (Proxy @a) cols) xs of
case mapM (\val' -> parseColumns @a @cols (Proxy @a) cols val' q.subQueryLevelQ) xs of
Left err -> do
logError $ "Clickhouse parsing result error: " <> T.pack err
pure []
Expand Down

0 comments on commit b4e418b

Please sign in to comment.