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 DeriveGeneric #-}
  {-# LANGUAGE DeriveFunctor #-}
  {-# LANGUAGE DeriveTraversable #-}
  {-# LANGUAGE StandaloneDeriving #-}
  {-# LANGUAGE ScopedTypeVariables #-}
  {-# LANGUAGE TypeFamilies #-}
  {-# LANGUAGE FlexibleContexts #-}
  {-# LANGUAGE UndecidableInstances #-}
  {-# LANGUAGE RecordWildCards #-}
  
  module Database.Sql.Hive.Type where
  
  import Database.Sql.Type hiding (insertValues, insertInfo)
  import Database.Sql.Position
  import Database.Sql.Info
  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.Scope
  import Database.Sql.Util.Schema
  import Database.Sql.Util.Tables
  
  import Control.Monad.Identity
  import Control.Monad.Writer (tell)
  
  import Data.Aeson
  import qualified Data.Map  as M
  import qualified Data.Set  as S
  import Data.Text.Lazy (Text)
  import qualified Data.Text.Lazy.Encoding as TL
  import           Data.ByteString.Lazy (ByteString)
  import qualified Data.ByteString.Lazy as BL
  import Data.Proxy (Proxy (..))
  
  import GHC.Generics (Generic)
  import Data.Data (Data)
  
  
  data Hive
  
  deriving instance Data Hive
  
  dialectProxy :: Proxy Hive
  dialectProxy = Proxy
  
  instance Dialect Hive where
      type DialectCreateTableExtra Hive r = HiveCreateTableExtra r
  
      shouldCTEsShadowTables _ = False
  
      -- Nothing to resolve in the table extra
      resolveCreateTableExtra _ HiveCreateTableExtra{..} = pure HiveCreateTableExtra{..}
  
      getSelectScope _ fromColumns selectionAliases = SelectScope
          { bindForWhere = bindFromColumns fromColumns
          , bindForGroup = bindFromColumns fromColumns
          , bindForHaving = bindBothColumns fromColumns selectionAliases
          , bindForOrder = bindAliasedColumns selectionAliases
          , bindForNamedWindow = bindFromColumns fromColumns
          }
  
      areLcolumnsVisibleInLateralViews _ = False
  
  
  data HiveStatement r a = HiveStandardSqlStatement (Statement Hive r a)
                           | HiveUseStmt (Use a)
                           | HiveAnalyzeStmt (Analyze r a)
                           | HiveInsertDirectoryStmt (InsertDirectory r a)
                           | HiveTruncatePartitionStmt (TruncatePartition r a)
                           | HiveAlterTableSetLocationStmt (AlterTableSetLocation r a)
                           | HiveAlterPartitionSetLocationStmt (AlterPartitionSetLocation r a)
                           | HiveSetPropertyStmt (SetProperty a)
                           | HiveUnhandledStatement a
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (HiveStatement r a)
  deriving instance Generic (HiveStatement r a)
  deriving instance ConstrainSNames Eq r a => Eq (HiveStatement r a)
  deriving instance ConstrainSNames Show r a => Show (HiveStatement r a)
  deriving instance ConstrainSASNames Functor r => Functor (HiveStatement r)
  deriving instance ConstrainSASNames Foldable r => Foldable (HiveStatement r)
  deriving instance ConstrainSASNames Traversable r => Traversable (HiveStatement r)
  
  data SetProperty a = SetProperty (SetPropertyDetails a)
                       | PrintProperties a Text
                         deriving (Generic, Data, Eq, Show, Functor, Foldable, Traversable)
  
  data SetPropertyDetails a = SetPropertyDetails
      { setPropertyDetailsInfo :: a
      , setPropertyDetailsName :: Text
      , setPropertyDetailsValue :: Text
      } deriving (Generic, Data, Eq, Show, Functor, Foldable, Traversable)
  
  data HiveCreateTableExtra r a = HiveCreateTableExtra
      { hiveCreateTableExtraInfo :: a
      , hiveCreateTableExtraTableProperties :: Maybe (HiveMetadataProperties a)
      }
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (HiveCreateTableExtra r a)
  deriving instance Generic (HiveCreateTableExtra r a)
  deriving instance ConstrainSNames Eq r a => Eq (HiveCreateTableExtra r a)
  deriving instance ConstrainSNames Show r a => Show (HiveCreateTableExtra r a)
  deriving instance ConstrainSASNames Functor r => Functor (HiveCreateTableExtra r)
  deriving instance ConstrainSASNames Foldable r => Foldable (HiveCreateTableExtra r)
  deriving instance ConstrainSASNames Traversable r => Traversable (HiveCreateTableExtra r)
  
  data HiveMetadataProperties a = HiveMetadataProperties
      { hiveMetadataPropertiesInfo :: a
      , hiveMetadataPropertiesProperties :: [HiveMetadataProperty a]
      } deriving (Generic, Data, Eq, Show, Functor, Foldable, Traversable)
  
  data HiveMetadataProperty a = HiveMetadataProperty
      { hiveMetadataPropertyInfo :: a
      , hiveMetadataPropertyKey :: ByteString
      , hiveMetadataPropertyValue :: ByteString
      } deriving (Generic, Data, Eq, Show, Functor, Foldable, Traversable)
  
  -- Important terminology note:
  -- Hive "databases" are schemas.
  -- https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL#LanguageManualDDL-Create/Drop/Alter/UseDatabase
  data Use a = UseDatabase (UQSchemaName a)
             | UseDefault a
               deriving (Generic, Data, Eq, Show, Functor, Foldable, Traversable)
  
  
  data Analyze r a = Analyze
      { analyzeInfo :: a
      , analyzeTable :: TableName r a
      }
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (Analyze r a)
  deriving instance Generic (Analyze r a)
  deriving instance ConstrainSNames Eq r a => Eq (Analyze r a)
  deriving instance ConstrainSNames Show r a => Show (Analyze r a)
  deriving instance ConstrainSASNames Functor r => Functor (Analyze r)
  deriving instance ConstrainSASNames Foldable r => Foldable (Analyze r)
  deriving instance ConstrainSASNames Traversable r => Traversable (Analyze r)
  
  
  data InsertDirectory r a = InsertDirectory
      { insertDirectoryInfo :: a
      , insertDirectoryLocale :: InsertDirectoryLocale a
      , insertDirectoryPath :: Location a
      , insertDirectoryQuery :: Query r a
      }
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (InsertDirectory r a)
  deriving instance Generic (InsertDirectory r a)
  deriving instance ConstrainSNames Eq r a => Eq (InsertDirectory r a)
  deriving instance ConstrainSNames Show r a => Show (InsertDirectory r a)
  deriving instance ConstrainSASNames Functor r => Functor (InsertDirectory r)
  deriving instance ConstrainSASNames Foldable r => Foldable (InsertDirectory r)
  deriving instance ConstrainSASNames Traversable r => Traversable (InsertDirectory r)
  
  
  data Location a = HDFSPath a ByteString
        deriving (Generic, Data, Eq, Show, Functor, Foldable, Traversable)
  
  
  data InsertDirectoryLocale a = InsertDirectoryLocal a | InsertDirectoryHDFS
        deriving (Generic, Data, Eq, Show, Functor, Foldable, Traversable)
  
  data StaticPartitionSpecItem r a = StaticPartitionSpecItem a (ColumnRef r a) (Constant a)
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (StaticPartitionSpecItem r a)
  deriving instance Generic (StaticPartitionSpecItem r a)
  deriving instance ConstrainSNames Eq r a => Eq (StaticPartitionSpecItem r a)
  deriving instance ConstrainSNames Show r a => Show (StaticPartitionSpecItem r a)
  deriving instance ConstrainSASNames Functor r => Functor (StaticPartitionSpecItem r)
  deriving instance ConstrainSASNames Foldable r => Foldable (StaticPartitionSpecItem r)
  deriving instance ConstrainSASNames Traversable r => Traversable (StaticPartitionSpecItem r)
  
  data DynamicPartitionSpecItem r a = DynamicPartitionSpecItem a (ColumnRef r a)
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (DynamicPartitionSpecItem r a)
  deriving instance Generic (DynamicPartitionSpecItem r a)
  deriving instance ConstrainSNames Eq r a => Eq (DynamicPartitionSpecItem r a)
  deriving instance ConstrainSNames Show r a => Show (DynamicPartitionSpecItem r a)
  deriving instance ConstrainSASNames Functor r => Functor (DynamicPartitionSpecItem r)
  deriving instance ConstrainSASNames Foldable r => Foldable (DynamicPartitionSpecItem r)
  deriving instance ConstrainSASNames Traversable r => Traversable (DynamicPartitionSpecItem r)
  
  -- Note: We don't track anything at the partition level (for now), so
  -- we discard the info on which particular partition is being truncated.
  data TruncatePartition r a = TruncatePartition
      { truncatePartitionInfo :: a
      , truncatePartitionTruncate :: Truncate r a
      }
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (TruncatePartition r a)
  deriving instance Generic (TruncatePartition r a)
  deriving instance ConstrainSNames Eq r a => Eq (TruncatePartition r a)
  deriving instance ConstrainSNames Show r a => Show (TruncatePartition r a)
  deriving instance ConstrainSASNames Functor r => Functor (TruncatePartition r)
  deriving instance ConstrainSASNames Foldable r => Foldable (TruncatePartition r)
  deriving instance ConstrainSASNames Traversable r => Traversable (TruncatePartition r)
  
  data AlterTableSetLocation r a = AlterTableSetLocation
      { alterTableSetLocationInfo :: a
      , alterTableSetLocationTable :: TableName r a
      , alterTableSetLocationLocation :: Location a
      }
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (AlterTableSetLocation r a)
  deriving instance ConstrainSNames Eq r a => Eq (AlterTableSetLocation r a)
  deriving instance ConstrainSNames Show r a => Show (AlterTableSetLocation r a)
  deriving instance ConstrainSASNames Functor r => Functor (AlterTableSetLocation r)
  deriving instance ConstrainSASNames Foldable r => Foldable (AlterTableSetLocation r)
  deriving instance ConstrainSASNames Traversable r => Traversable (AlterTableSetLocation r)
  
  
  data AlterPartitionSetLocation r a = AlterPartitionSetLocation
      { alterPartitionSetLocationInfo :: a
      , alterPartitionSetLocationTable :: TableName r a
      , alterPartitionSetLocationPartition :: [StaticPartitionSpecItem r a]
      , alterPartitionSetLocationLocation :: Location a
      }
  
  deriving instance (ConstrainSNames Data r a, Data r) => Data (AlterPartitionSetLocation r a)
  deriving instance ConstrainSNames Eq r a => Eq (AlterPartitionSetLocation r a)
  deriving instance ConstrainSNames Show r a => Show (AlterPartitionSetLocation r a)
  deriving instance ConstrainSASNames Functor r => Functor (AlterPartitionSetLocation r)
  deriving instance ConstrainSASNames Foldable r => Foldable (AlterPartitionSetLocation r)
  deriving instance ConstrainSASNames Traversable r => Traversable (AlterPartitionSetLocation r)
  
  
  instance HasJoins (HiveStatement ResolvedNames a) where
      getJoins (HiveStandardSqlStatement stmt) = getJoins stmt
      getJoins (HiveInsertDirectoryStmt stmt) =
          getJoins (QueryStmt $ insertDirectoryQuery stmt)
      getJoins (HiveUseStmt _) = S.empty
      getJoins (HiveAnalyzeStmt _) = S.empty
      getJoins (HiveTruncatePartitionStmt _) = S.empty
      getJoins (HiveAlterTableSetLocationStmt _) = S.empty
      getJoins (HiveAlterPartitionSetLocationStmt _) = S.empty
      getJoins (HiveSetPropertyStmt _) = S.empty
      getJoins (HiveUnhandledStatement _) = S.empty
  
  instance HasTableLineage (HiveStatement ResolvedNames a) where
      getTableLineage (HiveStandardSqlStatement stmt) = tableLineage stmt
      getTableLineage (HiveUseStmt _) = M.empty
      getTableLineage (HiveAnalyzeStmt _) = M.empty
      getTableLineage (HiveInsertDirectoryStmt _) = M.empty
      getTableLineage (HiveTruncatePartitionStmt (TruncatePartition _ (Truncate _ (RTableName t _)))) =
          let table = mkFQTN t
           in M.singleton table $ S.singleton table
      getTableLineage (HiveAlterTableSetLocationStmt _) = M.empty
      getTableLineage (HiveAlterPartitionSetLocationStmt _) = M.empty
      getTableLineage (HiveSetPropertyStmt _) = M.empty
      getTableLineage (HiveUnhandledStatement _) = M.empty
  
  instance HasColumnLineage (HiveStatement ResolvedNames Range) where
      getColumnLineage (HiveStandardSqlStatement stmt) = columnLineage stmt
      getColumnLineage (HiveUseStmt _) = returnNothing M.empty
      getColumnLineage (HiveAnalyzeStmt _) = returnNothing M.empty
      getColumnLineage (HiveInsertDirectoryStmt _) = returnNothing M.empty
      getColumnLineage (HiveTruncatePartitionStmt (TruncatePartition _ (Truncate _ (RTableName t SchemaMember{..})))) =
          returnNothing
              $ M.insert (Left $ fqtnToFQTN t) (singleTableSet (getInfo t) $ fqtnToFQTN t)
                  $ M.fromList $ map ((\ fqcn -> (Right fqcn, singleColumnSet (getInfo t) fqcn)) . fqcnToFQCN . qualifyColumnName t) columnsList
      getColumnLineage (HiveAlterTableSetLocationStmt _) = returnNothing M.empty
      getColumnLineage (HiveAlterPartitionSetLocationStmt _) = returnNothing M.empty
      getColumnLineage (HiveSetPropertyStmt _) = returnNothing M.empty
      getColumnLineage (HiveUnhandledStatement _) = returnNothing M.empty
  
  resolveHiveStatement :: HiveStatement RawNames a -> Resolver (HiveStatement ResolvedNames) a
  resolveHiveStatement (HiveStandardSqlStatement stmt) = HiveStandardSqlStatement <$> resolveStatement stmt
  resolveHiveStatement (HiveUseStmt stmt) = pure $ HiveUseStmt stmt
  resolveHiveStatement (HiveAnalyzeStmt stmt) = HiveAnalyzeStmt <$> resolveAnalyze stmt
  resolveHiveStatement (HiveInsertDirectoryStmt stmt) = HiveInsertDirectoryStmt <$> resolveInsertDirectory stmt
  resolveHiveStatement (HiveTruncatePartitionStmt stmt) = HiveTruncatePartitionStmt <$> resolveTruncatePartition stmt
  resolveHiveStatement (HiveAlterTableSetLocationStmt stmt) = HiveAlterTableSetLocationStmt <$> resolveAlterTableSetLocation stmt
  resolveHiveStatement (HiveAlterPartitionSetLocationStmt stmt) = HiveAlterPartitionSetLocationStmt <$> resolveAlterPartitionSetLocation stmt
  resolveHiveStatement (HiveSetPropertyStmt stmt) = pure $ HiveSetPropertyStmt stmt
  resolveHiveStatement (HiveUnhandledStatement stmt) = pure $ HiveUnhandledStatement stmt
  
  resolveAnalyze :: Analyze RawNames a -> Resolver (Analyze ResolvedNames) a
  resolveAnalyze Analyze{..} = do
      analyzeTable' <- resolveTableName analyzeTable
      pure Analyze
          { analyzeTable = analyzeTable'
          , ..
          }
  
  resolveInsertDirectory :: InsertDirectory RawNames a -> Resolver (InsertDirectory ResolvedNames) a
  resolveInsertDirectory InsertDirectory{..} = do
      insertDirectoryQuery' <- resolveQuery insertDirectoryQuery
      pure InsertDirectory
          { insertDirectoryQuery = insertDirectoryQuery'
          , ..
          }
  
  resolveTruncatePartition :: TruncatePartition RawNames a -> Resolver (TruncatePartition ResolvedNames) a
  resolveTruncatePartition TruncatePartition{..} = do
      truncatePartitionTruncate' <- resolveTruncate truncatePartitionTruncate
      pure TruncatePartition
          { truncatePartitionTruncate = truncatePartitionTruncate'
          , ..
          }
  
  resolveAlterTableSetLocation :: AlterTableSetLocation RawNames a -> Resolver (AlterTableSetLocation ResolvedNames) a
  resolveAlterTableSetLocation AlterTableSetLocation{..} = do
      alterTableSetLocationTable' <- resolveTableName alterTableSetLocationTable
      pure AlterTableSetLocation
          { alterTableSetLocationTable = alterTableSetLocationTable'
          , ..
          }
  
  resolveAlterPartitionSetLocation :: AlterPartitionSetLocation RawNames a -> Resolver (AlterPartitionSetLocation ResolvedNames) a
  resolveAlterPartitionSetLocation AlterPartitionSetLocation{..} = do
      alterPartitionSetLocationTable'@(RTableName fqtn table@SchemaMember{..}) <- resolveTableName alterPartitionSetLocationTable
      let tableInfo = tableNameInfo fqtn
          columnSet =
              [ ( Just $ RTableRef fqtn table
                , map (RColumnRef . (const tableInfo <$>) . qualifyColumnName fqtn) columnsList
                )
              ]
      alterPartitionSetLocationPartition' <- bindColumns columnSet
          $ forM alterPartitionSetLocationPartition
              $ \ (StaticPartitionSpecItem info column constant) -> do
                  column' <- resolveColumnName column
                  pure $ StaticPartitionSpecItem info column' constant
      pure AlterPartitionSetLocation
          { alterPartitionSetLocationTable = alterPartitionSetLocationTable'
          , alterPartitionSetLocationPartition = alterPartitionSetLocationPartition'
          , ..
          }
  
  instance HasSchemaChange (HiveStatement ResolvedNames a) where
      getSchemaChange (HiveStandardSqlStatement stmt) = getSchemaChange stmt
      getSchemaChange (HiveUseStmt (UseDefault _)) = [UsePath [QSchemaName () None "default" NormalSchema]]
      getSchemaChange (HiveUseStmt (UseDatabase schema)) = [UsePath [void schema]]
      getSchemaChange (HiveAnalyzeStmt _) = []
      getSchemaChange (HiveInsertDirectoryStmt _) = []
      getSchemaChange (HiveTruncatePartitionStmt _) = []
      getSchemaChange (HiveAlterTableSetLocationStmt _) = []  -- In fact, it may very well have a schema change but
                                                              -- we can't know without hitting the metastore.
      getSchemaChange (HiveAlterPartitionSetLocationStmt _) = []
      getSchemaChange (HiveSetPropertyStmt _) = []
      getSchemaChange (HiveUnhandledStatement _) = []
  
  instance (ConstrainSNames ToJSON r a, ToJSON a) => ToJSON (HiveStatement r a) where
      toJSON (HiveStandardSqlStatement stmt) = toJSON stmt
      toJSON (HiveInsertDirectoryStmt stmt) = object
          [ "tag" .= String "InsertDirectoryStmt"
          , "target" .= stmt
          ]
      toJSON (HiveUseStmt stmt) = object
          [ "tag" .= String "UseStmt"
          , "target" .= stmt
          ]
      toJSON (HiveAnalyzeStmt stmt) = object
          [ "tag" .= String "AnalyzeStmt"
          , "target" .= stmt
          ]
      toJSON (HiveTruncatePartitionStmt truncatePartition) = object
          [ "tag" .= String "HiveTruncatePartitionStmt"
          , "statement" .= truncatePartition
          ]
      toJSON (HiveAlterTableSetLocationStmt alterTableSetLocation) = object
          [ "tag" .= String "HiveAlterTableSetLocationStmt"
          , "statement" .= alterTableSetLocation
          ]
      toJSON (HiveAlterPartitionSetLocationStmt alterPartitionSetLocation) = object
          [ "tag" .= String "HiveAlterPartitionSetLocationStmt"
          , "statement" .= alterPartitionSetLocation
          ]
      toJSON (HiveSetPropertyStmt stmt) = object
          [ "tag" .= String "HiveSetPropertyStmt"
          , "info" .= stmt
          ]
      toJSON (HiveUnhandledStatement info) = object
          [ "tag" .= String "HiveUnhandledStatement"
          , "info" .= info
          ]
  
  typeExample :: ()
  typeExample = const () $ toJSON (undefined :: HiveStatement ResolvedNames Range)
  
  instance (ConstrainSNames ToJSON r a, ToJSON a) => ToJSON (HiveCreateTableExtra r a) where
      toJSON HiveCreateTableExtra{..} = object
          [ "tag" .= String "HiveCreateTableExtra"
          , "info" .= hiveCreateTableExtraInfo
          , "properties" .= hiveCreateTableExtraTableProperties
          ]
  
  instance ToJSON a => ToJSON (HiveMetadataProperties a) where
      toJSON HiveMetadataProperties{..} = object
          [ "tag" .= String "HiveMetadataProperties"
          , "info" .= hiveMetadataPropertiesInfo
          , "properties" .= hiveMetadataPropertiesProperties
          ]
  
  instance ToJSON a => ToJSON (HiveMetadataProperty a) where
      toJSON HiveMetadataProperty{..} = object
          [ "tag" .= String "MetadataProperty"
          , "info" .= hiveMetadataPropertyInfo
          , "key" .= bsToJSON hiveMetadataPropertyKey
          , "value" .= bsToJSON hiveMetadataPropertyValue
          ]
  
  bsToJSON :: ByteString -> Value
  bsToJSON bs = case TL.decodeUtf8' bs of
      Left _ -> toJSON $ BL.unpack bs
      Right str -> toJSON str
  
  instance (ConstrainSNames ToJSON r a, ToJSON a) => ToJSON (InsertDirectory r a) where
      toJSON stmt = object
          [ "tag" .= String "InsertDirectory"
          , "info" .= insertDirectoryInfo stmt
          , "directory" .= insertDirectoryPath stmt
          , "locale" .= insertDirectoryLocale stmt
          , "values" .= insertDirectoryQuery stmt
          ]
  
  instance ToJSON a => ToJSON (Location a) where
      toJSON (HDFSPath _ p) = bsToJSON p
  
  instance ToJSON a => ToJSON (InsertDirectoryLocale a) where
      toJSON (InsertDirectoryLocal _) = String "Local"
      toJSON InsertDirectoryHDFS = String "HDFS"
  
  instance ToJSON a => ToJSON (Use a) where
      toJSON (UseDatabase dbn) = toJSON dbn
      toJSON (UseDefault _) = String "Default"
  
  instance ToJSON a => ToJSON (SetProperty a) where
      toJSON (SetProperty details) = toJSON details
      toJSON (PrintProperties info t) = object
          [ "tag" .= String "Set"
          , "info" .= info
          , "property" .= t
          ]
  
  instance ToJSON a => ToJSON (SetPropertyDetails a) where
      toJSON d@SetPropertyDetails{} = object
          [ "tag" .= String "Set"
          , "info" .= setPropertyDetailsInfo d
          , "property" .= setPropertyDetailsName d
          , "value" .= setPropertyDetailsValue d
          ]
  
  instance (ConstrainSNames ToJSON r a, ToJSON a) => ToJSON (Analyze r a) where
      toJSON (Analyze info tbn) = object
          [ "tag" .= String "Analyze"
          , "info" .= info
          , "table" .= toJSON tbn
          ]
  
  instance (ConstrainSNames ToJSON r a, ToJSON a) => ToJSON (TruncatePartition r a) where
      toJSON (TruncatePartition info truncate') = object
          [ "tag" .= String "TruncatePartition"
          , "info" .= info
          , "truncate" .= truncate'
          ]
  
  instance HasInfo (TruncatePartition r a) where
      type Info (TruncatePartition r a) = a
      getInfo (TruncatePartition info _) = info
  
  instance HasInfo (Location a) where
      type Info (Location a) = a
      getInfo (HDFSPath info _) = info
  
  instance (ConstrainSNames ToJSON r a, ToJSON a) => ToJSON (AlterTableSetLocation r a) where
      toJSON (AlterTableSetLocation info table location) = object
          [ "tag" .= String "AlterTableSetLocation"
          , "info" .= info
          , "table" .= table
          , "location" .= location
          ]
  
  instance (ConstrainSNames ToJSON r a, ToJSON a) => ToJSON (AlterPartitionSetLocation r a) where
      toJSON (AlterPartitionSetLocation info table items location) = object
          [ "tag" .= String "AlterPartitionSetLocation"
          , "info" .= info
          , "table" .= table
          , "items" .= items
          , "location" .= location
          ]
  
  instance (ConstrainSNames ToJSON r a, ToJSON a) => ToJSON (StaticPartitionSpecItem r a) where
      toJSON (StaticPartitionSpecItem info column constant) = object
          [ "tag" .= String "StaticPartitionSpecItem"
          , "info" .= info
          , "column" .= column
          , "constant" .= constant
          ]
  
  
  instance HasTables (HiveStatement ResolvedNames a) where
    goTables (HiveStandardSqlStatement s) = goTables s
    goTables (HiveUseStmt _) = return ()
    goTables (HiveAnalyzeStmt _) = return ()
    goTables (HiveInsertDirectoryStmt s) = goTables s
    goTables (HiveTruncatePartitionStmt s) = goTables s
    goTables (HiveAlterTableSetLocationStmt s) = goTables s
    goTables (HiveAlterPartitionSetLocationStmt (AlterPartitionSetLocation _ (RTableName fqtn _) _ _)) = tell $ S.singleton $ TableUse WriteMeta $ fqtnToFQTN fqtn
    goTables (HiveSetPropertyStmt _) = return ()
    goTables (HiveUnhandledStatement _) = return ()
  
  instance HasTables (InsertDirectory ResolvedNames a) where
    goTables InsertDirectory{..} = goTables insertDirectoryQuery
  
  instance HasTables (TruncatePartition ResolvedNames a) where
    goTables (TruncatePartition _ s) = goTables s
  
  instance HasTables (AlterTableSetLocation ResolvedNames a) where
    goTables (AlterTableSetLocation _ table _) = do
        goTables table
  
  
  instance HasColumns (HiveStatement ResolvedNames a) where
    goColumns (HiveStandardSqlStatement s) = goColumns s
    goColumns (HiveUseStmt _) = return ()
    goColumns (HiveAnalyzeStmt _) = return ()
    goColumns (HiveInsertDirectoryStmt s) = goColumns s
    goColumns (HiveTruncatePartitionStmt _) = return ()
    goColumns (HiveAlterTableSetLocationStmt _) = return ()
    goColumns (HiveAlterPartitionSetLocationStmt (AlterPartitionSetLocation _ _ items _)) =
        forM_ items $ \ (StaticPartitionSpecItem _ column _) ->
            tell [clauseObservation (void column) "PARTITION"]
    goColumns (HiveSetPropertyStmt _) = return ()
    goColumns (HiveUnhandledStatement _) = return ()
  
  instance HasColumns (InsertDirectory ResolvedNames a) where
    goColumns InsertDirectory{..} = goColumns insertDirectoryQuery
  