diff --git a/hstream/app/client.hs b/hstream/app/client.hs index 760ac90a8..dd0b205b7 100644 --- a/hstream/app/client.hs +++ b/hstream/app/client.hs @@ -4,7 +4,6 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternSynonyms #-} -{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} @@ -31,7 +30,6 @@ import qualified Options.Applicative as O import Proto3.Suite (def) import System.Exit (exitFailure) import System.Timeout (timeout) -import Text.RawString.QQ (r) import qualified HStream.Admin.Server.Command as Admin import HStream.Client.Action (alterConnectorConfig, @@ -46,6 +44,7 @@ import HStream.Client.Execute (executeWithLookupResource_, initCliContext, simpleExecute) import HStream.Client.Internal (interactiveAppend) +import HStream.RawString (cliBanner) #ifdef HStreamEnableSchema import HStream.Client.SQLNew (commandExec, interactiveSQLApp) @@ -116,18 +115,10 @@ hstreamSQL connOpt HStreamSqlOpts{_updateInterval = updateInterval, _retryInterval = retryInterval, _retryLimit = retryLimit, .. } = do hstreamCliContext <- initCliContext connOpt case _execute of - Nothing -> showHStream *> interactiveSQLApp HStreamSqlContext{..} _historyFile + Nothing -> putStrLn cliBanner *> interactiveSQLApp HStreamSqlContext{..} _historyFile Just statement -> do when (Char.isSpace `all` statement) $ do putStrLn "Empty statement" *> exitFailure commandExec HStreamSqlContext{..} statement - where - showHStream = putStrLn [r| - __ _________________ _________ __ ___ - / / / / ___/_ __/ __ \/ ____/ | / |/ / - / /_/ /\__ \ / / / /_/ / __/ / /| | / /|_/ / - / __ /___/ // / / _, _/ /___/ ___ |/ / / / - /_/ /_//____//_/ /_/ |_/_____/_/ |_/_/ /_/ - |] hstreamNodes :: RefinedCliConnOpts -> HStreamNodes -> IO () hstreamNodes connOpts HStreamNodesList = diff --git a/hstream/app/lib/KafkaServer.hs b/hstream/app/lib/KafkaServer.hs index 0e5c7f1f8..7eec7ce01 100644 --- a/hstream/app/lib/KafkaServer.hs +++ b/hstream/app/lib/KafkaServer.hs @@ -5,7 +5,6 @@ {-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternSynonyms #-} -{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} @@ -35,6 +34,7 @@ import ZooKeeper (withResource, zookeeperResInit) import qualified Data.Vector as V + import HStream.Base (setupFatalSignalHandler) import HStream.Common.Server.HashRing (updateHashRing) import qualified HStream.Common.Server.MetaData as M @@ -65,15 +65,12 @@ import qualified HStream.Logger as Log import HStream.MetaStore.Types (MetaHandle (..), MetaStore (..), RHandle (..)) +import HStream.RawString (banner) import qualified HStream.Server.HStreamInternal as I import qualified HStream.Store.Logger as S import qualified HStream.ThirdParty.Protobuf as Proto import HStream.Utils (getProtoTimestamp) -#ifndef HSTREAM_ENABLE_ASAN -import Text.RawString.QQ (r) -#endif - ------------------------------------------------------------------------------- runApp :: IO () @@ -162,17 +159,7 @@ serve :: ServerContext -> K.ServerOptions -> IO () serve sc@ServerContext{..} netOpts usingCppServer = do Log.i "************************" -#ifndef HSTREAM_ENABLE_ASAN - hPutStrLn stderr $ [r| - _ _ __ _____ ___ ___ __ __ __ - | || |/' _/_ _| _ \ __|/ \| V | - | >< |`._`. | | | v / _|| /\ | \_/ | - |_||_||___/ |_| |_|_\___|_||_|_| |_| - - |] -#else - hPutStrLn stderr "ONLY FOR DEBUG: Enable ASAN" -#endif + hPutStrLn stderr banner Log.i "************************" let serverOnStarted = do diff --git a/hstream/app/server.hs b/hstream/app/server.hs index 9e13f8eab..e49592559 100644 --- a/hstream/app/server.hs +++ b/hstream/app/server.hs @@ -6,7 +6,6 @@ {-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternSynonyms #-} -{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} @@ -46,11 +45,13 @@ import HStream.Gossip (GossipContext (..), initGossipContext, startGossip, waitGossipBoot) import HStream.Gossip.Types (InitType (Gossip)) +import qualified HStream.Gossip.Types as Gossip import qualified HStream.Kafka.Server.Config as Ka import qualified HStream.Logger as Log import HStream.MetaStore.Types as M (MetaHandle (..), MetaStore (..), RHandle (..)) +import HStream.RawString (banner) import HStream.Server.Config (AdvertisedListeners, ExperimentalFeature (..), FileLoggerSettings (..), @@ -88,12 +89,6 @@ import qualified Network.GRPC.HighLevel.Client as GRPC import qualified Network.GRPC.HighLevel.Generated as GRPC #endif -#ifndef HSTREAM_ENABLE_ASAN -import Text.RawString.QQ (r) -#endif - -import qualified HStream.Gossip.Types as Gossip - ------------------------------------------------------------------------------- main :: IO () @@ -195,17 +190,7 @@ serve -> IO () serve sc@ServerContext{..} rpcOpts enableStreamV2 = do Log.i "************************" -#ifndef HSTREAM_ENABLE_ASAN - hPutStrLn stderr $ [r| - _ _ __ _____ ___ ___ __ __ __ - | || |/' _/_ _| _ \ __|/ \| V | - | >< |`._`. | | | v / _|| /\ | \_/ | - |_||_||___/ |_| |_|_\___|_||_|_| |_| - - |] -#else - hPutStrLn stderr "ONLY FOR DEBUG: Enable ASAN" -#endif + hPutStrLn stderr banner Log.i "************************" let serverOnStarted = do diff --git a/hstream/hstream.cabal b/hstream/hstream.cabal index 36de08d1a..088e46a5b 100644 --- a/hstream/hstream.cabal +++ b/hstream/hstream.cabal @@ -74,6 +74,21 @@ common link-asan "-optl-Wl,--whole-archive" "-optl-Wl,-Bstatic" "-optl-Wl,-lasan" "-optl-Wl,-Bdynamic" "-optl-Wl,--no-whole-archive" +-- Local library for HStream RawStrings, see module description for why we need this. +library hstream-lib-rawstring + import: shared-properties + hs-source-dirs: src/lib + exposed-modules: HStream.RawString + build-depends: + , base + , containers + , raw-strings-qq + + if flag(hstream_enable_asan) + cpp-options: -DHSTREAM_ENABLE_ASAN + + default-language: Haskell2010 + library import: shared-properties exposed-modules: @@ -160,6 +175,7 @@ library , hstream-processing , hstream-sql , hstream-store + , hstream:hstream-lib-rawstring , memory , microlens , microlens-aeson @@ -168,7 +184,6 @@ library , optparse-applicative , proto3-suite , proto3-wire - , raw-strings-qq , rocksdb-haskell-bindings , scientific , split @@ -183,7 +198,7 @@ library , unix , unordered-containers , uuid - , vector ^>=0.13 + , vector ^>=0.13 , vector-algorithms , yaml , Z-Data @@ -234,12 +249,12 @@ library hstream-app , hstream-gossip , hstream-kafka , hstream-store + , hstream:hstream-lib-rawstring , http-client , memory , optparse-applicative , proto3-suite , proto3-wire - , raw-strings-qq , stm , suspend , text @@ -283,13 +298,12 @@ executable hstream-server , hstream-gossip , hstream-kafka , hstream-store - , hstream:{hstream, hstream-app} + , hstream:{hstream, hstream-app, hstream-lib-rawstring} , http-client , memory , optparse-applicative , proto3-suite , proto3-wire - , raw-strings-qq , stm , suspend , text @@ -320,7 +334,6 @@ executable hstream , grpc-haskell , grpc-haskell-core , haskeline - , hstream , hstream-admin-server , hstream-api-hs , hstream-common @@ -328,11 +341,11 @@ executable hstream , hstream-common-stats , hstream-sql , hstream-store + , hstream:{hstream, hstream-lib-rawstring} , network , optparse-applicative , proto3-suite , random - , raw-strings-qq , split , text , unix diff --git a/hstream/src/HStream/Client/SQL.hs b/hstream/src/HStream/Client/SQL.hs index 6725c34b6..3dcd51e65 100644 --- a/hstream/src/HStream/Client/SQL.hs +++ b/hstream/src/HStream/Client/SQL.hs @@ -4,7 +4,6 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternSynonyms #-} -{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} @@ -19,19 +18,18 @@ import Control.Concurrent (forkFinally, myThreadId, import Control.Exception (SomeException, handle, try) import Control.Monad (forM_, forever, void, (>=>)) import Control.Monad.IO.Class (liftIO) +import qualified Data.Aeson.Text as J import Data.Char (toUpper) import qualified Data.Map as M import qualified Data.Text as T +import qualified Data.Text.Lazy as TL import qualified Data.Vector as V import Network.GRPC.HighLevel.Client (ClientRequest (..), ClientResult (..)) import Network.GRPC.HighLevel.Generated (withGRPCClient) import qualified System.Console.Haskeline as RL import qualified System.Console.Haskeline.History as RL -import Text.RawString.QQ (r) -import qualified Data.Aeson.Text as J -import qualified Data.Text.Lazy as TL import HStream.Client.Action (createConnector, createStream, createStreamBySelectWithCustomQueryName, @@ -50,6 +48,8 @@ import HStream.Client.Types (HStreamCliContext (..), import HStream.Client.Utils (calculateShardId, dropPlanToResType) import HStream.Common.Types (hashShardKey) +import HStream.RawString (cliSqlHelpInfo, + cliSqlHelpInfos) import HStream.Server.HStreamApi (CommandQuery (..), CommandQueryResponse (..), HStreamApi (..), @@ -78,7 +78,7 @@ import HStream.Utils (HStreamClientApi, -- and this needs to be optimized. This could be done with a grpc client pool. interactiveSQLApp :: HStreamSqlContext -> Maybe FilePath -> IO () interactiveSQLApp sqlCtx@HStreamSqlContext{hstreamCliContext = cliCtx@HStreamCliContext{..}, ..} historyFile = do - putStrLn helpInfo + putStrLn cliSqlHelpInfo tid <- myThreadId void $ forkFinally maintainAvailableNodes (\case Left err -> throwTo tid err; _ -> return ()) RL.runInputT settings loop @@ -121,9 +121,9 @@ commandExec HStreamSqlContext{hstreamCliContext = cliCtx@HStreamCliContext{..},. -- ":listSubs":_ -> callListSubscriptions cliCtx -- -- } - ":h": _ -> putStrLn helpInfo + ":h": _ -> putStrLn cliSqlHelpInfo [":help"] -> putStr groupedHelpInfo - ":help":x:_ -> forM_ (M.lookup (map toUpper x) helpInfos) putStrLn + ":help":x:_ -> forM_ (M.lookup (map toUpper x) cliSqlHelpInfos) putStrLn (_:_) -> liftIO $ handle (\(e :: SomeSQLException) -> putStrLn . formatSomeSQLException $ e) $ do rSQL <- parseAndRefine $ T.pack xs @@ -191,69 +191,8 @@ sqlAction HStreamApi{..} sql = do putStr $ HStream.Utils.formatCommandQueryResponse x ClientErrorResponse _ -> putStr $ HStream.Utils.formatResult resp --- XXX: Can't build template-haskell with asan -#ifndef HSTREAM_ENABLE_ASAN -helpInfo :: String -helpInfo = - [r| -Command - :h To show these help info - :q To exit command line interface - :help [sql_operation] To show full usage of sql statement - -SQL STATEMENTS: - To create a simplest stream: - CREATE STREAM stream_name; - - To create a query select all fields from a stream: - SELECT * FROM stream_name EMIT CHANGES; - - To insert values to a stream: - INSERT INTO stream_name (field1, field2) VALUES (1, 2); - |] - -helpInfos :: M.Map String String -helpInfos = M.fromList [ - ("CREATE",[r| - CREATE STREAM [AS ] [ WITH ( {stream_options} ) ]; - CREATE {SOURCE|SINK} CONNECTOR [IF NOT EXIST] WITH ( {connector_options} ) ; - CREATE VIEW AS ; - |]), - ("INSERT",[r| - INSERT INTO ( {field_name} ) VALUES ( {field_value} ); - INSERT INTO VALUES CAST ('json_value' AS JSONB); - INSERT INTO VALUES CAST ('binary_value' AS BYTEA); - |]), - ("SELECT", [r| - SELECT <* | {expression [ AS field_alias ]}> - FROM stream_name_1 - [ join_type JOIN stream_name_2 - WITHIN (some_interval) - ON stream_name_1.field_1 = stream_name_2.field_2 ] - [ WHERE search_condition ] - [ GROUP BY field_name [, window_type] ] - EMIT CHANGES; - |]), - ("SHOW", [r| - SHOW ; - |]), - ("TERMINATE", [r| - TERMINATE |ALL>; - |]), - ("DROP", [r| - DROP |VIEW |QUERY > [IF EXISTS]; - |]) - ] -#else -helpInfo :: String -helpInfo = "" - -helpInfos :: M.Map String String -helpInfos = M.fromList [] -#endif - groupedHelpInfo :: String -groupedHelpInfo = ("SQL Statements\n" <> ) . unlines . map (\(x, y) -> x <> " " <> y) . M.toList $ helpInfos +groupedHelpInfo = ("SQL Statements\n" <> ) . unlines . map (\(x, y) -> x <> " " <> y) . M.toList $ cliSqlHelpInfos runActionWithGrpc :: HStreamCliContext -> (HStream.Utils.HStreamClientApi -> IO b) -> IO b diff --git a/hstream/src/HStream/Client/SQLNew.hs b/hstream/src/HStream/Client/SQLNew.hs index f46a1c66c..87fff8817 100644 --- a/hstream/src/HStream/Client/SQLNew.hs +++ b/hstream/src/HStream/Client/SQLNew.hs @@ -4,7 +4,6 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternSynonyms #-} -{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} @@ -18,10 +17,12 @@ import Control.Concurrent (forkFinally, myThreadId, import Control.Exception (SomeException, handle, try) import Control.Monad (forM_, forever, void, (>=>)) import Control.Monad.IO.Class (liftIO) +import qualified Data.Aeson.Text as J import qualified Data.ByteString.Lazy as BL import Data.Char (toUpper) import qualified Data.Map as M import qualified Data.Text as T +import qualified Data.Text.Lazy as TL import qualified Data.Vector as V import Network.GRPC.HighLevel.Client (ClientRequest (..), ClientResult (..)) @@ -29,10 +30,7 @@ import Network.GRPC.HighLevel.Generated (withGRPCClient) import qualified Proto3.Suite as PB import qualified System.Console.Haskeline as RL import qualified System.Console.Haskeline.History as RL -import Text.RawString.QQ (r) -import qualified Data.Aeson.Text as J -import qualified Data.Text.Lazy as TL import HStream.Client.Action (createConnector, createStream, createStreamBySelect, @@ -55,6 +53,8 @@ import HStream.Client.Types (HStreamCliContext (..), import HStream.Client.Utils (calculateShardId, dropPlanToResType) import HStream.Common.Types (hashShardKey) +import HStream.RawString (cliSqlHelpInfo, + cliSqlHelpInfos) import HStream.Server.HStreamApi (CommandQuery (..), CommandQueryResponse (..), HStreamApi (..), @@ -85,7 +85,7 @@ import HStream.Utils (HStreamClientApi, -- and this needs to be optimized. This could be done with a grpc client pool. interactiveSQLApp :: HStreamSqlContext -> Maybe FilePath -> IO () interactiveSQLApp sqlCtx@HStreamSqlContext{hstreamCliContext = cliCtx@HStreamCliContext{..}, ..} historyFile = do - putStrLn helpInfo + putStrLn cliSqlHelpInfo tid <- myThreadId void $ forkFinally maintainAvailableNodes (\case Left err -> throwTo tid err; _ -> return ()) RL.runInputT settings loop @@ -128,9 +128,9 @@ commandExec HStreamSqlContext{hstreamCliContext = cliCtx@HStreamCliContext{..},. -- ":listSubs":_ -> callListSubscriptions cliCtx -- -- } - ":h": _ -> putStrLn helpInfo + ":h": _ -> putStrLn cliSqlHelpInfo [":help"] -> putStr groupedHelpInfo - ":help":x:_ -> forM_ (M.lookup (map toUpper x) helpInfos) putStrLn + ":help":x:_ -> forM_ (M.lookup (map toUpper x) cliSqlHelpInfos) putStrLn (_:_) -> liftIO $ handle (\(e :: SomeSQLException) -> putStrLn . formatSomeSQLException $ e) $ do bSQL <- parseAndBind (T.pack xs) (cliGetSchema cliCtx) @@ -200,69 +200,8 @@ sqlAction HStreamApi{..} sql = do putStr $ HStream.Utils.formatCommandQueryResponse x ClientErrorResponse _ -> putStr $ HStream.Utils.formatResult resp --- XXX: Can't build template-haskell with asan -#ifndef HSTREAM_ENABLE_ASAN -helpInfo :: String -helpInfo = - [r| -Command - :h To show these help info - :q To exit command line interface - :help [sql_operation] To show full usage of sql statement - -SQL STATEMENTS: - To create a simplest stream: - CREATE STREAM stream_name; - - To create a query select all fields from a stream: - SELECT * FROM stream_name EMIT CHANGES; - - To insert values to a stream: - INSERT INTO stream_name (field1, field2) VALUES (1, 2); - |] - -helpInfos :: M.Map String String -helpInfos = M.fromList [ - ("CREATE",[r| - CREATE STREAM [AS ] [ WITH ( {stream_options} ) ]; - CREATE {SOURCE|SINK} CONNECTOR [IF NOT EXIST] WITH ( {connector_options} ) ; - CREATE VIEW AS ; - |]), - ("INSERT",[r| - INSERT INTO ( {field_name} ) VALUES ( {field_value} ); - INSERT INTO VALUES CAST ('json_value' AS JSONB); - INSERT INTO VALUES CAST ('binary_value' AS BYTEA); - |]), - ("SELECT", [r| - SELECT <* | {expression [ AS field_alias ]}> - FROM stream_name_1 - [ join_type JOIN stream_name_2 - WITHIN (some_interval) - ON stream_name_1.field_1 = stream_name_2.field_2 ] - [ WHERE search_condition ] - [ GROUP BY field_name [, window_type] ] - EMIT CHANGES; - |]), - ("SHOW", [r| - SHOW ; - |]), - ("TERMINATE", [r| - TERMINATE |ALL>; - |]), - ("DROP", [r| - DROP |VIEW |QUERY > [IF EXISTS]; - |]) - ] -#else -helpInfo :: String -helpInfo = "" - -helpInfos :: M.Map String String -helpInfos = M.fromList [] -#endif - groupedHelpInfo :: String -groupedHelpInfo = ("SQL Statements\n" <> ) . unlines . map (\(x, y) -> x <> " " <> y) . M.toList $ helpInfos +groupedHelpInfo = ("SQL Statements\n" <> ) . unlines . map (\(x, y) -> x <> " " <> y) . M.toList $ cliSqlHelpInfos runActionWithGrpc :: HStreamCliContext -> (HStream.Utils.HStreamClientApi -> IO b) -> IO b diff --git a/hstream/src/lib/HStream/RawString.hs b/hstream/src/lib/HStream/RawString.hs new file mode 100644 index 000000000..41991cc58 --- /dev/null +++ b/hstream/src/lib/HStream/RawString.hs @@ -0,0 +1,133 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE QuasiQuotes #-} + +{- +Since ghc doesn't support raw string literals, we have to use the +`Text.RawString.QQ` module to define raw string literals. + +Related proposals: + +- https://github.com/ghc-proposals/ghc-proposals/issues/260 +- https://github.com/ghc-proposals/ghc-proposals/pull/569 + +However, when using `Text.RawString.QQ`(template-haskell), it will cause the +following errors: + +1. Fail to build with ASAN. The workaroud is to use `#ifndef HSTREAM_ENABLE_ASAN` + to disable the raw string literals. + +2. A more complex issue is that when using `Text.RawString.QQ` with hstream + packages(e.g. hstream-client-cpp), it will cause the following error: + (At least with current hstreamdb/haskell image which uses system provided + jemalloc) + + ``` + : error: + /lib/x86_64-linux-gnu/libjemalloc.so.2: cannot allocate memory in static TLS block + ``` + + A simple reproducible example is to create a library which using + `Text.RawString.QQ` and has a dependency on hstream-common. + + FIXME: figure out the root cause of this issue. + + There are two workarouds for this issue: + + - Build a custom jemalloc with `--disable-initial-exec-tls` option, + See https://github.com/jemalloc/jemalloc/issues/1237 + - Put all the raw string literals in a separate library which doesn't + depend on hstream-*. This is the current solution we use. + +-} + +module HStream.RawString + ( banner + , cliBanner + , cliSqlHelpInfo + , cliSqlHelpInfos + ) where + +import qualified Data.Map as M +#ifndef HSTREAM_ENABLE_ASAN +import Text.RawString.QQ (r) +#endif + +banner, cliBanner, cliSqlHelpInfo :: String +cliSqlHelpInfos :: M.Map String String + +#ifndef HSTREAM_ENABLE_ASAN +banner = + [r| + _ _ __ _____ ___ ___ __ __ __ + | || |/' _/_ _| _ \ __|/ \| V | + | >< |`._`. | | | v / _|| /\ | \_/ | + |_||_||___/ |_| |_|_\___|_||_|_| |_| + + |] + +cliBanner = + [r| + __ _________________ _________ __ ___ + / / / / ___/_ __/ __ \/ ____/ | / |/ / + / /_/ /\__ \ / / / /_/ / __/ / /| | / /|_/ / + / __ /___/ // / / _, _/ /___/ ___ |/ / / / + /_/ /_//____//_/ /_/ |_/_____/_/ |_/_/ /_/ + + |] + +cliSqlHelpInfo = + [r| +Command + :h To show these help info + :q To exit command line interface + :help [sql_operation] To show full usage of sql statement + +SQL STATEMENTS: + To create a simplest stream: + CREATE STREAM stream_name; + + To create a query select all fields from a stream: + SELECT * FROM stream_name EMIT CHANGES; + + To insert values to a stream: + INSERT INTO stream_name (field1, field2) VALUES (1, 2); + |] + +cliSqlHelpInfos = M.fromList + [ ("CREATE", [r| + CREATE STREAM [AS ] [ WITH ( {stream_options} ) ]; + CREATE {SOURCE|SINK} CONNECTOR [IF NOT EXIST] WITH ( {connector_options} ) ; + CREATE VIEW AS ; + |]) + , ("INSERT", [r| + INSERT INTO ( {field_name} ) VALUES ( {field_value} ); + INSERT INTO VALUES CAST ('json_value' AS JSONB); + INSERT INTO VALUES CAST ('binary_value' AS BYTEA); + |]) + , ("SELECT", [r| + SELECT <* | {expression [ AS field_alias ]}> + FROM stream_name_1 + [ join_type JOIN stream_name_2 + WITHIN (some_interval) + ON stream_name_1.field_1 = stream_name_2.field_2 ] + [ WHERE search_condition ] + [ GROUP BY field_name [, window_type] ] + EMIT CHANGES; + |]) + , ("SHOW", [r| + SHOW ; + |]) + , ("TERMINATE", [r| + TERMINATE |ALL>; + |]) + , ("DROP", [r| + DROP |VIEW |QUERY > [IF EXISTS]; + |]) + ] + +#else +banner = "!!! ASAN Enabled !!!" +cliBanner = "!!! ASAN Enabled !!!" +cliSqlHelpInfo = "!!! ASAN Enabled !!!" +cliSqlHelpInfos = M.empty +#endif