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 DeriveDataTypeable #-}
  {-# LANGUAGE DeriveFunctor #-}
  {-# LANGUAGE DeriveGeneric #-}
  {-# LANGUAGE DeriveTraversable #-}
  {-# LANGUAGE StandaloneDeriving #-}
  {-# LANGUAGE FlexibleContexts #-}
  {-# LANGUAGE UndecidableInstances #-}
  
  module Database.Sql.Presto.Type where
  
  import Database.Sql.Type
  import Database.Sql.Position (Range)
  import Database.Sql.Util.Columns
  import Database.Sql.Util.Joins
  import Database.Sql.Util.Lineage.Table
  import Database.Sql.Util.Lineage.ColumnPlus
  import Database.Sql.Util.Schema
  import Database.Sql.Util.Scope
  import Database.Sql.Util.Tables
  
  import Data.Aeson as JSON
  import Data.Proxy (Proxy (..))
  
  import qualified Data.Set as S
  import qualified Data.Map as M
  
  import Data.Data (Data)
  import GHC.Generics (Generic)
  
  data Presto
  
  deriving instance Data Presto
  
  dialectProxy :: Proxy Presto
  dialectProxy = Proxy
  
  instance Dialect Presto where
  
      shouldCTEsShadowTables _ = True
  
      resolveCreateTableExtra _ Unused = pure Unused
  
      getSelectScope _ fromColumns selectionAliases = SelectScope
          { bindForWhere = bindFromColumns fromColumns
          , bindForGroup = bindFromColumns fromColumns
          , bindForHaving = bindFromColumns fromColumns
          , bindForOrder = bindBothColumns fromColumns selectionAliases
          , bindForNamedWindow = bindColumns [] -- Presto doesn't have this language feature
          }
  
      areLcolumnsVisibleInLateralViews _ = True
  
  data PrestoStatement r a = PrestoStandardSqlStatement (Statement Presto r a)
                             | PrestoUnhandledStatement a
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (PrestoStatement r a)
  deriving instance Generic (PrestoStatement r a)
  deriving instance ConstrainSNames Eq r a => Eq (PrestoStatement r a)
  deriving instance ConstrainSNames Show r a => Show (PrestoStatement r a)
  deriving instance ConstrainSASNames Functor r => Functor (PrestoStatement r)
  deriving instance ConstrainSASNames Foldable r => Foldable (PrestoStatement r)
  deriving instance ConstrainSASNames Traversable r => Traversable (PrestoStatement r)
  
  
  instance HasJoins (PrestoStatement ResolvedNames a) where
      getJoins (PrestoStandardSqlStatement stmt) = getJoins stmt
      getJoins (PrestoUnhandledStatement _) = S.empty
  
  instance HasTableLineage (PrestoStatement ResolvedNames a) where
      getTableLineage (PrestoStandardSqlStatement stmt) = tableLineage stmt
      getTableLineage (PrestoUnhandledStatement _) = M.empty
  
  instance HasColumnLineage (PrestoStatement ResolvedNames Range) where
      getColumnLineage (PrestoStandardSqlStatement stmt) = columnLineage stmt
      getColumnLineage (PrestoUnhandledStatement _) = returnNothing M.empty
  
  resolvePrestoStatement :: PrestoStatement RawNames a -> Resolver (PrestoStatement ResolvedNames) a
  resolvePrestoStatement (PrestoStandardSqlStatement stmt) =
      PrestoStandardSqlStatement <$> resolveStatement stmt
  resolvePrestoStatement (PrestoUnhandledStatement info) = pure $ PrestoUnhandledStatement info
  
  instance HasSchemaChange (PrestoStatement ResolvedNames a) where
      getSchemaChange (PrestoStandardSqlStatement stmt) = getSchemaChange stmt
      getSchemaChange (PrestoUnhandledStatement _) = []
  
  instance (ConstrainSNames ToJSON r a, ToJSON a) => ToJSON (PrestoStatement r a) where
      toJSON (PrestoStandardSqlStatement stmt) = toJSON stmt
      toJSON (PrestoUnhandledStatement info) = JSON.object
          [ "tag" .= JSON.String "PrestoUnhandledStatement"
          , "info" .= info
          ]
  
  typeExample :: ()
  typeExample = const () $ toJSON (undefined :: PrestoStatement ResolvedNames Range)
  
  instance HasTables (PrestoStatement ResolvedNames a) where
      goTables (PrestoStandardSqlStatement s) = goTables s
      goTables (PrestoUnhandledStatement _) = return ()
  
  instance HasColumns (PrestoStatement ResolvedNames a) where
      goColumns (PrestoStandardSqlStatement s) = goColumns s
      goColumns (PrestoUnhandledStatement _) = return ()
  