content: -- Copyright (c) 2017 Uber Technologies, Inc.
  --
  -- Permission is hereby granted, free of charge, to any person obtaining a copy
  -- of this software and associated documentation files (the "Software"), to deal
  -- in the Software without restriction, including without limitation the rights
  -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  -- copies of the Software, and to permit persons to whom the Software is
  -- furnished to do so, subject to the following conditions:
  --
  -- The above copyright notice and this permission notice shall be included in
  -- all copies or substantial portions of the Software.
  --
  -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  -- THE SOFTWARE.
  
  {-# LANGUAGE FlexibleContexts #-}
  {-# LANGUAGE DeriveAnyClass #-}
  {-# LANGUAGE DeriveDataTypeable #-}
  {-# LANGUAGE DeriveGeneric #-}
  {-# LANGUAGE DeriveTraversable #-}
  {-# LANGUAGE StandaloneDeriving #-}
  {-# LANGUAGE ScopedTypeVariables #-}
  {-# LANGUAGE UndecidableInstances #-}
  
  module Database.Sql.Type.Query where
  
  import Database.Sql.Type.Names
  
  import Control.Applicative ((<|>))
  
  import qualified Data.Char as Char
  import           Data.Text.Lazy (Text)
  import qualified Data.Text.Lazy as TL
  import qualified Data.Text.Lazy.Encoding as TL
  import           Data.ByteString.Lazy (ByteString)
  import qualified Data.ByteString.Lazy as BL
  
  import Data.Aeson
  import Data.Foldable (toList)
  import Data.String (IsString (..))
  
  import Data.Data (Data)
  import GHC.Generics (Generic)
  
  import Test.QuickCheck
  
  
  data Query r a
      = QuerySelect a (Select r a)
      | QueryExcept a (ComposedQueryColumns r a) (Query r a) (Query r a)
      | QueryUnion a Distinct (ComposedQueryColumns r a) (Query r a) (Query r a)
      | QueryIntersect a (ComposedQueryColumns r a) (Query r a) (Query r a)
      | QueryWith a [CTE r a] (Query r a)
      | QueryOrder a [Order r a] (Query r a)
      | QueryLimit a (Limit a) (Query r a)
      | QueryOffset a (Offset a) (Query r a)
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (Query r a)
  deriving instance Generic (Query r a)
  deriving instance ConstrainSNames Eq r a => Eq (Query r a)
  deriving instance ConstrainSNames Ord r a => Ord (Query r a)
  deriving instance ConstrainSNames Show r a => Show (Query r a)
  deriving instance ConstrainSASNames Functor r => Functor (Query r)
  deriving instance ConstrainSASNames Foldable r => Foldable (Query r)
  deriving instance ConstrainSASNames Traversable r => Traversable (Query r)
  
  newtype Distinct = Distinct Bool
    deriving (Data, Generic, Eq, Ord, Show)
  
  notDistinct :: Distinct
  notDistinct = Distinct False
  
  data CTE r a = CTE
      { cteInfo :: a
      , cteAlias :: TableAlias a
      , cteColumns :: [ColumnAlias a]
      , cteQuery :: Query r a
      }
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (CTE r a)
  deriving instance Generic (CTE r a)
  deriving instance ConstrainSNames Eq r a => Eq (CTE r a)
  deriving instance ConstrainSNames Ord r a => Ord (CTE r a)
  deriving instance ConstrainSNames Show r a => Show (CTE r a)
  deriving instance ConstrainSASNames Functor r => Functor (CTE r)
  deriving instance ConstrainSASNames Foldable r => Foldable (CTE r)
  deriving instance ConstrainSASNames Traversable r => Traversable (CTE r)
  
  
  data Select r a = Select
      { selectInfo :: a
      , selectCols :: SelectColumns r a
      , selectFrom :: Maybe (SelectFrom r a)
      , selectWhere :: Maybe (SelectWhere r a)
      , selectTimeseries :: Maybe (SelectTimeseries r a)
      , selectGroup :: Maybe (SelectGroup r a)
      , selectHaving :: Maybe (SelectHaving r a)
      , selectNamedWindow :: Maybe (SelectNamedWindow r a)
      , selectDistinct :: Distinct
      }
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (Select r a)
  deriving instance Generic (Select r a)
  deriving instance ConstrainSNames Eq r a => Eq (Select r a)
  deriving instance ConstrainSNames Ord r a => Ord (Select r a)
  deriving instance ConstrainSNames Show r a => Show (Select r a)
  deriving instance ConstrainSASNames Functor r => Functor (Select r)
  deriving instance ConstrainSASNames Foldable r => Foldable (Select r)
  deriving instance ConstrainSASNames Traversable r => Traversable (Select r)
  
  
  data SelectColumns r a = SelectColumns
      { selectColumnsInfo :: a
      , selectColumnsList :: [Selection r a]
      }
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (SelectColumns r a)
  deriving instance Generic (SelectColumns r a)
  deriving instance ConstrainSNames Eq r a => Eq (SelectColumns r a)
  deriving instance ConstrainSNames Ord r a => Ord (SelectColumns r a)
  deriving instance ConstrainSNames Show r a => Show (SelectColumns r a)
  deriving instance ConstrainSASNames Functor r => Functor (SelectColumns r)
  deriving instance ConstrainSASNames Foldable r => Foldable (SelectColumns r)
  deriving instance ConstrainSASNames Traversable r => Traversable (SelectColumns r)
  
  
  data SelectFrom r a
      = SelectFrom a [Tablish r a]
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (SelectFrom r a)
  deriving instance Generic (SelectFrom r a)
  deriving instance ConstrainSNames Eq r a => Eq (SelectFrom r a)
  deriving instance ConstrainSNames Ord r a => Ord (SelectFrom r a)
  deriving instance ConstrainSNames Show r a => Show (SelectFrom r a)
  deriving instance ConstrainSASNames Functor r => Functor (SelectFrom r)
  deriving instance ConstrainSASNames Foldable r => Foldable (SelectFrom r)
  deriving instance ConstrainSASNames Traversable r => Traversable (SelectFrom r)
  
  
  data TablishAliases a
      = TablishAliasesNone
      | TablishAliasesT (TableAlias a)
      | TablishAliasesTC (TableAlias a) [ColumnAlias a]
        deriving (Generic, Data, Eq, Ord, Show, Functor, Foldable, Traversable)
  
  
  -- Tablish is a Table, Query, or join of 2 Tablishs
  data Tablish r a
      = TablishTable a (TablishAliases a) (TableRef r a)
      | TablishSubQuery a (TablishAliases a) (Query r a)
      | TablishJoin a (JoinType a) (JoinCondition r a)
              (Tablish r a) (Tablish r a)
      | TablishLateralView a (LateralView r a) (Maybe (Tablish r a))
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (Tablish r a)
  deriving instance Generic (Tablish r a)
  deriving instance ConstrainSNames Eq r a => Eq (Tablish r a)
  deriving instance ConstrainSNames Ord r a => Ord (Tablish r a)
  deriving instance ConstrainSNames Show r a => Show (Tablish r a)
  deriving instance ConstrainSASNames Functor r => Functor (Tablish r)
  deriving instance ConstrainSASNames Foldable r => Foldable (Tablish r)
  deriving instance ConstrainSASNames Traversable r => Traversable (Tablish r)
  
  
  data JoinType a
      = JoinInner a  -- JoinInner also encompasses CROSS JOIN (see D508260)
      | JoinLeft a
      | JoinRight a
      | JoinFull a
      | JoinSemi a
        deriving (Generic, Data, Eq, Ord, Show, Functor, Foldable, Traversable)
  
  
  data JoinCondition r a
      = JoinNatural a (NaturalColumns r a)
      | JoinOn (Expr r a)
      | JoinUsing a [UsingColumn r a]
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (JoinCondition r a)
  deriving instance Generic (JoinCondition r a)
  deriving instance ConstrainSNames Eq r a => Eq (JoinCondition r a)
  deriving instance ConstrainSNames Ord r a => Ord (JoinCondition r a)
  deriving instance ConstrainSNames Show r a => Show (JoinCondition r a)
  deriving instance ConstrainSASNames Functor r => Functor (JoinCondition r)
  deriving instance ConstrainSASNames Foldable r => Foldable (JoinCondition r)
  deriving instance ConstrainSASNames Traversable r => Traversable (JoinCondition r)
  
  data LateralView r a = LateralView
      { lateralViewInfo :: a
      , lateralViewOuter :: Maybe a
      , lateralViewExprs :: [Expr r a]
      , lateralViewWithOrdinality :: Bool
      , lateralViewAliases :: TablishAliases a
      }
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (LateralView r a)
  deriving instance Generic (LateralView r a)
  deriving instance ConstrainSNames Eq r a => Eq (LateralView r a)
  deriving instance ConstrainSNames Ord r a => Ord (LateralView r a)
  deriving instance ConstrainSNames Show r a => Show (LateralView r a)
  deriving instance ConstrainSASNames Functor r => Functor (LateralView r)
  deriving instance ConstrainSASNames Foldable r => Foldable (LateralView r)
  deriving instance ConstrainSASNames Traversable r => Traversable (LateralView r)
  
  
  
  data SelectWhere r a
      = SelectWhere a (Expr r a)
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (SelectWhere r a)
  deriving instance Generic (SelectWhere r a)
  deriving instance ConstrainSNames Eq r a => Eq (SelectWhere r a)
  deriving instance ConstrainSNames Ord r a => Ord (SelectWhere r a)
  deriving instance ConstrainSNames Show r a => Show (SelectWhere r a)
  deriving instance ConstrainSASNames Functor r => Functor (SelectWhere r)
  deriving instance ConstrainSASNames Foldable r => Foldable (SelectWhere r)
  deriving instance ConstrainSASNames Traversable r => Traversable (SelectWhere r)
  
  
  data SelectTimeseries r a = SelectTimeseries
      { selectTimeseriesInfo :: a
      , selectTimeseriesSliceName :: ColumnAlias a
      , selectTimeseriesInterval :: Constant a
      , selectTimeseriesPartition :: Maybe (Partition r a)
      , selectTimeseriesOrder :: Expr r a
      }
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (SelectTimeseries r a)
  deriving instance Generic (SelectTimeseries r a)
  deriving instance ConstrainSNames Eq r a => Eq (SelectTimeseries r a)
  deriving instance ConstrainSNames Ord r a => Ord (SelectTimeseries r a)
  deriving instance ConstrainSNames Show r a => Show (SelectTimeseries r a)
  deriving instance ConstrainSASNames Functor r => Functor (SelectTimeseries r)
  deriving instance ConstrainSASNames Foldable r => Foldable (SelectTimeseries r)
  deriving instance ConstrainSASNames Traversable r => Traversable (SelectTimeseries r)
  
  
  data PositionOrExpr r a
      = PositionOrExprPosition a Int (PositionExpr r a)
      | PositionOrExprExpr (Expr r a)
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (PositionOrExpr r a)
  deriving instance Generic (PositionOrExpr r a)
  deriving instance ConstrainSNames Eq r a => Eq (PositionOrExpr r a)
  deriving instance ConstrainSNames Ord r a => Ord (PositionOrExpr r a)
  deriving instance ConstrainSNames Show r a => Show (PositionOrExpr r a)
  deriving instance ConstrainSASNames Functor r => Functor (PositionOrExpr r)
  deriving instance ConstrainSASNames Foldable r => Foldable (PositionOrExpr r)
  deriving instance ConstrainSASNames Traversable r => Traversable (PositionOrExpr r)
  
  data GroupingElement r a
      = GroupingElementExpr a (PositionOrExpr r a)
      | GroupingElementSet a [Expr r a]
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (GroupingElement r a)
  deriving instance Generic (GroupingElement r a)
  deriving instance ConstrainSNames Eq r a => Eq (GroupingElement r a)
  deriving instance ConstrainSNames Ord r a => Ord (GroupingElement r a)
  deriving instance ConstrainSNames Show r a => Show (GroupingElement r a)
  deriving instance ConstrainSASNames Functor r => Functor (GroupingElement r)
  deriving instance ConstrainSASNames Foldable r => Foldable (GroupingElement r)
  deriving instance ConstrainSASNames Traversable r => Traversable (GroupingElement r)
  
  
  data SelectGroup r a = SelectGroup
      { selectGroupInfo :: a
      , selectGroupGroupingElements :: [GroupingElement r a]
      }
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (SelectGroup r a)
  deriving instance Generic (SelectGroup r a)
  deriving instance ConstrainSNames Eq r a => Eq (SelectGroup r a)
  deriving instance ConstrainSNames Ord r a => Ord (SelectGroup r a)
  deriving instance ConstrainSNames Show r a => Show (SelectGroup r a)
  deriving instance ConstrainSASNames Functor r => Functor (SelectGroup r)
  deriving instance ConstrainSASNames Foldable r => Foldable (SelectGroup r)
  deriving instance ConstrainSASNames Traversable r => Traversable (SelectGroup r)
  
  
  data SelectHaving r a
      = SelectHaving a [Expr r a]
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (SelectHaving r a)
  deriving instance Generic (SelectHaving r a)
  deriving instance ConstrainSNames Eq r a => Eq (SelectHaving r a)
  deriving instance ConstrainSNames Ord r a => Ord (SelectHaving r a)
  deriving instance ConstrainSNames Show r a => Show (SelectHaving r a)
  deriving instance ConstrainSASNames Functor r => Functor (SelectHaving r)
  deriving instance ConstrainSASNames Foldable r => Foldable (SelectHaving r)
  deriving instance ConstrainSASNames Traversable r => Traversable (SelectHaving r)
  
  data SelectNamedWindow r a
      = SelectNamedWindow a [NamedWindowExpr r a ]
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (SelectNamedWindow r a)
  deriving instance Generic (SelectNamedWindow r a)
  deriving instance ConstrainSNames Eq r a => Eq (SelectNamedWindow r a)
  deriving instance ConstrainSNames Ord r a => Ord (SelectNamedWindow r a)
  deriving instance ConstrainSNames Show r a => Show (SelectNamedWindow r a)
  deriving instance ConstrainSASNames Functor r => Functor (SelectNamedWindow r)
  deriving instance ConstrainSASNames Foldable r => Foldable (SelectNamedWindow r)
  deriving instance ConstrainSASNames Traversable r => Traversable (SelectNamedWindow r)
  
  
  data Order r a
      = Order a (PositionOrExpr r a) (OrderDirection (Maybe a)) (NullPosition (Maybe a))
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (Order r a)
  deriving instance Generic (Order r a)
  deriving instance ConstrainSNames Eq r a => Eq (Order r a)
  deriving instance ConstrainSNames Ord r a => Ord (Order r a)
  deriving instance ConstrainSNames Show r a => Show (Order r a)
  deriving instance ConstrainSASNames Functor r => Functor (Order r)
  deriving instance ConstrainSASNames Foldable r => Foldable (Order r)
  deriving instance ConstrainSASNames Traversable r => Traversable (Order r)
  
  data OrderDirection a
      = OrderAsc a
      | OrderDesc a
        deriving (Generic, Data, Eq, Ord, Show, Functor, Foldable, Traversable)
  
  data NullPosition a
      = NullsFirst a
      | NullsLast a
      | NullsAuto a
        deriving (Generic, Data, Eq, Ord, Show, Functor, Foldable, Traversable)
  
  
  data Offset a
      = Offset a Text
        deriving (Generic, Data, Eq, Ord, Show, Functor, Foldable, Traversable)
  
  data Limit a
      = Limit a Text
        deriving (Generic, Data, Eq, Ord, Show, Functor, Foldable, Traversable)
  
  
  data Selection r a
      = SelectStar a (Maybe (TableRef r a)) (StarReferents r a)
      | SelectExpr a [ColumnAlias a] (Expr r a)
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (Selection r a)
  deriving instance Generic (Selection r a)
  deriving instance ConstrainSNames Eq r a => Eq (Selection r a)
  deriving instance ConstrainSNames Ord r a => Ord (Selection r a)
  deriving instance ConstrainSNames Show r a => Show (Selection r a)
  deriving instance ConstrainSASNames Functor r => Functor (Selection r)
  deriving instance ConstrainSASNames Foldable r => Foldable (Selection r)
  deriving instance ConstrainSASNames Traversable r => Traversable (Selection r)
  
  
  data Constant a
      = StringConstant a ByteString
      -- ^ nb: Encoding *probably* matches server encoding, but there are ways to cram arbitrary byte sequences into strings on both Hive and Vertica.
      | NumericConstant a Text
      | NullConstant a
      | BooleanConstant a Bool
      | TypedConstant a Text (DataType a)
      | ParameterConstant a
        deriving (Generic, Data, Eq, Ord, Show, Functor, Foldable, Traversable)
  
  data DataTypeParam a
      = DataTypeParamConstant (Constant a)
      | DataTypeParamType (DataType a)
        deriving (Generic, Data, Eq, Ord, Show, Functor, Foldable, Traversable)
  
  data DataType a
      = PrimitiveDataType a Text [DataTypeParam a]
      | ArrayDataType a (DataType a)
      | MapDataType a (DataType a) (DataType a)
      | StructDataType a [(Text, DataType a)]
      | UnionDataType a [DataType a]
        deriving (Generic, Data, Eq, Ord, Show, Functor, Foldable, Traversable)
  
  
  data Operator a
      = Operator Text
        deriving (Generic, Data, Eq, Ord, Show, Functor, Foldable, Traversable)
  
  instance IsString (Operator a) where
      fromString = Operator . TL.pack
  
  data ArrayIndex a = ArrayIndex a Text
        deriving (Generic, Data, Eq, Ord, Show, Functor, Foldable, Traversable)
  
  data Expr r a
      = BinOpExpr a (Operator a) (Expr r a) (Expr r a)
      | CaseExpr a [(Expr r a, Expr r a)] (Maybe (Expr r a))
      | UnOpExpr a (Operator a) (Expr r a)
      | LikeExpr a (Operator a) (Maybe (Escape r a)) (Pattern r a) (Expr r a)
      | ConstantExpr a (Constant a)
      | ColumnExpr a (ColumnRef r a)
      | InListExpr a [Expr r a] (Expr r a)
      | InSubqueryExpr a (Query r a) (Expr r a)
      | BetweenExpr a (Expr r a) (Expr r a) (Expr r a)
      | OverlapsExpr a (Expr r a, Expr r a) (Expr r a, Expr r a)
  
      | FunctionExpr a
          (FunctionName a)
          Distinct
          [Expr r a]
          [(ParamName a, Expr r a)]
          (Maybe (Filter r a))
          (Maybe (OverSubExpr r a))
  
      | AtTimeZoneExpr a (Expr r a) (Expr r a)
  
      | SubqueryExpr a (Query r a)
      | ArrayExpr a [Expr r a]
      | ExistsExpr a (Query r a)
      | FieldAccessExpr a (Expr r a) (StructFieldName a)
      | ArrayAccessExpr a (Expr r a) (Expr r a)
      | TypeCastExpr a CastFailureAction (Expr r a) (DataType a)
      | VariableSubstitutionExpr a
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (Expr r a)
  deriving instance Generic (Expr r a)
  deriving instance ConstrainSNames Eq r a => Eq (Expr r a)
  deriving instance ConstrainSNames Ord r a => Ord (Expr r a)
  deriving instance ConstrainSNames Show r a => Show (Expr r a)
  deriving instance ConstrainSASNames Functor r => Functor (Expr r)
  deriving instance ConstrainSASNames Foldable r => Foldable (Expr r)
  deriving instance ConstrainSASNames Traversable r => Traversable (Expr r)
  
  
  data CastFailureAction
      = CastFailureToNull
      | CastFailureError
        deriving (Generic, Data, Eq, Ord, Show, ToJSON, FromJSON)
  
  
  newtype Escape r a = Escape
      { escapeExpr :: Expr r a
      }
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (Escape r a)
  deriving instance Generic (Escape r a)
  deriving instance ConstrainSNames Eq r a => Eq (Escape r a)
  deriving instance ConstrainSNames Ord r a => Ord (Escape r a)
  deriving instance ConstrainSNames Show r a => Show (Escape r a)
  deriving instance ConstrainSASNames Functor r => Functor (Escape r)
  deriving instance ConstrainSASNames Foldable r => Foldable (Escape r)
  deriving instance ConstrainSASNames Traversable r => Traversable (Escape r)
  
  
  newtype Pattern r a = Pattern
      { patternExpr :: Expr r a
      }
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (Pattern r a)
  deriving instance Generic (Pattern r a)
  deriving instance ConstrainSNames Eq r a => Eq (Pattern r a)
  deriving instance ConstrainSNames Ord r a => Ord (Pattern r a)
  deriving instance ConstrainSNames Show r a => Show (Pattern r a)
  deriving instance ConstrainSASNames Functor r => Functor (Pattern r)
  deriving instance ConstrainSASNames Foldable r => Foldable (Pattern r)
  deriving instance ConstrainSASNames Traversable r => Traversable (Pattern r)
  
  data Filter r a
      = Filter
          { filterInfo :: a
          , filterExpr :: Expr r a
          }
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (Filter r a)
  deriving instance Generic (Filter r a)
  deriving instance ConstrainSNames Eq r a => Eq (Filter r a)
  deriving instance ConstrainSNames Ord r a => Ord (Filter r a)
  deriving instance ConstrainSNames Show r a => Show (Filter r a)
  deriving instance ConstrainSASNames Functor r => Functor (Filter r)
  deriving instance ConstrainSASNames Foldable r => Foldable (Filter r)
  deriving instance ConstrainSASNames Traversable r => Traversable (Filter r)
  
  
  data Partition r a
      = PartitionBy a [Expr r a]
      | PartitionBest a
      | PartitionNodes a
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (Partition r a)
  deriving instance Generic (Partition r a)
  deriving instance ConstrainSNames Eq r a => Eq (Partition r a)
  deriving instance ConstrainSNames Ord r a => Ord (Partition r a)
  deriving instance ConstrainSNames Show r a => Show (Partition r a)
  deriving instance ConstrainSASNames Functor r => Functor (Partition r)
  deriving instance ConstrainSASNames Foldable r => Foldable (Partition r)
  deriving instance ConstrainSASNames Traversable r => Traversable (Partition r)
  
  data FrameType a
      = RowFrame a
      | RangeFrame a
        deriving (Generic, Data, Eq, Ord, Show, Functor, Foldable, Traversable)
  
  data FrameBound a
      = Unbounded a
      | CurrentRow a
      | Preceding a (Constant a)
      | Following a (Constant a)
        deriving (Generic, Data, Eq, Ord, Show, Functor, Foldable, Traversable)
  
  data Frame a = Frame
      { frameInfo :: a
      , frameType :: FrameType a
      , frameStart :: FrameBound a
      , frameEnd :: Maybe (FrameBound a)
      } deriving (Generic, Data, Eq, Ord, Show, Functor, Foldable, Traversable)
  
  data OverSubExpr r a
      = OverWindowExpr        a (WindowExpr r a)
      | OverWindowName        a (WindowName a)
      | OverPartialWindowExpr a (PartialWindowExpr r a)
  deriving instance (ConstrainSNames Data r a, Data r) => Data (OverSubExpr r a)
  deriving instance Generic (OverSubExpr r a)
  deriving instance ConstrainSNames Eq r a => Eq (OverSubExpr r a)
  deriving instance ConstrainSNames Ord r a => Ord (OverSubExpr r a)
  deriving instance ConstrainSNames Show r a => Show (OverSubExpr r a)
  deriving instance ConstrainSASNames Functor r => Functor (OverSubExpr r)
  deriving instance ConstrainSASNames Foldable r => Foldable (OverSubExpr r)
  deriving instance ConstrainSASNames Traversable r => Traversable (OverSubExpr r)
  
  data WindowExpr r a
      = WindowExpr
      { windowExprInfo :: a
      , windowExprPartition :: Maybe (Partition r a)
      , windowExprOrder :: [Order r a]
      , windowExprFrame :: Maybe (Frame a)
      }
  deriving instance (ConstrainSNames Data r a, Data r) => Data (WindowExpr r a)
  deriving instance Generic (WindowExpr r a)
  deriving instance ConstrainSNames Eq r a => Eq (WindowExpr r a)
  deriving instance ConstrainSNames Ord r a => Ord (WindowExpr r a)
  deriving instance ConstrainSNames Show r a => Show (WindowExpr r a)
  deriving instance ConstrainSASNames Functor r => Functor (WindowExpr r)
  deriving instance ConstrainSASNames Foldable r => Foldable (WindowExpr r)
  deriving instance ConstrainSASNames Traversable r => Traversable (WindowExpr r)
  
  data PartialWindowExpr r a
      = PartialWindowExpr
      { partWindowExprInfo :: a
      , partWindowExprInherit :: WindowName a
      , partWindowExprPartition :: Maybe (Partition r a)
      , partWindowExprOrder :: [Order r a]
      , partWindowExprFrame :: Maybe (Frame a)
      }
  deriving instance (ConstrainSNames Data r a, Data r) => Data (PartialWindowExpr r a)
  deriving instance Generic (PartialWindowExpr r a)
  deriving instance ConstrainSNames Eq r a => Eq (PartialWindowExpr r a)
  deriving instance ConstrainSNames Ord r a => Ord (PartialWindowExpr r a)
  deriving instance ConstrainSNames Show r a => Show (PartialWindowExpr r a)
  deriving instance ConstrainSASNames Functor r => Functor (PartialWindowExpr r)
  deriving instance ConstrainSASNames Foldable r => Foldable (PartialWindowExpr r)
  deriving instance ConstrainSASNames Traversable r => Traversable (PartialWindowExpr r)
  
  data WindowName a = WindowName a Text
    deriving (Generic, Data, Eq, Ord, Show, Functor, Foldable, Traversable)
  
  data NamedWindowExpr r a
      = NamedWindowExpr        a (WindowName a) (WindowExpr r a)
      | NamedPartialWindowExpr a (WindowName a) (PartialWindowExpr r a)
  deriving instance (ConstrainSNames Data r a, Data r) => Data (NamedWindowExpr r a)
  deriving instance Generic (NamedWindowExpr r a)
  deriving instance ConstrainSNames Eq r a => Eq (NamedWindowExpr r a)
  deriving instance ConstrainSNames Ord r a => Ord (NamedWindowExpr r a)
  deriving instance ConstrainSNames Show r a => Show (NamedWindowExpr r a)
  deriving instance ConstrainSASNames Functor r => Functor (NamedWindowExpr r)
  deriving instance ConstrainSASNames Foldable r => Foldable (NamedWindowExpr r)
  deriving instance ConstrainSASNames Traversable r => Traversable (NamedWindowExpr r)
  
  -- ToJSON
  
  instance ConstrainSNames ToJSON r a => ToJSON (Query r a) where
      toJSON (QuerySelect info select) = object
          [ "tag" .= String "QuerySelect"
          , "info" .= info
          , "select" .= select
          ]
  
      toJSON (QueryExcept info columns lhs rhs) = object
          [ "tag" .= String "Except"
          , "info" .= info
          , "columns" .= columns
          , "lhs" .= lhs
          , "rhs" .= rhs
          ]
  
      toJSON (QueryUnion info distinct columns lhs rhs) = object
          [ "tag" .= String "Union"
          , "info" .= info
          , "distinct" .= distinct
          , "columns" .= columns
          , "lhs" .= lhs
          , "rhs" .= rhs
          ]
  
      toJSON (QueryIntersect info columns lhs rhs) = object
          [ "tag" .= String "Intersect"
          , "info" .= info
          , "columns" .= columns
          , "lhs" .= lhs
          , "rhs" .= rhs
          ]
  
      toJSON (QueryWith info ctes query) = object
          [ "tag" .= String "With"
          , "info" .= info
          , "ctes" .= ctes
          , "query" .= query
          ]
  
      toJSON (QueryOrder info orders query) = object
          [ "tag" .= String "QueryOrder"
          , "info" .= info
          , "orders" .= orders
          , "query" .= query
          ]
  
      toJSON (QueryLimit info limit query) = object
          [ "tag" .= String "QueryLimit"
          , "info" .= info
          , "limit" .= limit
          , "query" .= query
          ]
  
      toJSON (QueryOffset info offset query) = object
          [ "tag" .= String "QueryOffset"
          , "info" .= info
          , "offset" .= offset
          , "query" .= query
          ]
  
  
  instance ToJSON Distinct where
      toJSON (Distinct bool) = toJSON bool
  
  
  instance ConstrainSNames ToJSON r a => ToJSON (CTE r a) where
      toJSON (CTE {..}) = object
          [ "tag" .= String "CTE"
          , "alias" .= cteAlias
          , "columns" .= cteColumns
          , "query" .= cteQuery
          ]
  
  
  instance ConstrainSNames ToJSON r a => ToJSON (Select r a) where
      toJSON (Select {..}) = object
           [ "tag" .= String "Select"
           , "cols" .= selectCols
           , "from" .= selectFrom
           , "where" .= selectWhere
           , "timeseries" .= selectTimeseries
           , "group" .= selectGroup
           , "having" .= selectHaving
           , "window" .= selectNamedWindow
           , "distinct" .= selectDistinct
           ]
  
  
  instance ConstrainSNames ToJSON r a => ToJSON (SelectColumns r a) where
      toJSON (SelectColumns info columns) = object
          [ "tag" .= String "SelectColumns"
          , "info" .= info
          , "columns" .= columns
          ]
  
  
  instance ConstrainSNames ToJSON r a => ToJSON (SelectFrom r a) where
      toJSON (SelectFrom info tables) = object
          [ "tag" .= String "SelectFrom"
          , "info" .= info
          , "tables" .= tables
          ]
  
  
  instance ConstrainSNames ToJSON r a => ToJSON (SelectWhere r a) where
      toJSON (SelectWhere info conditions) = object
          [ "tag" .= String "SelectWhere"
          , "info" .= info
          , "conditions" .= conditions
          ]
  
  
  instance ConstrainSNames ToJSON r a=> ToJSON (SelectTimeseries r a) where
      toJSON SelectTimeseries{..} = object
          [ "tag" .= String "SelectTimeseries"
          , "info" .= selectTimeseriesInfo
          , "slice_name" .= selectTimeseriesSliceName
          , "interval" .= selectTimeseriesInterval
          , "partition" .= selectTimeseriesPartition
          , "order" .= selectTimeseriesOrder
          ]
  
  
  instance ConstrainSNames ToJSON r a => ToJSON (PositionOrExpr r a) where
      toJSON (PositionOrExprPosition info pos expr) = object
          [ "tag" .= String "PositionOrExprPosition"
          , "info" .= info
          , "position" .= pos
          , "expr" .= expr
          ]
      toJSON (PositionOrExprExpr expr) = object
          [ "tag" .= String "PositionOrExprExpr"
          , "expr" .= expr
          ]
  
  
  instance ConstrainSNames ToJSON r a => ToJSON (GroupingElement r a) where
      toJSON (GroupingElementExpr info posOrExpr) = object
          [ "tag" .= String "GroupingElementExpr"
          , "info" .= info
          , "position_or_expr" .= posOrExpr
          ]
      toJSON (GroupingElementSet info exprs) = object
          [ "tag" .= String "GroupingElementSet"
          , "info" .= info
          , "exprs" .= exprs
          ]
  
  
  instance ConstrainSNames ToJSON r a => ToJSON (SelectGroup r a) where
      toJSON SelectGroup{..} = object
          [ "tag" .= String "SelectGroup"
          , "info" .= selectGroupInfo
          , "grouping_elements" .= selectGroupGroupingElements
          ]
  
  
  instance ConstrainSNames ToJSON r a => ToJSON (SelectHaving r a) where
      toJSON (SelectHaving info conditions) = object
          [ "tag" .= String "SelectHaving"
          , "info" .= info
          , "conditions" .= conditions
          ]
  
  
  instance ConstrainSNames ToJSON r a => ToJSON (SelectNamedWindow r a) where
      toJSON (SelectNamedWindow info windows) = object
          [ "tag" .= String "SelectNamedWindow"
          , "info" .= info
          , "windows" .= windows
          ]
  
  
  instance ConstrainSNames ToJSON r a => ToJSON (Selection r a) where
      toJSON (SelectStar info table referents) = object
          [ "tag" .= String "SelectStar"
          , "info" .= info
          , "table" .= table
          , "referents" .= referents
          ]
  
      toJSON (SelectExpr info aliases expr) = object
          [ "tag" .= String "SelectExpr"
          , "info" .= info
          , "aliases" .= aliases
          , "expr" .= expr
          ]
  
  
  instance ToJSON a => ToJSON (Constant a) where
      toJSON (StringConstant info value) = object
          [ "tag" .= String "StringConstant"
          , "info" .= info
          , case TL.decodeUtf8' value of
              Left _ -> "value" .= BL.unpack value
              Right str -> "value" .= str
          ]
  
      toJSON (NumericConstant info value) = object
          [ "tag" .= String "NumericConstant"
          , "info" .= info
          , "value" .= value
          ]
  
      toJSON (NullConstant info) = object
          [ "tag" .= String "NullConstant"
          , "info" .= info
          ]
  
      toJSON (BooleanConstant info value) = object
          [ "tag" .= String "BooleanConstant"
          , "info" .= info
          , "value" .= value
          ]
  
      toJSON (TypedConstant info value type_) = object
          [ "tag" .= String "TypedConstant"
          , "info" .= info
          , "value" .= value
          , "type" .= type_
          ]
  
      toJSON (ParameterConstant info) = object
          [ "tag" .= String "ParameterConstant"
          , "info" .= info
          ]
  
  
  instance ConstrainSNames ToJSON r a => ToJSON (Expr r a) where
  
      toJSON (BinOpExpr info op lhs rhs) = object
          [ "tag" .= String "BinOpExpr"
          , "info" .= info
          , "op" .= op
          , "lhs" .= lhs
          , "rhs" .= rhs
          ]
  
      toJSON (UnOpExpr info op expr) = object
          [ "tag" .= String "UnOpExpr"
          , "info" .= info
          , "op" .= op
          , "expr" .= expr
          ]
  
      toJSON (LikeExpr info op escape pattern expr) = object
          [ "tag" .= String "LikeExpr"
          , "info" .= info
          , "op" .= op
          , "escape" .= fmap escapeExpr escape
          , "pattern" .= patternExpr pattern
          , "expr" .= expr
          ]
  
      toJSON (CaseExpr info whens melse) = object
          [ "tag" .= String "CaseExpr"
          , "info" .= info
  
          , "whens" .=
              let conditionToJSON (c, r) =
                      object ["condition" .= c
                             , "result" .= r
                             ]
               in map conditionToJSON whens
  
          , "else" .= melse
          ]
  
      toJSON (ConstantExpr info constant) = object
          [ "tag" .= String "ConstantExpr"
          , "info" .= info
          , "constant" .= constant
          ]
  
      toJSON (ColumnExpr info column) = object
          [ "tag" .= String "ColumnExpr"
          , "info" .= info
          , "column" .= column
          ]
  
      toJSON (InListExpr info array expr) = object
          [ "tag" .= String "InListExpr"
          , "info" .= info
          , "array" .= array
          , "expr" .= expr
          ]
  
      toJSON (InSubqueryExpr info query expr) = object
          [ "tag" .= String "InSubqueryExpr"
          , "info" .= info
          , "query" .= query
          , "expr" .= expr
          ]
  
      toJSON (BetweenExpr info expr start end) = object
          [ "tag" .= String "BetweenExpr"
          , "info" .= info
          , "expr" .= expr
          , "start" .= start
          , "end" .= end
          ]
  
      toJSON (OverlapsExpr info lhs rhs) = object
          [ "tag" .= String "OverlapsExpr"
          , "info" .= info
          , "ranges" .=
              let rangeToJSON (start, end) =
                      object [ "start" .= start, "end" .= end ]
               in map rangeToJSON [lhs, rhs]
          ]
  
      toJSON (AtTimeZoneExpr info expr tz) = object
          [ "tag" .= String "AtTimeZoneExpr"
          , "info" .= info
          , "expr" .= expr
          , "tz" .= tz
          ]
  
      toJSON (SubqueryExpr info query) = object
          [ "tag" .= String "SubqueryExpr"
          , "info" .= info
          , "query" .= query
          ]
  
      toJSON (FunctionExpr info fn distinct args params filter' over) = object
          [ "tag" .= String "FunctionExpr"
          , "info" .= info
          , "function" .= fn
          , "distinct" .= distinct
          , "args" .= args
          , "params" .= params
          , "filter" .= filter'
          , "over" .= over
          ]
  
      toJSON (ExistsExpr info query) = object
          [ "tag" .= String "ExistsExpr"
          , "info" .= info
          , "query" .= query
          ]
  
      toJSON (ArrayExpr info values) = object
          [ "tag" .= String "ArrayExpr"
          , "info" .= info
          , "values" .= values
          ]
  
      toJSON (FieldAccessExpr info struct field) = object
          [ "tag" .= String "FieldAccessExpr"
          , "info" .= info
          , "struct" .= struct
          , "field" .= field
          ]
  
      toJSON (ArrayAccessExpr info expr idx) = object
          [ "tag" .= String "ArrayAccessExpr"
          , "info" .= info
          , "expr" .= expr
          , "idx" .= idx
          ]
  
      toJSON (TypeCastExpr info onFail expr type_) = object
          [ "tag" .= String "TypeCastExpr"
          , "info" .= info
          , "onfail" .= onFail
          , "expr" .= expr
          , "type" .= type_
          ]
  
      toJSON (VariableSubstitutionExpr info) = object
          [ "tag" .= String "VariableSubstitutionExpr"
          , "info" .= info
          ]
  
  
  instance ToJSON a => ToJSON (ArrayIndex a) where
      toJSON (ArrayIndex info value) = object
          [ "tag" .= String "ArrayIndex"
          , "info" .= info
          , "value" .= value
          ]
  
  
  instance ToJSON a => ToJSON (DataTypeParam a) where
      toJSON (DataTypeParamConstant constant) = object
          [ "tag" .= String "DataTypeParamConstant"
          , "param" .= constant
          ]
      toJSON (DataTypeParamType type_) = object
          [ "tag" .= String "DataTypeParamType"
          , "param" .= type_
          ]
  
  
  instance ToJSON a => ToJSON (DataType a) where
      toJSON (PrimitiveDataType info name args) = object
          [ "tag" .= String "PrimitiveDataType"
          , "info" .= info
          , "name" .= name
          , "args" .= args
          ]
      toJSON (ArrayDataType info itemType) = object
          [ "tag" .= String "ArrayDataType"
          , "info" .= info
          , "itemType" .= itemType
          ]
      toJSON (MapDataType info keyType valueType) = object
          [ "tag" .= String "MapDataType"
          , "info" .= info
          , "keyType" .= keyType
          , "valueType" .= valueType
          ]
      toJSON (StructDataType info fields) = object
          [ "tag" .= String "StructDataType"
          , "info" .= info
          , "fields" .= fields
          ]
      toJSON (UnionDataType info types) = object
          [ "tag" .= String "UnionDataType"
          , "info" .= info
          , "types" .= types
          ]
  
  
  instance ConstrainSNames ToJSON r a => ToJSON (Filter r a) where
      toJSON (Filter info expr) = object
          [ "tag" .= String "Filter"
          , "info" .= info
          , "expr" .= expr
          ]
  
  
  instance ConstrainSNames ToJSON r a => ToJSON (OverSubExpr r a) where
      toJSON (OverWindowExpr info windowExpr) = object
          [ "tag" .= String "OverWindowExpr"
          , "info" .= info
          , "windowExpr" .= windowExpr
          ]
  
      toJSON (OverWindowName info windowName) = object
          [ "tag" .= String "OverWindowName"
          , "info" .= info
          , "windowName" .= windowName
          ]
  
      toJSON (OverPartialWindowExpr info partialWindowExpr) = object
          [ "tag" .= String "OverPartialWindowExpr"
          , "info" .= info
          , "partialWindowExpr" .= partialWindowExpr
          ]
  
  instance ConstrainSNames ToJSON r a => ToJSON (WindowExpr r a) where
      toJSON (WindowExpr info partition order frame) = object
          [ "tag" .= String "WindowExpr"
          , "info" .= info
          , "partition" .= partition
          , "order" .= order
          , "frame" .= frame
          ]
  
  instance ConstrainSNames ToJSON r a => ToJSON (PartialWindowExpr r a) where
      toJSON (PartialWindowExpr info inherit partition order frame) = object
          [ "tag" .= String "PartialWindowExpr"
          , "info" .= info
          , "inherit" .= inherit
          , "partition" .= partition
          , "order" .= order
          , "frame" .= frame
          ]
  
  instance ToJSON a => ToJSON (WindowName a) where
      toJSON (WindowName info name) = object
          [ "tag" .= String "WindowName"
          , "info" .= info
          , "name" .= name
          ]
  
  
  instance ConstrainSNames ToJSON r a => ToJSON (NamedWindowExpr r a) where
      toJSON (NamedWindowExpr info name window) = object
          [ "tag" .= String "NamedWindowExpr"
          , "info" .= info
          , "name" .= name
          , "window" .= window
          ]
      toJSON (NamedPartialWindowExpr info name partialWindow) = object
          [ "tag" .= String "NamedWindowExpr"
          , "info" .= info
          , "name" .= name
          , "partialWindow" .= partialWindow
          ]
  
  
  instance ConstrainSNames ToJSON r a => ToJSON (Partition r a) where
      toJSON (PartitionBest info) = object
          [ "tag" .= String "PartitionBest"
          , "info" .= info
          ]
  
      toJSON (PartitionNodes info) = object
          [ "tag" .= String "PartitionNodes"
          , "info" .= info
          ]
  
      toJSON (PartitionBy info expr) = object
          [ "tag" .= String "PartitionBy"
          , "info" .= info
          , "expr" .= expr
          ]
  
  instance ConstrainSNames ToJSON r a => ToJSON (Order r a) where
      toJSON (Order info posOrExpr direction nullPos) = object
          [ "tag" .= String "Order"
          , "info" .= info
          , "position_or_expr" .= posOrExpr
          , "direction" .= direction
          , "nullPos" .= nullPos
          ]
  
  instance ToJSON a => ToJSON (FrameType a) where
      toJSON (RowFrame info) = object
          [ "tag" .= String "RowFrame"
          , "info" .= info
          ]
  
      toJSON (RangeFrame info) = object
          [ "tag" .= String "RangeFrame"
          , "info" .= info
          ]
  
  
  instance ToJSON a => ToJSON (FrameBound a) where
      toJSON (Unbounded info) = object
          [ "tag" .= String "Unbounded"
          , "info" .= info
          ]
  
      toJSON (CurrentRow info) = object
          [ "tag" .= String "CurrentRow"
          , "info" .= info
          ]
  
      toJSON (Preceding info bound) = object
          [ "tag" .= String "Preceding"
          , "info" .= info
          , "bound" .= bound
          ]
  
      toJSON (Following info bound) = object
          [ "tag" .= String "Following"
          , "info" .= info
          , "bound" .= bound
          ]
  
  
  instance ToJSON a => ToJSON (Frame a) where
      toJSON (Frame info ftype start end) = object
          [ "tag" .= String "Frame"
          , "info" .= info
          , "ftype" .= ftype
          , "start" .= start
          , "end" .= end
          ]
  
  
  instance ToJSON a => ToJSON (Offset a) where
      toJSON (Offset info offset) = object
          [ "tag" .= String "Offset"
          , "info" .= info
          , "offset" .= offset
          ]
  
  
  instance ToJSON a => ToJSON (Limit a) where
      toJSON (Limit info limit) = object
          [ "tag" .= String "Limit"
          , "info" .= info
          , "limit" .= limit
          ]
  
  
  instance ToJSON (Operator a) where
      toJSON (Operator op) = object
          [ "tag" .= String "Operator"
          , "operator" .= op
          ]
  
  
  instance ToJSON a => ToJSON (JoinType a) where
      toJSON (JoinInner info) = object
          [ "tag" .= String "JoinInner"
          , "info" .= info
          ]
      toJSON (JoinLeft info) = object
          [ "tag" .= String "JoinLeft"
          , "info" .= info
          ]
      toJSON (JoinRight info) = object
          [ "tag" .= String "JoinRight"
          , "info" .= info
          ]
      toJSON (JoinFull info) = object
          [ "tag" .= String "JoinFull"
          , "info" .= info
          ]
      toJSON (JoinSemi info) = object
          [ "tag" .= String "JoinSemi"
          , "info" .= info
          ]
  
  
  instance ConstrainSNames ToJSON r a => ToJSON (JoinCondition r a) where
      toJSON (JoinNatural info columns) = object [ "tag" .= String "JoinNatural", "info" .= info, "columns" .= columns ]
      toJSON (JoinOn expr) = object
          [ "tag" .= String "JoinOn"
          , "expr" .= expr
          ]
      toJSON (JoinUsing info columns) = object
          [ "tag" .= String "JoinUsing"
          , "info" .= info
          , "columns" .= columns
          ]
  
  
  instance ToJSON a => ToJSON (TablishAliases a) where
      toJSON (TablishAliasesNone) = object
          [ "tag" .= String "TablishAliasesNone"
          ]
      toJSON (TablishAliasesT table) = object
          [ "tag" .= String "TablishAliasesT"
          , "table" .= table
          ]
      toJSON (TablishAliasesTC table columns) = object
          [ "tag" .= String "TablishAliasesTC"
          , "table" .= table
          , "columns" .= columns
          ]
  
  
  instance ConstrainSNames ToJSON r a => ToJSON (Tablish r a) where
      toJSON (TablishTable info aliases table) = object
          [ "tag" .= String "TablishTable"
          , "info" .= info
          , "aliases" .= aliases
          , "table" .= table
          ]
  
      toJSON (TablishSubQuery info alias query) = object
          [ "tag" .= String "TablishSubQuery"
          , "info" .= info
          , "alias" .= alias
          , "query" .= query
          ]
  
      toJSON (TablishJoin info join condition outer inner) = object
          [ "tag" .= String "TablishJoin"
          , "info" .= info
          , "join" .= join
          , "condition" .= condition
          , "outer" .= outer
          , "inner" .= inner
          ]
  
      toJSON (TablishLateralView info view lhs) = object
          [ "tag" .= String "TablishLateralView"
          , "info" .= info
          , "view" .= view
          , "lhs" .= lhs
          ]
  
  
  instance ConstrainSNames ToJSON r a => ToJSON (LateralView r a) where
      toJSON LateralView{..} = object
          [ "tag" .= String "LateralView"
          , "info" .= lateralViewInfo
          , "outer" .= lateralViewOuter
          , "exprs" .= lateralViewExprs
          , "with_ordinality" .= lateralViewWithOrdinality
          , "aliases" .= lateralViewAliases
          ]
  
  
  instance ToJSON a => ToJSON (OrderDirection a) where
      toJSON (OrderAsc info) = object
          [ "tag" .= String "OrderAsc"
          , "info" .= info
          ]
  
      toJSON (OrderDesc info) = object
          [ "tag" .= String "OrderDesc"
          , "info" .= info
          ]
  
  
  instance ToJSON a => ToJSON (NullPosition a) where
      toJSON (NullsFirst info) = object
          [ "tag" .= String "NullsFirst"
          , "info" .= info
          ]
  
      toJSON (NullsLast info) = object
          [ "tag" .= String "NullsLast"
          , "info" .= info
          ]
  
      toJSON (NullsAuto info) = object
          [ "tag" .= String "NullsAuto"
          , "info" .= info
          ]
  
  
  -- FromJSON
  
  instance ConstrainSNames FromJSON r a => FromJSON (Query r a) where
      parseJSON (Object o) = o .: "tag" >>= \case
              String "QuerySelect" -> QuerySelect
                  <$> o .: "info"
                  <*> o .: "select"
  
              String "QueryExcept" -> QueryExcept
                  <$> o .: "info"
                  <*> o .: "columns"
                  <*> o .: "lhs"
                  <*> o .: "rhs"
  
              String "QueryUnion" -> QueryUnion
                  <$> o .: "info"
                  <*> o .: "distinct"
                  <*> o .: "columns"
                  <*> o .: "lhs"
                  <*> o .: "rhs"
  
              String "QueryIntersect" -> QueryIntersect
                  <$> o .: "info"
                  <*> o .: "columns"
                  <*> o .: "lhs"
                  <*> o .: "rhs"
  
              String "QueryWith" -> QueryWith
                  <$> o .: "info"
                  <*> o .: "ctes"
                  <*> o .: "query"
  
              String "QueryOrder" -> QueryOrder
                  <$> o .: "info"
                  <*> o .: "orders"
                  <*> o .: "query"
  
              String "QueryLimit" -> QueryLimit
                  <$> o .: "info"
                  <*> o .: "limit"
                  <*> o .: "query"
  
              String "QueryOffset" -> QueryOffset
                  <$> o .: "info"
                  <*> o .: "offset"
                  <*> o .: "query"
  
              _ -> fail "unrecognized tag on query object"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as Query:"
          , show v
          ]
  
  
  instance FromJSON Distinct where
      parseJSON (Bool bool) = return $ Distinct bool
      parseJSON v = fail $ unwords
          [ "don't know how to parse as Distinct:"
          , show v
          ]
  
  instance ConstrainSNames FromJSON r a => FromJSON (CTE r a) where
      parseJSON (Object o) = do
          String "CTE" <- o .: "tag"
          cteInfo <- o .: "info"
          cteAlias <- o .: "alias"
          cteColumns <- o .: "columns"
          cteQuery <- o .: "query"
          return CTE{..}
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as CTE:"
          , show v
          ]
  
  
  instance FromJSON a => FromJSON (OrderDirection a) where
      parseJSON (Object o) = o .: "tag" >>= \case
          String "OrderAsc" -> OrderAsc <$> o .: "info"
          String "OrderDesc" -> OrderDesc <$> o .: "info"
          _ -> fail "unrecognized tag on order direction object"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as OrderDirection:"
          ,  show v
          ]
  
  
  instance ConstrainSNames FromJSON r a => FromJSON (Selection r a) where
      parseJSON (Object o) = o .: "tag" >>= \case
          String "SelectStar" ->
              SelectStar
                  <$> o .: "info"
                  <*> o .: "table"
                  <*> o .: "referents"
  
          String "SelectExpr" ->
              SelectExpr
                  <$> o .: "info"
                  <*> o .: "aliases"
                  <*> o .: "expr"
  
          _ -> fail "unrecognized tag on selection object"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as Selection:"
          , show v
          ]
  
  
  instance FromJSON a => FromJSON (Constant a) where
      parseJSON (Object o) = o .: "tag" >>= \case
          String "StringConstant" -> do
              info <- o .: "info"
              value <- TL.encodeUtf8 <$> o .: "value"
                  <|> BL.pack <$> o .: "value"
                  <|> fail "expected string or array for StringConstant value"
              pure $ StringConstant info value
  
          String "NumericConstant" ->
              NumericConstant
                  <$> o .: "info"
                  <*> o .: "value"
  
          String "NullConstant" ->
              NullConstant <$> o .: "info"
  
          String "BooleanConstant" ->
              BooleanConstant
                  <$> o .: "info"
                  <*> o .: "value"
  
          String "TypedConstant" ->
              TypedConstant
                  <$> o .: "info"
                  <*> o .: "value"
                  <*> o .: "type"
  
          String "ParameterConstant" ->
              ParameterConstant <$> o .: "info"
  
          _ -> fail "unrecognized tag on constant object"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as Constant:"
          , show v
          ]
  
  
  instance ConstrainSNames FromJSON r a => FromJSON (Expr r a) where
      parseJSON (Object o) = o .: "tag" >>= \case
          String "BinOpExpr" ->
              BinOpExpr
                  <$> o .: "info"
                  <*> o .: "op"
                  <*> o .: "lhs"
                  <*> o .: "rhs"
  
          String "UnOpExpr" ->
              UnOpExpr
                  <$> o .: "info"
                  <*> o .: "op"
                  <*> o .: "expr"
  
          String "LikeExpr" ->
              LikeExpr
                  <$> o .: "info"
                  <*> o .: "op"
                  <*> (fmap Escape <$> o .: "escape")
                  <*> (Pattern <$> o .: "pattern")
                  <*> o .: "expr"
  
          String "CaseExpr" -> do
              let jsonToWhen (Object w) =
                      (,) <$> w .: "condition"
                          <*> w .: "result"
  
                  jsonToWhen v = fail $ unwords
                      [ "don't know how to parse as (Expr, Expr):"
                      , show v
                      ]
  
              whens <- mapM jsonToWhen =<< o .: "whens"
  
              CaseExpr <$> o .: "info" <*> pure whens <*> o .: "else"
  
          String "ConstantExpr" ->
              ConstantExpr
                  <$> o .: "info"
                  <*> o .: "constant"
  
          String "ColumnExpr" ->
              ColumnExpr
                  <$> o .: "info"
                  <*> o .: "column"
  
          String "InListExpr" ->
              InListExpr
                  <$> o .: "info"
                  <*> o .: "array"
                  <*> o .: "expr"
  
          String "InSubqueryExpr" ->
              InSubqueryExpr
                  <$> o .: "info"
                  <*> o .: "query"
                  <*> o .: "expr"
  
          String "BetweenExpr" ->
              BetweenExpr
                  <$> o .: "info"
                  <*> o .: "expr"
                  <*> o .: "start"
                  <*> o .: "end"
  
          String "OverlapsExpr" -> do
              info <- o .: "info"
              Array ranges <- o .: "ranges"
  
              let jsonToRange (Object r) = (,) <$> r .: "start"
                                               <*> r .: "end"
                  jsonToRange v = fail $ unwords
                      [ "don't know how to parse as (Expr, Expr):"
                      , show v
                      ]
  
              [range1, range2] <- mapM jsonToRange $ toList ranges
  
              return $ OverlapsExpr info range1 range2
  
          String "FunctionExpr" ->
              FunctionExpr
                  <$> o .: "info"
                  <*> o .: "function"
                  <*> o .: "distinct"
                  <*> o .: "args"
                  <*> o .: "params"
                  <*> o .: "filter"
                  <*> o .: "over"
  
          String "AtTimeZoneExpr" ->
              AtTimeZoneExpr
                  <$> o .: "info"
                  <*> o .: "expr"
                  <*> o .: "tz"
  
          String "ArrayExpr" ->
              ArrayExpr
                  <$> o .: "info"
                  <*> o .: "values"
  
          String "SubqueryExpr" ->
              SubqueryExpr
                  <$> o .: "info"
                  <*> o .: "query"
  
          String "ExistsExpr" ->
              ExistsExpr
                  <$> o .: "info"
                  <*> o .: "query"
  
          String "FieldAccessExpr" ->
              FieldAccessExpr
                  <$> o .: "info"
                  <*> o .: "struct"
                  <*> o .: "field"
  
          String "ArrayAccessExpr" ->
              ArrayAccessExpr
                  <$> o .: "info"
                  <*> o .: "expr"
                  <*> o .: "idx"
  
          String "TypeCastExpr" ->
              TypeCastExpr
                  <$> o .: "info"
                  <*> o .: "onfailure"
                  <*> o .: "expr"
                  <*> o .: "type"
  
          String "VariableSubstitutionExpr" ->
              VariableSubstitutionExpr <$> o .: "info"
  
          _ -> fail "unrecognized tag on expression object"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as Expr:"
          , show v
          ]
  
  
  instance FromJSON a => FromJSON (ArrayIndex a) where
      parseJSON (Object o) =  do
          String "ArrayIndex" <- o .: "tag"
          ArrayIndex
              <$> o .: "info"
              <*> o .: "value"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as ArrayIndex:"
          , show v
          ]
  
  
  instance FromJSON a => FromJSON (DataTypeParam a) where
      parseJSON (Object o) = o .: "tag" >>= \case
          String "DataTypeParamConstant" ->
              DataTypeParamConstant <$> o .: "param"
          String "DataTypeParamType" ->
              DataTypeParamType <$> o .: "param"
  
          _ -> fail "unrecognized tag on data type param object"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as DataTypeParam:"
          , show v
          ]
  
  instance FromJSON a => FromJSON (DataType a) where
      parseJSON (Object o) = o .: "tag" >>= \case
          String "PrimitiveDataType" ->
              PrimitiveDataType
                  <$> o .: "info"
                  <*> o .: "name"
                  <*> o .: "args"
          String "ArrayDataType" ->
              ArrayDataType
                  <$> o .: "info"
                  <*> o .: "itemType"
          String "MapDataType" ->
              MapDataType
                  <$> o .: "info"
                  <*> o .: "keyType"
                  <*> o .: "valueType"
          String "StructDataType" ->
              StructDataType
                  <$> o .: "info"
                  <*> o .: "fields"
          String "UnionDataType" ->
              UnionDataType
                  <$> o .: "info"
                  <*> o .: "types"
  
          _ -> fail "unrecognized tag on data type object"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as DataType:"
          , show v
          ]
  
  
  instance ConstrainSNames FromJSON r a => FromJSON (Filter r a) where
      parseJSON (Object o) = o .: "tag" >>= \case
          String "Filter" ->
            Filter
              <$> o .: "info"
              <*> o .: "expr"
          _ -> fail "unrecognized tag on Filter object"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as Filter:"
          , show v
          ]
  
  
  instance ConstrainSNames FromJSON r a => FromJSON (OverSubExpr r a) where
      parseJSON (Object o) = o .: "tag" >>= \case
          String "OverWindowExpr" ->
            OverWindowExpr
              <$> o .: "info"
              <*> o .: "windowExpr"
          String "OverWindowName" ->
            OverWindowName
              <$> o .: "info"
              <*> o .: "windowName"
          String "OverPartialWindowExpr" ->
            OverPartialWindowExpr
              <$> o .: "info"
              <*> o .: "partialWindowExpr"
          _ -> fail "unrecognized tag on OverSubExpr object"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as OverSubExpr:"
          , show v
          ]
  
  instance ConstrainSNames FromJSON r a => FromJSON (WindowExpr r a) where
      parseJSON (Object o) = do
          String "WindowExpr" <- o .: "tag"
          WindowExpr
              <$> o .: "info"
              <*> o .: "partition"
              <*> o .: "order"
              <*> o .: "frame"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as WindowExpr:"
          , show v
          ]
  
  instance ConstrainSNames FromJSON r a => FromJSON (PartialWindowExpr r a) where
      parseJSON (Object o) =
        do
          String "PartialWindowExpr" <- o .: "tag"
          PartialWindowExpr
              <$> o .: "info"
              <*> o .: "inherit"
              <*> o .: "partition"
              <*> o .: "order"
              <*> o .: "frame"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as PartialWindowExpr:"
          , show v
          ]
  
  instance FromJSON a => FromJSON (WindowName a) where
      parseJSON (Object o) =
        do
          String "WindowName" <- o .: "tag"
          WindowName
              <$> o .: "info"
              <*> o .: "name"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as WindowName:"
          , show v
          ]
  
  
  instance ConstrainSNames FromJSON r a => FromJSON (NamedWindowExpr r a) where
      parseJSON (Object o) = o .: "tag" >>= \case
          String "NamedWindowExpr" ->
            NamedWindowExpr
              <$> o .: "info"
              <*> o .: "name"
              <*> o .: "window"
          String "NamedPartialWindowExpr" ->
            NamedPartialWindowExpr
              <$> o .: "info"
              <*> o .: "name"
              <*> o .: "partialWindow"
          _ -> fail "unrecognized tag on NamedWindowExpr object"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as NamedWindowExpr:"
          , show v
          ]
  
  
  instance ConstrainSNames FromJSON r a => FromJSON (Partition r a) where
      parseJSON (Object o) = o .: "tag" >>= \case
          String "PartitionBest" -> PartitionBest <$> o .: "info"
          String "PartitionNodes" -> PartitionNodes <$> o .: "info"
          String "PartitionBy" -> PartitionBy <$> o .: "info" <*> o .: "expr"
  
          _ -> fail "unrecognized tag on partition object"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as Partition:"
          , show v
          ]
  
  instance ConstrainSNames FromJSON r a => FromJSON (Order r a) where
      parseJSON (Object o) = do
        String "Order" <- o .: "tag"
        Order
            <$> o .: "info"
            <*> o .: "position_or_expr"
            <*> o .: "direction"
            <*> o .: "nullPos"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as Order:"
          , show v
          ]
  
  
  instance FromJSON a => FromJSON (NullPosition a) where
      parseJSON (Object o) = o .: "tag" >>= \case
          String "NullsFirst" -> NullsFirst <$> o .: "info"
          String "NullsLast" -> NullsLast <$> o .: "info"
          String "NullsAuto" -> NullsAuto <$> o .: "info"
  
          _ -> fail "unrecognized tag on null position object"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as NullPosition:"
          , show v
          ]
  
  
  instance FromJSON a => FromJSON (FrameType a) where
      parseJSON (Object o) = o .: "tag" >>= \case
          String "RowFrame" -> RowFrame <$> o .: "info"
          String "RangeFrame" -> RangeFrame <$> o .: "info"
  
          _ -> fail "unrecognized tag on frame type object"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as FrameType:"
          , show v
          ]
  
  
  instance FromJSON a => FromJSON (FrameBound a) where
      parseJSON (Object o) = o .: "tag" >>= \case
           String "Unbounded" -> Unbounded <$> o .: "info"
           String "CurrentRow" -> CurrentRow <$> o .: "info"
           String "Preceding" -> Preceding <$> o .: "info" <*> o .: "bound"
           String "Following" -> Following <$> o .: "info" <*> o .: "bound"
  
           _ -> fail "unrecognized tag on frame bound object"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as FrameBound:"
          , show v
          ]
  
  
  instance FromJSON a => FromJSON (Frame a) where
      parseJSON (Object o) = do
          String "Frame" <- o .: "tag"
          Frame <$> o .: "info"
                <*> o .: "ftype"
                <*> o .: "start"
                <*> o .: "end"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as Frame:"
          , show v
          ]
  
  
  instance FromJSON a => FromJSON (Offset a) where
      parseJSON (Object o) = do
          String "Offset" <- o .: "tag"
          Offset <$> o .: "info" <*> o .: "offset"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as Offset:"
          , show v
          ]
  
  
  instance FromJSON a => FromJSON (Limit a) where
      parseJSON (Object o) = do
          String "Limit" <- o .: "tag"
          Limit <$> o .: "info" <*> o .: "limit"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as Limit:"
          , show v
          ]
  
  
  instance FromJSON (Operator a) where
      parseJSON (Object o) = do
          String "Operator" <- o .: "tag"
          Operator <$> o .: "operator"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as Operator:"
          , show v
          ]
  
  
  instance FromJSON a => FromJSON (JoinType a) where
      parseJSON (Object o) = o .: "tag" >>= \case
          String "JoinInner" -> JoinInner <$> o .: "info"
          String "JoinLeft" -> JoinLeft <$> o .: "info"
          String "JoinRight" -> JoinRight <$> o .: "info"
          String "JoinFull" -> JoinFull <$> o .: "info"
          String "JoinSemi" -> JoinSemi <$> o .: "info"
          _ -> fail "unrecognized tag on JoinType object"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as JoinType:"
          , show v
          ]
  
  
  instance ConstrainSNames FromJSON r a => FromJSON (JoinCondition r a) where
      parseJSON (Object o) = o .: "tag" >>= \case
          String "JoinNatural" -> JoinNatural <$> o .: "info" <*> o .: "columns"
          String "JoinOn" -> JoinOn <$> o .: "expr"
          String "JoinUsing" -> JoinUsing <$> o .: "info" <*> o .: "columns"
          _ -> fail "unrecognized tag on JoinCondition object"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as JoinCondition:"
          , show v
          ]
  
  
  instance FromJSON a => FromJSON (TablishAliases a) where
      parseJSON (Object o) = o .: "tag" >>= \case
          String "TablishAliasesNone" -> return TablishAliasesNone
          String "TablishAliasesT" -> TablishAliasesT <$> o .: "table"
          String "TablishAliasesTC" -> TablishAliasesTC <$> o .: "table" <*> o .: "columns"
          _ -> fail "unrecognized tag on TablishAliases object"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as TablishAliases:"
          , show v
          ]
  
  
  instance ConstrainSNames FromJSON r a => FromJSON (Tablish r a) where
      parseJSON (Object o) = o .: "tag" >>= \case
          String "TablishTable" ->
              TablishTable
                  <$> o .: "info"
                  <*> o .: "aliases"
                  <*> o .: "table"
  
          String "TablishSubQuery" ->
              TablishSubQuery
                  <$> o .: "info"
                  <*> o .: "alias"
                  <*> o .: "query"
  
          String "TablishJoin" ->
              TablishJoin
                  <$> o .: "info"
                  <*> o .: "join"
                  <*> o .: "condition"
                  <*> o .: "outer"
                  <*> o .: "inner"
  
          String "TablishLateralView" ->
              TablishLateralView
                  <$> o .: "info"
                  <*> o .: "view"
                  <*> o .: "lhs"
  
          _ -> fail "unrecognized tag on tablish object"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as Tablish:"
          , show v
          ]
  
  
  instance ConstrainSNames FromJSON r a => FromJSON (LateralView r a) where
      parseJSON (Object o) = do
          String "LateralView" <- o .: "tag"
          lateralViewInfo <- o .: "info"
          lateralViewOuter <- o .: "outer"
          lateralViewExprs <- o .: "exprs"
          lateralViewWithOrdinality <- o .: "with_ordinality"
          lateralViewAliases <- o .: "aliases"
          pure LateralView{..}
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as LateralView:"
          , show v
          ]
  
  
  instance ConstrainSNames FromJSON r a => FromJSON (Select r a) where
      parseJSON (Object o) = do
          String "Select" <- o .: "tag"
          selectInfo <- o .: "info"
          selectCols <- o .: "cols"
          selectFrom <- o .: "from"
          selectWhere <- o .: "where"
          selectTimeseries <- o .:? "timeseries"
          selectGroup <- o .: "group"
          selectHaving <- o .: "having"
          selectNamedWindow <- o .: "window"
          selectDistinct <- o .: "distinct"
          return Select{..}
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as Select:"
          , show v
          ]
  
  
  instance ConstrainSNames FromJSON r a => FromJSON (SelectColumns r a) where
      parseJSON (Object o) = do
          String "SelectColumns" <- o .: "tag"
          SelectColumns <$> o .: "info" <*> o .: "columns"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as SelectColumns:"
          , show v
          ]
  
  
  instance ConstrainSNames FromJSON r a => FromJSON (SelectFrom r a) where
      parseJSON (Object o) = do
          String "SelectFrom" <- o .: "tag"
          SelectFrom <$> o .: "info" <*> o .: "tables"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as SelectFrom:"
          , show v
          ]
  
  
  instance ConstrainSNames FromJSON r a => FromJSON (SelectWhere r a) where
      parseJSON (Object o) = do
          String "SelectWhere" <- o .: "tag"
          SelectWhere <$> o .: "info" <*> o .: "conditions"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as SelectWhere:"
          , show v
          ]
  
  
  instance ConstrainSNames FromJSON r a => FromJSON (SelectTimeseries r a) where
      parseJSON (Object o) = do
          String "SelectTimeseries" <- o .: "tag"
          selectTimeseriesInfo <- o .: "info"
          selectTimeseriesSliceName <- o .: "slice_name"
          selectTimeseriesInterval <- o .: "interval"
          selectTimeseriesPartition <- o .: "partition"
          selectTimeseriesOrder <- o .: "order"
          pure SelectTimeseries{..}
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as SelectTimeseries:"
          , show v
          ]
  
  
  instance ConstrainSNames FromJSON r a => FromJSON (PositionOrExpr r a) where
      parseJSON (Object o) = o .: "tag" >>= \case
          String "PositionOrExprPosition" -> PositionOrExprPosition <$> o .: "info" <*> o .: "position" <*> o .: "expr"
          String "PositionOrExprExpr" -> PositionOrExprExpr <$> o .: "expr"
          _ -> fail "unrecognized tag on PositionOrExpr object"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as PositionOrExpr:"
          , show v
          ]
  
  
  instance ConstrainSNames FromJSON r a => FromJSON (GroupingElement r a) where
      parseJSON (Object o) = o .: "tag" >>= \case
          String "GroupingElementExpr" -> GroupingElementExpr <$> o .: "info" <*> o .: "position_or_expr"
          String "GroupingElementSet" -> GroupingElementSet <$> o .: "info" <*> o .: "exprs"
          _ -> fail "unrecognized tag on GroupingElement object"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as GroupingElement:"
          , show v
          ]
  
  
  instance ConstrainSNames FromJSON r a => FromJSON (SelectGroup r a) where
      parseJSON (Object o) = do
          String "SelectGroup" <- o .: "tag"
          selectGroupInfo <- o .: "info"
          selectGroupGroupingElements <- o .: "grouping_elements"
          pure $ SelectGroup{..}
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as SelectGroup:"
          , show v
          ]
  
  
  instance ConstrainSNames FromJSON r a => FromJSON (SelectHaving r a) where
      parseJSON (Object o) = do
          String "SelectHaving" <- o .: "tag"
          SelectHaving <$> o .: "info" <*> o .: "conditions"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as SelectHaving:"
          , show v
          ]
  
  
  instance ConstrainSNames FromJSON r a => FromJSON (SelectNamedWindow r a) where
      parseJSON (Object o) = do
          String "SelectNamedWindow" <- o .: "tag"
          SelectNamedWindow
              <$> o .: "info"
              <*> o .: "windows"
  
      parseJSON v = fail $ unwords
          [ "don't know how to parse as SelectNamedWindow:"
          , show v
          ]
  
  
  -- Arbitrary helpers
  
  arbitraryDate :: Gen Text
  arbitraryDate = do
      y <- choose (0,3000) :: Gen Int
      m <- choose (1,12) :: Gen Int
      d <- choose (1,28) :: Gen Int
      pure $ TL.intercalate "-" $ map (TL.pack . show) [y,m,d]
  
  arbitraryTime :: Gen Text
  arbitraryTime = do
      h <- choose (0,23) :: Gen Int
      m <- choose (0,59) :: Gen Int
      s <- choose (0,59) :: Gen Int
      pure $ TL.intercalate ":" $ map (TL.pack . show) [h,m,s]
  
  arbitraryTimestamp :: Gen Text
  arbitraryTimestamp = do
      d <- arbitraryDate
      t <- arbitraryTime
      pure $ TL.unwords [d, t]
  
  arbitraryInterval :: Gen Text
  arbitraryInterval = do
      n <- (TL.pack . show) <$> (arbitrary :: Gen Int)
      period <- elements ["days", "weeks", "hours"]
      pure $ TL.unwords [n, period]
  
  arbitraryText :: Gen Text
  arbitraryText = TL.pack <$> oneof
      [ arbitrary
      , listOf $ arbitrary `suchThat` Char.isPrint
      ]
  
  arbitraryByteString :: Gen ByteString
  arbitraryByteString = oneof
      [ BL.pack <$> arbitrary
      , TL.encodeUtf8 <$> arbitraryText
      ]
  
  shrinkByteString :: ByteString -> [ByteString]
  shrinkByteString str
      | BL.null str = []
      | BL.length str == 1 = [""]
      | otherwise =
          [ ""
          , BL.take halfLen str
          , BL.drop halfLen str
          ] ++ if any (not . Char.isPrint . Char.chr . fromIntegral) $ BL.unpack str
                then [BL.filter (Char.isPrint . Char.chr . fromIntegral) str]
                else []
    where
      halfLen = BL.length str `div` 2
  
  
  -- Arbitrary queries
  
  instance Arbitrary a => Arbitrary (Constant a) where
      arbitrary = oneof
          [ StringConstant <$> arbitrary
                           <*> arbitraryByteString
          , NumericConstant <$> arbitrary <*> oneof
              [ fmap (TL.pack . show) (arbitrary :: Gen Int)
              , fmap (TL.pack . show) (arbitrary :: Gen Float)
              ]
          , NullConstant <$> arbitrary
          , BooleanConstant <$> arbitrary <*> arbitrary
          , do
              info <- arbitrary
              (text, dataType) <- oneof
                  [ do
                      value <- arbitraryText
                      info' <- arbitrary
                      let name = "VARCHAR"
                      pure (value, PrimitiveDataType info' name [])
                  , do
                      value <- arbitraryText
                      info' <- arbitrary
                      len <- arbitrary `suchThat` (>0) :: Gen Int
                      let name = "VARCHAR"
                      lenConst <- NumericConstant <$> arbitrary <*> pure (TL.pack $ show len)
                      pure (value, PrimitiveDataType info' name [DataTypeParamConstant lenConst])
                  , do
                      value <- arbitraryTimestamp
                      info' <- arbitrary
                      name <- elements ["TIMESTAMP", "TIMESTAMP WITH TIME ZONE"]
                      pure (value, PrimitiveDataType info' name [])
                  , do
                      value <- fmap (TL.pack . show) $ (arbitrary :: Gen Int)
                      info' <- arbitrary
                      let name = "INT"
                      pure (value, PrimitiveDataType info' name [])
                  , do
                      value <- arbitraryInterval
                      info' <- arbitrary
                      let name = "INTERVAL"
                      pure (value, PrimitiveDataType info' name [])
                  ]
              pure $ TypedConstant info text dataType
          , ParameterConstant <$> arbitrary
          ]
      shrink (StringConstant info s) = StringConstant info <$> shrinkByteString s
  
      shrink (NumericConstant info n) =
          case reads $ TL.unpack n of
              [] -> []
              (prefix :: Float, _):_ -> NumericConstant info . TL.pack . show <$> shrink prefix
  
      shrink (NullConstant _) = []
  
      shrink (BooleanConstant _ True) = []
      shrink (BooleanConstant info False) = [BooleanConstant info True]
  
      shrink (TypedConstant _ _ (PrimitiveDataType _ "VARCHAR" [])) = []
      shrink (TypedConstant info value (PrimitiveDataType info' "TIMESTAMP WITH TIME ZONE" args)) =
          [TypedConstant info value (PrimitiveDataType info' "TIMESTAMP" args)]
      shrink (TypedConstant info value (PrimitiveDataType info' _ _)) =
          [TypedConstant info value (PrimitiveDataType info' "VARCHAR" [])]
      shrink (TypedConstant _ _ _) =
          fail "TODO: shrink for complex data types"
  
      shrink (ParameterConstant _) = []
  
  scaleDown :: Int -> Gen a -> Gen a
  scaleDown n = scale (`div` n)
  
  instance (Arbitrary (PositionExpr r a), Arbitrary a) => Arbitrary (Partition r a) where
      arbitrary = oneof
          [ PartitionBy <$> arbitrary <*> (scaleDown 5 arbitrary)
          , PartitionBest <$> arbitrary
          , PartitionNodes <$> arbitrary
          ]
      shrink (PartitionBest _) = []
      shrink (PartitionNodes info) = [PartitionBest info]
      shrink (PartitionBy info _) = [PartitionBest info]
  
  instance (Arbitrary (PositionExpr r a), Arbitrary a) => Arbitrary (PositionOrExpr r a) where
      arbitrary = oneof
          [ PositionOrExprPosition <$> arbitrary <*> arbitrary <*> arbitrary
          , PositionOrExprExpr <$> arbitrary
          ]
      shrink (PositionOrExprPosition info _ expr) = PositionOrExprPosition info 1 <$> shrink expr
      shrink (PositionOrExprExpr expr) = PositionOrExprExpr <$> shrink expr
  
  instance (Arbitrary (PositionExpr r a), Arbitrary a) => Arbitrary (Order r a) where
      arbitrary = Order <$> arbitrary <*> (scaleDown 5 arbitrary) <*> arbitrary <*> arbitrary
  
  instance Arbitrary a => Arbitrary (OrderDirection a) where
      arbitrary = oneof
          [ OrderAsc <$> arbitrary
          , OrderDesc <$> arbitrary
          ]
      shrink (OrderAsc _) = []
      shrink (OrderDesc info) = [OrderAsc info]
  
  instance Arbitrary a => Arbitrary (NullPosition a) where
      arbitrary = oneof
          [ NullsFirst <$> arbitrary
          , NullsLast <$> arbitrary
          , NullsAuto <$> arbitrary
          ]
      shrink (NullsFirst _) = []
      shrink (NullsLast info) = [NullsFirst info]
      shrink (NullsAuto info) = [NullsFirst info]
  
  instance Arbitrary a => Arbitrary (FrameType a) where
      arbitrary = oneof
          [ RowFrame <$> arbitrary
          , RangeFrame <$> arbitrary
          ]
      shrink (RowFrame _) = []
      shrink (RangeFrame info) = [RowFrame info]
  
  instance Arbitrary a => Arbitrary (FrameBound a) where
      arbitrary = oneof
          [ Unbounded <$> arbitrary
          , CurrentRow <$> arbitrary
          , Preceding <$> arbitrary <*> arbitrary
          , Following <$> arbitrary <*> arbitrary
          ]
      shrink (Unbounded _) = []
      shrink (CurrentRow info) = [Unbounded info]
      shrink (Preceding info _) = [Unbounded info]
      shrink (Following info _) = [Unbounded info]
  
  instance Arbitrary a => Arbitrary (Frame a) where
      arbitrary = do
          frameInfo <- arbitrary
          frameType <- arbitrary
          frameStart <- arbitrary
          frameEnd <- arbitrary
          pure $ Frame{..}
      shrink (Frame i t s e) = [Frame i t' s' e' | (t', s', e') <- shrink (t, s, e)]
  
  instance (Arbitrary (PositionExpr r a), Arbitrary a) => Arbitrary (OverSubExpr r a) where
      arbitrary = do
        info <- arbitrary
        oneof
            [ OverWindowExpr info <$> arbitrary
            , OverWindowName info <$> arbitrary
            , OverPartialWindowExpr info <$> arbitrary
            ]
      shrink (OverWindowExpr info e) = OverWindowExpr info <$> shrink e
      shrink (OverWindowName _ _) = [] -- Ideally resolve name, once implemented
      shrink (OverPartialWindowExpr info e) =
        OverPartialWindowExpr info <$> shrink e
  
  instance (Arbitrary (PositionExpr r a), Arbitrary a) => Arbitrary (WindowExpr r a) where
      arbitrary = do
          windowExprInfo <- arbitrary
          windowExprPartition <- arbitrary
          windowExprOrder <- scaleDown 5 arbitrary
          windowExprFrame <- arbitrary
          pure $ WindowExpr{..}
      shrink (WindowExpr i p o f) = [WindowExpr i p' o' f' | (p', o', f') <- shrink (p, o, f)]
  
  instance Arbitrary a => Arbitrary (WindowName a) where
      arbitrary =
        do
          info <- arbitrary
          name <- TL.pack <$> arbitrary
          pure $ WindowName info name
  
  instance (Arbitrary (PositionExpr r a), Arbitrary a) => Arbitrary (PartialWindowExpr r a) where
      arbitrary =
        do
          partWindowExprInfo <- arbitrary
          partWindowExprInherit <- arbitrary
          partWindowExprPartition <- arbitrary
          partWindowExprOrder <- scaleDown 5 arbitrary
          partWindowExprFrame <- arbitrary
          pure $ PartialWindowExpr{..}
  
      shrink (PartialWindowExpr i n p o f) =
        [PartialWindowExpr i n p' o' f' | (p', o', f') <- shrink (p, o, f)]
  
  instance Arbitrary Distinct where
      arbitrary = Distinct <$> arbitrary
      shrink (Distinct bool) = Distinct <$> shrink bool
  
  instance (Arbitrary (PositionExpr r a), Arbitrary a) => Arbitrary (Filter r a) where
      arbitrary = Filter <$> arbitrary <*> arbitrary
      shrink (Filter info expr) = Filter info <$> shrink expr
  
  instance (Arbitrary (PositionExpr r a), Arbitrary a) => Arbitrary (Expr r a) where
      arbitrary = sized $ \size -> do
          info <- arbitrary
          frequency
              [ (5, ConstantExpr info <$> arbitrary)
              , (1, FunctionExpr info <$> arbitrary <*> arbitrary <*> (scaleDown 5 arbitrary) <*> (scaleDown 5 arbitrary) <*> (if size > 5 then arbitrary else pure Nothing) <*> (if size > 5 then arbitrary else pure Nothing))
              ]
      shrink (ConstantExpr info c) = ConstantExpr info <$> shrink c
      shrink (FunctionExpr i n d e p f o) =
          [FunctionExpr i n' d' e' p' f' o' | (n', d', e', p', (f', o')) <- shrink (n, d, e, p, (f, o))]
      shrink _ = []  -- NB shrinking is only partially implemented; stubbing this
                     -- out to avoid compiler warnings about non-exhaustive
                     -- pattern matching
  