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 TypeFamilies #-}
  
  module Database.Sql.Vertica.Parser where
  
  import Database.Sql.Type
  import Database.Sql.Info
  import Database.Sql.Helpers
  import Database.Sql.Vertica.Type
  
  import Database.Sql.Vertica.Scanner
  import Database.Sql.Vertica.Parser.Internal
  import Database.Sql.Position
  
  import qualified Database.Sql.Vertica.Parser.Token as Tok
  import Database.Sql.Vertica.Parser.IngestionOptions
  import Database.Sql.Vertica.Parser.Shared
  
  import           Data.Char (isDigit)
  import           Data.Text.Lazy (Text)
  import qualified Data.Text.Lazy as TL
  import qualified Data.Text.Lazy.Encoding as TL
  import qualified Data.List as L
  
  import Data.Maybe (catMaybes, fromMaybe)
  import Data.Monoid (Endo (..))
  import Data.Semigroup (Option (..))
  import qualified Text.Parsec as P
  import           Text.Parsec ( chainl1, choice, many, many1
                               , option, optional, optionMaybe
                               , sepBy, sepBy1, try, (<|>), (<?>))
  
  import Control.Arrow (first)
  import Control.Monad (void, (>=>), when)
  
  
  import Data.Semigroup (Semigroup (..), sconcat)
  import Data.List.NonEmpty (NonEmpty ((:|)))
  import qualified Data.List.NonEmpty as NE (last, fromList)
  import Data.Foldable (fold)
  
  statementParser :: Parser (VerticaStatement RawNames Range)
  statementParser = do
      maybeStmt <- optionMaybe $ choice
          [ try $ VerticaStandardSqlStatement <$> statementP
          , do
                _ <- try $ P.lookAhead createProjectionPrefixP
                VerticaCreateProjectionStatement <$> createProjectionP
          , try $ VerticaMultipleRenameStatement <$> multipleRenameP
          , try $ VerticaSetSchemaStatement <$> setSchemaP
          , try $ VerticaUnhandledStatement <$> renameProjectionP
          , do
                _ <- try $ P.lookAhead alterResourcePoolPrefixP
                VerticaUnhandledStatement <$> alterResourcePoolP
          , do
                _ <- try $ P.lookAhead createResourcePoolPrefixP
                VerticaUnhandledStatement <$> createResourcePoolP
          , do
                _ <- try $ P.lookAhead dropResourcePoolPrefixP
                VerticaUnhandledStatement <$> dropResourcePoolP
          , do
                _ <- try $ P.lookAhead createFunctionPrefixP
                VerticaUnhandledStatement <$> createFunctionP
          , VerticaUnhandledStatement <$> alterTableAddConstraintP
          , VerticaUnhandledStatement <$> exportToStdoutP
          , do
                _ <- try $ P.lookAhead setSessionPrefixP
                VerticaUnhandledStatement <$> setSessionP
          , VerticaUnhandledStatement <$> setTimeZoneP
          , VerticaUnhandledStatement <$> connectP
          , VerticaUnhandledStatement <$> disconnectP
          , VerticaUnhandledStatement <$> createAccessPolicyP
          , VerticaUnhandledStatement <$> copyFromP
          , VerticaUnhandledStatement <$> showP
          , VerticaMergeStatement <$> mergeP
          ]
      case maybeStmt of
          Just stmt -> terminator >> return stmt
          Nothing -> VerticaStandardSqlStatement <$> emptyStatementP
    where
      terminator = (Tok.semicolonP <|> eof) -- normal statements may be terminated by `;` or eof
      emptyStatementP = EmptyStmt <$> Tok.semicolonP  -- but we don't allow eof here. `;` is the
      -- only way to write the empty statement, i.e. `` (empty string) is not allowed.
  
  
  -- | parse consumes a statement, or fails
  parse :: Text -> Either P.ParseError (VerticaStatement RawNames Range)
  parse = P.runParser statementParser 0 "-"  . tokenize
  
  -- | parseAll consumes all input as a single statement, or fails
  parseAll :: Text -> Either P.ParseError (VerticaStatement RawNames Range)
  parseAll = P.runParser (statementParser <* P.eof) 0 "-"  . tokenize
  
  -- | parseMany consumes multiple statements, or fails
  parseMany :: Text -> Either P.ParseError [VerticaStatement RawNames Range]
  parseMany = P.runParser (P.many1 statementParser) 0 "-"  . tokenize
  
  -- | parseManyAll consumes all input multiple statements, or fails
  parseManyAll :: Text -> Either P.ParseError [VerticaStatement RawNames Range]
  parseManyAll text = P.runParser (P.many1 statementParser <* P.eof) 0 "-"  . tokenize $ text
  
  -- | parseManyEithers consumes all input as multiple (statements or failures)
  -- it should never fail
  parseManyEithers :: Text -> Either P.ParseError [Either (Unparsed Range) (VerticaStatement RawNames Range)]
  parseManyEithers text = P.runParser parser 0 "-"  . tokenize $ text
    where
      parser = do
          statements <- P.many1 $ P.setState 0 >> choice
              [ try $ Right <$> statementParser
              , try $ Left <$> do
                  ss  <- many Tok.notSemicolonP
                  e <- Tok.semicolonP
                  pure $ case ss of
                      [] -> Unparsed e
                      s:_ -> Unparsed (s <> e)
              ]
  
          locs <- many Tok.notSemicolonP
          P.eof
          pure $ case locs of
              [] -> statements
              s:es -> statements ++ [Left $ Unparsed $ sconcat (s:|es)]
  
  optionBool :: Parser a -> Parser Bool
  optionBool p = option False $ p >> pure True
  
  statementP :: Parser (Statement Vertica RawNames Range)
  statementP = choice
      [ InsertStmt <$> insertP
      , DeleteStmt <$> deleteP
      , QueryStmt <$> queryP
      , explainP
      , TruncateStmt <$> truncateP
      , AlterTableStmt <$> alterTableP
      , do
            _ <- try $ P.lookAhead createSchemaPrefixP
            CreateSchemaStmt <$> createSchemaP
      , do
            _ <- try $ P.lookAhead createExternalTablePrefixP
            CreateTableStmt <$> createExternalTableP
      , do
            _ <- try $ P.lookAhead createViewPrefixP
            CreateViewStmt <$> createViewP
      , CreateTableStmt <$> createTableP
      , do
            _ <- try $ P.lookAhead dropViewPrefixP
            DropViewStmt <$> dropViewP
      , DropTableStmt <$> dropTableP
      , GrantStmt <$> grantP
      , RevokeStmt <$> revokeP
      , BeginStmt <$> beginP
      , CommitStmt <$> commitP
      , RollbackStmt <$> rollbackP
      ]
  
  oqColumnNameP :: Parser (OQColumnName Range)
  oqColumnNameP = (\ (c, r') -> QColumnName r' Nothing c) <$> Tok.columnNameP
  
  insertP :: Parser (Insert RawNames Range)
  insertP = do
      r <- Tok.insertP
      insertBehavior <- InsertAppend <$> Tok.intoP
  
      insertTable <- tableNameP
  
      insertColumns <- optionMaybe $ try $ do
          _ <- Tok.openP
          c:cs <- oqColumnNameP `sepBy1` Tok.commaP
          _ <- Tok.closeP
          pure (c :| cs)
  
      insertValues <- choice
          [ do
              s <- Tok.defaultP
              e <- Tok.valuesP
              pure $ InsertDefaultValues (s <> e)
  
          , do
              s <- Tok.valuesP
              _ <- Tok.openP
  
              x:xs <- defaultExprP `sepBy1` Tok.commaP
  
              e <- Tok.closeP
  
              let row = x :| xs
                  rows = row :| []  -- there can only be one
  
              pure $ InsertExprValues (s <> e) rows
          , InsertSelectValues <$> queryP
          ]
  
      let insertInfo = r <> getInfo insertValues
  
      pure Insert{..}
  
  defaultExprP :: Parser (DefaultExpr RawNames Range)
  defaultExprP = choice
      [ DefaultValue <$> Tok.defaultP
      , ExprValue <$> exprP
      ]
  
  
  deleteP :: Parser (Delete RawNames Range)
  deleteP = do
      r <- Tok.deleteP
  
      _ <- Tok.fromP
      table <- tableNameP
  
      maybeExpr <- optionMaybe $ do
          _ <- Tok.whereP
          exprP
  
      let r' = case maybeExpr of
            Nothing -> getInfo table
            Just expr -> getInfo expr
          info = r <> r'
  
      pure $ Delete info table maybeExpr
  
  
  truncateP :: Parser (Truncate RawNames Range)
  truncateP = do
      s <- Tok.truncateP
      _ <- Tok.tableP
      table <- tableNameP
  
      pure $ Truncate (s <> getInfo table) table
  
  
  querySelectP :: Parser (Query RawNames Range)
  querySelectP = do
          select <- selectP
          return $ QuerySelect (selectInfo select) select
  
  queryP :: Parser (Query RawNames Range)
  queryP = manyParensP $ do
      with <- option id withP
  
      query <- ((querySelectP <|> P.between Tok.openP Tok.closeP queryP) `chainl1` (exceptP <|> unionP))
                  `chainl1` intersectP
  
      order <- option id orderP
      limit <- option id limitP
      offset <- option id offsetP
  
      return $ with $ limit $ offset $ order $ query
    where
      exceptP = do
          r <- Tok.exceptP
          return $ QueryExcept r Unused
  
      unionP = do
          r <- Tok.unionP
          distinct <- option (Distinct True) distinctP
          return $ QueryUnion r distinct Unused
  
      intersectP = do
          r <- Tok.intersectP
          return $ QueryIntersect r Unused
  
      withP = do
          r <- Tok.withP
          withs <- cteP `sepBy1` Tok.commaP
  
          return $ \ query ->
              let r' = sconcat $ r :| getInfo query : map cteInfo withs
               in QueryWith r' withs query
  
      cteP = do
          (name, r) <- Tok.tableNameP
          alias <- makeTableAlias r name
  
          columns <- option []
              $ P.between Tok.openP Tok.closeP $ columnAliasP `sepBy1` Tok.commaP
  
          _ <- Tok.asP
  
          (query, r') <- do
              _ <- Tok.openP
              q <- queryP
              r' <- Tok.closeP
              return (q, r')
  
          return $ CTE (r <> r') alias columns query
  
      orderP = do
          (r, orders) <- orderTopLevelP
          return $ \ query -> QueryOrder (getInfo query <> r) orders query
  
      limitP = do
          r <- Tok.limitP
          choice
              [ Tok.numberP >>= \ (v, r') ->
                  let limit = Limit (r <> r') v
                   in return $ \ query -> QueryLimit (getInfo query <> r') limit query
  
              , Tok.nullP >> return id
              ]
  
      offsetP = do
          r <- Tok.offsetP
          Tok.numberP >>= \ (v, r') ->
              let offset = Offset (r <> r') v
               in return $ \ query -> QueryOffset (getInfo query <> r') offset query
  
  
  distinctP :: Parser Distinct
  distinctP = choice $
      [ Tok.allP >> return (Distinct False)
      , Tok.distinctP >> return (Distinct True)
      ]
  
  
  explainP :: Parser (Statement Vertica RawNames Range)
  explainP = do
      s <- Tok.explainP
      stmt <- choice
          [ InsertStmt <$> insertP
          , DeleteStmt <$> deleteP
          , QueryStmt <$> queryP
          ]
  
      pure $ ExplainStmt (s <> getInfo stmt) stmt
  
  
  columnAliasP :: Parser (ColumnAlias Range)
  columnAliasP = do
      (name, r) <- Tok.columnNameP
      makeColumnAlias r name
  
  
  alterTableP :: Parser (AlterTable RawNames Range)
  alterTableP = do
      s <- Tok.alterP
      _ <- Tok.tableP
      from <- tableNameP
      _ <- Tok.renameP
      _ <- Tok.toP
      to <- (\ uqtn -> uqtn { tableNameSchema = Nothing }) <$> unqualifiedTableNameP
  
      pure $ AlterTableRenameTable (s <> getInfo to) from to
  
  
  createSchemaPrefixP :: Parser Range
  createSchemaPrefixP = do
      s <- Tok.createP
      e <- Tok.schemaP
      return $ s <> e
  
  ifNotExistsP :: Parser (Maybe Range)
  ifNotExistsP = optionMaybe $ do
      s <- Tok.ifP
      _ <- Tok.notP
      e <- Tok.existsP
      pure $ s <> e
  
  ifExistsP :: Parser Range
  ifExistsP = do
      s <- Tok.ifP
      e <- Tok.existsP
      pure $ s <> e
  
  createSchemaP :: Parser (CreateSchema RawNames Range)
  createSchemaP = do
      s <- createSchemaPrefixP
      createSchemaIfNotExists <- ifNotExistsP
  
      (name, r) <- Tok.schemaNameP
      let createSchemaName = mkNormalSchema name r
  
      e <- option r (Tok.authorizationP >> snd <$> Tok.userNameP)
      let createSchemaInfo = s <> e
  
      return $ CreateSchema{..}
  
  
  createTableColumnsP :: Parser (TableDefinition Vertica RawNames Range)
  createTableColumnsP = do
      s <- Tok.openP
      c:cs <- columnOrConstraintP `sepBy1` Tok.commaP
      e <- Tok.closeP
      pure $ TableColumns (s <> e) (c:|cs)
    where
      columnOrConstraintP :: Parser (ColumnOrConstraint Vertica RawNames Range)
      columnOrConstraintP = choice
          [ try $ ColumnOrConstraintColumn <$> columnDefinitionP
          , ColumnOrConstraintConstraint <$> constraintDefinitionP
          ]
  
      columnDefinitionP = do
          (name, s) <- Tok.columnNameP
          columnDefinitionType <- dataTypeP
  
          updates <- many $ choice [ notNullUpdateP, nullUpdateP, defaultUpdateP ]
  
          let columnDefinitionInfo = s <> getInfo columnDefinitionType
              columnDefinitionExtra = Nothing  -- TODO
              -- set when applying updates
              columnDefinitionNull = Nothing
              columnDefinitionDefault = Nothing
              columnDefinitionName = QColumnName s None name
  
          foldr (>=>) pure updates ColumnDefinition{..}
  
      notNullUpdateP :: Parser (ColumnDefinition d r Range -> Parser (ColumnDefinition d r Range))
      notNullUpdateP = do
          r <- (<>) <$> Tok.notP <*> Tok.nullP
  
          pure $ \ d -> case columnDefinitionNull d of
              Nothing -> pure $ d { columnDefinitionNull = Just $ NotNull r }
              Just (Nullable _) -> fail "conflicting NULL/NOT NULL specifications on column"
              Just (NotNull _) -> pure d
  
      nullUpdateP :: Parser (ColumnDefinition d r Range -> Parser (ColumnDefinition d r Range))
      nullUpdateP = do
          r <- Tok.nullP
  
          pure $ \ d -> case columnDefinitionNull d of
              Nothing -> pure $ d { columnDefinitionNull = Just $ Nullable r }
              Just (NotNull _) -> fail "conflicting NULL/NOT NULL specifications on column"
              Just (Nullable _) -> pure d
  
      defaultUpdateP :: Parser (ColumnDefinition d RawNames Range -> Parser (ColumnDefinition d RawNames Range))
      defaultUpdateP = do
          _ <- Tok.defaultP
          expr <- exprP
  
          pure $ \ d -> case columnDefinitionDefault d of
              Nothing -> pure $ d { columnDefinitionDefault = Just expr }
              Just _ -> fail "multiple defaults for column"
  
      constraintDefinitionP :: Parser (ConstraintDefinition Range)
      constraintDefinitionP = ConstraintDefinition <$> tableConstraintP
  
  
  createExternalTablePrefixP :: Parser (Range, Externality Range)
  createExternalTablePrefixP = do
      s <- Tok.createP
      r <- Tok.externalP
      _ <- Tok.tableP
      return (s, External r)
  
  
  createExternalTableP :: Parser (CreateTable Vertica RawNames Range)
  createExternalTableP = do
      (s, createTableExternality) <- createExternalTablePrefixP
      let createTablePersistence = Persistent
      createTableIfNotExists <- ifNotExistsP
      createTableName <- tableNameP
  
      createTableDefinition <- createTableColumnsP -- TODO allow for column-name-list syntax
  
      _ <- optional $ do
              _ <- optional $ Tok.includeP <|> Tok.excludeP
              _ <- Tok.schemaP
              Tok.privilegesP
  
      _ <- Tok.asP
      e <- Tok.copyP
  
      e' <- consumeOrderedOptions e $
          [ ingestionColumnListP (getInfo <$> exprP)
          , ingestionColumnOptionP
          , fromP -- you need **either** a FROM or a SOURCE clause, but let's not be fussy
          , fileStorageFormatP
          ]
  
      e'' <- consumeUnorderedOptions e' $
          [ Tok.withP
          , abortOnErrorP
          , delimiterAsP
          , enclosedByP
          , Tok.enforceLengthP
          , errorToleranceP
          , escapeFormatP
          , exceptionsOnNodeP
          , fileFilterP
          , nullAsP
          , fileParserP
          , recordTerminatorP
          , rejectedDataOnNodeP
          , rejectMaxP
          , skipRecordsP
          , skipBytesP
          , fileSourceP
          , trailingNullColsP
          , trimByteP
          ]
  
      let createTableInfo = s <> e''
          createTableExtra = Nothing
  
      pure CreateTable{..}
  
    where
      stringP :: Parser Range
      stringP = snd <$> Tok.stringP
  
      fromP :: Parser Range
      fromP = do
          s <- Tok.fromP
          let fileP = do
                  r <- stringP
                  consumeOrderedOptions r [nodeLocationP, compressionP]
          rs <- fileP `sepBy1` Tok.commaP
          return $ s <> last rs
  
      nodeLocationP = choice $
          [ Tok.onP >> snd <$> Tok.nodeNameP
          , Tok.onP >> Tok.anyP >> Tok.nodeP
          ]
  
  createViewPrefixP :: Parser (Range, Maybe Range, Persistence Range)
  createViewPrefixP = do
      s <- Tok.createP
      ifNotExists <- optionMaybe $ do
          s' <- Tok.orP
          e' <- Tok.replaceP
          pure $ s' <> e'
      persistence <- option Persistent $ Temporary <$> do
          s' <- Tok.localP
          e' <- Tok.temporaryP
          pure $ s' <> e'
  
      e <- Tok.viewP
      pure (s <> e, ifNotExists, persistence)
  
  schemaPrivilegesP :: Parser Range
  schemaPrivilegesP = do
      s <- choice [ Tok.includeP, Tok.excludeP ]
      optional Tok.schemaP
      e <- Tok.privilegesP
      return $ s <> e
  
  createViewP :: Parser (CreateView RawNames Range)
  createViewP = do
      (s, createViewIfNotExists, createViewPersistence) <- createViewPrefixP
  
      createViewName <- tableNameP >>= \case
          QTableName info Nothing view ->
              case createViewPersistence of
                  Persistent -> pure $ QTableName info Nothing view
                  Temporary _ -> pure $ QTableName info (pure $ QSchemaName info Nothing "<session>" SessionSchema) view
          qualifiedTableName ->
              case createViewPersistence of
                  Persistent -> pure $ qualifiedTableName
                  Temporary _ -> fail $ "cannot specify schema on a local temporary view"
  
      createViewColumns <- optionMaybe $ do
          _ <- Tok.openP
          c:cs <- unqualifiedColumnNameP `sepBy1` Tok.commaP
          _ <- Tok.closeP
          return (c:|cs)
  
      case createViewPersistence of
          Persistent -> optional schemaPrivilegesP
          Temporary _ -> pure ()
  
      _ <- Tok.asP
      createViewQuery <- queryP
  
      let createViewInfo = s <> getInfo createViewQuery
      pure CreateView{..}
    where
      unqualifiedColumnNameP = do
          (name, r) <- Tok.columnNameP
          pure $ QColumnName r None name
  
  
  createTableP :: Parser (CreateTable Vertica RawNames Range)
  createTableP = do
      s <- Tok.createP
  
      (createTablePersistence, isLocal) <- option (Persistent, False) $ do
          isLocal <- option False $ choice
              [ Tok.localP >> pure True
              , Tok.globalP >> pure False
              ]
  
          createTablePersistence <- Temporary <$> Tok.temporaryP
          pure (createTablePersistence, isLocal)
  
      let createTableExternality = Internal
  
      _ <- Tok.tableP
  
      createTableIfNotExists <- ifNotExistsP
  
      createTableName <- tableNameP >>= \case
          QTableName info Nothing table ->
              if isLocal
               then pure $ QTableName info (pure $ QSchemaName info Nothing "<session>" SessionSchema) table
               else pure $ QTableName info (pure $ QSchemaName info Nothing "public" NormalSchema) table
          qualifiedTableName ->
              if isLocal
               then fail "cannot specify schema on a local temporary table"
               else pure $ qualifiedTableName
  
      let onCommitP = case createTablePersistence of
              Persistent -> pure ()
              Temporary _ -> do
                  -- TODO (T374141): do something with this
                  _ <- Tok.onP
                  _ <- Tok.commitP
                  _ <- Tok.deleteP <|> Tok.preserveP
                  void Tok.rowsP
  
      createTableDefinition <- choice
          [ createTableColumnsP <* optional onCommitP <* optional schemaPrivilegesP
          , try $ optional onCommitP *> optional schemaPrivilegesP *> createTableAsP
          , optional schemaPrivilegesP *> createTableLikeP
          ]
  
      createTableExtra <- tableInfoP
  
      case createTablePersistence of
          Persistent -> pure ()
          Temporary _ -> optional $ do
              _ <- Tok.noP
              void Tok.projectionP
  
      let e = maybe (getInfo createTableDefinition) getInfo createTableExtra
          createTableInfo = s <> e
  
      pure CreateTable{..}
  
    where
      columnListP :: Parser (NonEmpty (UQColumnName Range))
      columnListP = do
          _ <- Tok.openP
          c:cs <- (`sepBy1` Tok.commaP) $ do
              (name, r) <- Tok.columnNameP
              pure $ QColumnName r None name
          _ <- Tok.closeP
          pure (c:|cs)
  
      createTableLikeP = do
              s <- Tok.likeP
              table <- tableNameP
              e <- option (getInfo table) $ do
                  -- TODO - include projection info in createTableExtra
                  _ <- Tok.includingP <|> Tok.excludingP
                  Tok.projectionsP
  
              pure $ TableLike (s <> e) table
  
      createTableAsP = do
              s <- Tok.asP
              columns <- optionMaybe $ try columnListP
              query <- optionalParensP $ queryP
              pure $ TableAs (s <> getInfo query) columns query
  
      tableInfoP :: Parser (Maybe (TableInfo RawNames Range))
      tableInfoP = do
          mOrdering <- optionMaybe orderTopLevelP
          let tableInfoOrdering = snd <$> mOrdering
  
          let tableInfoEncoding :: Maybe (TableEncoding RawNames Range)
              tableInfoEncoding = Nothing -- TODO
  
          tableInfoSegmentation <- optionMaybe $ choice
              [ do
                  s <- Tok.unsegmentedP
                  choice
                      [ do
                          _ <- Tok.nodeP
                          node <- nodeNameP
                          let e = getInfo node
                          pure $ UnsegmentedOneNode (s <> e) node
                      , do
                          _ <- Tok.allP
                          e <- Tok.nodesP
                          pure $ UnsegmentedAllNodes (s <> e)
                      ]
              , do
                  s <- Tok.segmentedP
                  _ <- Tok.byP
                  expr <- exprP
                  list <- nodeListP
  
                  pure $ SegmentedBy (s <> getInfo list) expr list
              ]
  
          tableInfoKSafety <- optionMaybe $ do
              s <- Tok.ksafeP
              choice
                  [ do
                      (n, e) <- integerP
                      pure $ KSafety (s <> e) (Just n)
  
                  , pure $ KSafety s Nothing
                  ]
  
          tableInfoPartitioning <- optionMaybe $ do
              s <- Tok.partitionP
              _ <- Tok.byP
              expr <- exprP
              pure $ Partitioning (s <> getInfo expr) expr
  
          let infos = [ fst <$> mOrdering
                      , getInfo <$> tableInfoEncoding
                      , getInfo <$> tableInfoSegmentation
                      , getInfo <$> tableInfoKSafety
                      , getInfo <$> tableInfoPartitioning
                      ]
  
          case getOption $ mconcat $ map Option infos of
              Nothing -> pure Nothing
              Just tableInfoInfo -> pure $ Just TableInfo{..}
  
  
  dropViewPrefixP :: Parser Range
  dropViewPrefixP = do
      s <- Tok.dropP
      e <- Tok.viewP
      pure $ s <> e
  
  dropViewP :: Parser (DropView RawNames Range)
  dropViewP = do
      s <- dropViewPrefixP
      dropViewIfExists <- optionMaybe ifExistsP
      dropViewName <- tableNameP
  
      let dropViewInfo = s <> getInfo dropViewName
      pure DropView{..}
  
  dropTableP :: Parser (DropTable RawNames Range)
  dropTableP = do
      s <- Tok.dropP
      _ <- Tok.tableP
      dropTableIfExists <- optionMaybe ifExistsP
      (dropTableName:rest) <- tableNameP `sepBy1` Tok.commaP
      cascade <- optionMaybe Tok.cascadeP
  
      let dropTableNames = dropTableName :| rest
          dropTableInfo = s <> (fromMaybe (getInfo $ NE.last dropTableNames) cascade)
      pure DropTable{..}
  
  
  grantP :: Parser (Grant Range)
  grantP = do
      s <- Tok.grantP
      e <- many1 Tok.notSemicolonP
      return $ Grant (s <> (last e))
  
  revokeP :: Parser (Revoke Range)
  revokeP = do
      s <- Tok.revokeP
      e <- many1 Tok.notSemicolonP
      return $ Revoke (s <> (last e))
  
  
  beginP :: Parser Range
  beginP = do
      s <- choice [ do
                        s <- Tok.beginP
                        e <- option s (Tok.workP <|> Tok.transactionP)
                        return $ s <> e
                  , do
                        s <- Tok.startP
                        e <- Tok.transactionP
                        return $ s <> e
                  ]
      e <- consumeOrderedOptions s [isolationLevelP, transactionModeP]
      return $ s <> e
    where
      isolationLevelP :: Parser Range
      isolationLevelP = do
          s <- Tok.isolationP
          _ <- Tok.levelP
          e <- choice [ Tok.serializableP
                      , Tok.repeatableP >> Tok.readP
                      , Tok.readP >> (Tok.committedP <|> Tok.uncommittedP)
                      ]
          return $ s <> e
  
      transactionModeP :: Parser Range
      transactionModeP = do
          s <- Tok.readP
          e <- Tok.onlyP <|> Tok.writeP
          return $ s <> e
  
  commitP :: Parser Range
  commitP = do
      s <- Tok.commitP <|> Tok.endP
      e <- option s (Tok.workP <|> Tok.transactionP)
      return $ s <> e
  
  rollbackP :: Parser Range
  rollbackP = do
      s <- Tok.rollbackP <|> Tok.abortP
      e <- option s (Tok.workP <|> Tok.transactionP)
      return $ s <> e
  
  nodeListP :: Parser (NodeList Range)
  nodeListP = choice
      [ do
          s <- Tok.allP
          e <- Tok.nodesP
          offset <- optionMaybe nodeListOffsetP
  
          let e' = maybe e getInfo offset
  
          pure $ AllNodes (s <> e') offset
  
      , do
          s <- Tok.nodesP
          n:ns <- nodeNameP `sepBy1` Tok.commaP
          let e = getInfo $ last (n:ns)
          pure $ Nodes (s <> e) (n:|ns)
      ]
  
  nodeListOffsetP :: Parser (NodeListOffset Range)
  nodeListOffsetP = do
      s <- Tok.offsetP
      (n, e) <- integerP
      pure $ NodeListOffset (s <> e) n
  
  nodeNameP :: Parser (Node Range)
  nodeNameP = do
      (node, e) <- Tok.nodeNameP
      pure $ Node e node
  
  integerP :: Parser (Int, Range)
  integerP = do
      (n, e) <- Tok.numberP
      case reads $ TL.unpack n of
          [(n', "")] -> pure (n', e)
          _ -> fail $ unwords ["unable to parse", show n, "as integer"]
  
  
  selectP :: Parser (Select RawNames Range)
  selectP = do
      r <- Tok.selectP
  
      selectDistinct <- option notDistinct distinctP
  
      selectCols <- do
          selections <- selectionP `sepBy1` Tok.commaP
          let r' = foldl1 (<>) $ map getInfo selections
          return $ SelectColumns r' selections
  
      selectFrom <- optionMaybe fromP
      selectWhere <- optionMaybe whereP
      selectTimeseries <- optionMaybe timeseriesP
      selectGroup <- optionMaybe groupP
      selectHaving <- optionMaybe havingP
      selectNamedWindow <- optionMaybe namedWindowP
  
      let (Just selectInfo) = sconcat $ Just r :|
              [ Just $ getInfo selectCols
              , getInfo <$> selectFrom
              , getInfo <$> selectWhere
              , getInfo <$> selectTimeseries
              , getInfo <$> selectGroup
              , getInfo <$> selectHaving
              , getInfo <$> selectNamedWindow
              ]
      return Select{..}
  
    where
      fromP = do
          r <- Tok.fromP
          tablishes <- tablishP `sepBy1` Tok.commaP
  
          let r' = foldl (<>) r $ fmap getInfo tablishes
          return $ SelectFrom r' tablishes
  
      whereP = do
          r <- Tok.whereP
          condition <- exprP
          return $ SelectWhere (r <> getInfo condition) condition
  
      timeseriesP = do
          s <- Tok.timeseriesP
  
          selectTimeseriesSliceName <- columnAliasP
  
          _ <- Tok.asP
  
          selectTimeseriesInterval <- do
              (c, r) <- Tok.stringP
              pure $ StringConstant r c
  
          _ <- Tok.overP
          _ <- Tok.openP
          selectTimeseriesPartition <- optionMaybe partitionP
          selectTimeseriesOrder <- do
              _ <- Tok.orderP
              _ <- Tok.byP
              exprP
          e <- Tok.closeP
  
          let selectTimeseriesInfo = s <> e
          pure $ SelectTimeseries {..}
  
      toGroupingElement :: PositionOrExpr RawNames Range -> GroupingElement RawNames Range
      toGroupingElement posOrExpr = GroupingElementExpr (getInfo posOrExpr) posOrExpr
  
      groupP = do
          r <- Tok.groupP
          _ <- Tok.byP
          exprs <- exprP `sepBy1` Tok.commaP
  
          let selectGroupGroupingElements = map (toGroupingElement . handlePositionalReferences) exprs
              selectGroupInfo = foldl (<>) r $ fmap getInfo selectGroupGroupingElements
          return SelectGroup{..}
  
      havingP = do
          r <- Tok.havingP
          conditions <- exprP `sepBy1` Tok.commaP
  
          let r' = foldl (<>) r $ fmap getInfo conditions
          return $ SelectHaving r' conditions
  
      namedWindowP =
        do
          r <- Tok.windowP
          windows <- (flip sepBy1) Tok.commaP $ do
            name <- windowNameP
            _ <- Tok.asP
            _ <- Tok.openP
            window <- choice
                [ do
                    partition@(Just p) <- Just <$> partitionP
                    order <- option [] orderInWindowClauseP
                    let orderInfos = map getInfo order -- better way?
                        info = L.foldl' (<>) (getInfo p) orderInfos
                    return $ Left $ WindowExpr info partition order Nothing
                , do
                    inherit <- windowNameP
                    order <- option [] orderInWindowClauseP
                    let orderInfo = map getInfo order -- better way?
                        info = L.foldl' (<>) (getInfo inherit) orderInfo
                    return $ Right $ PartialWindowExpr info inherit Nothing order Nothing
                ]
            e <- Tok.closeP
            let info = getInfo name <> e
            return $ case window of
              Left w -> NamedWindowExpr info name w
              Right pw -> NamedPartialWindowExpr info name pw
          let info = L.foldl' (<>) r $ fmap getInfo windows
          return $ SelectNamedWindow info windows
  
  handlePositionalReferences :: Expr RawNames Range -> PositionOrExpr RawNames Range
  handlePositionalReferences e = case e of
      ConstantExpr _ (NumericConstant _ n) | TL.all isDigit n -> PositionOrExprPosition (getInfo e) (read $ TL.unpack n) Unused
      _ -> PositionOrExprExpr e
  
  
  selectStarP :: Parser (Selection RawNames Range)
  selectStarP = choice
      [ do
          r <- Tok.starP
          return $ SelectStar r Nothing Unused
  
      , try $ do
          (t, r) <- Tok.tableNameP
          _ <- Tok.dotP
          r' <- Tok.starP
  
          return $ SelectStar (r <> r') (Just $ QTableName r Nothing t) Unused
  
      , try $ do
          (s, t, r, r') <- qualifiedTableNameP
          _ <- Tok.dotP
          r'' <- Tok.starP
  
          return $ SelectStar (r <> r'')
              (Just $ QTableName r' (Just $ mkNormalSchema s r) t) Unused
      ]
  
  
  selectionP :: Parser (Selection RawNames Range)
  selectionP = try selectStarP <|> do
      expr <- exprP
      alias <- aliasP expr
  
      return $ SelectExpr (getInfo alias <> getInfo expr) [alias] expr
  
  makeColumnAlias :: Range -> Text -> Parser (ColumnAlias Range)
  makeColumnAlias r alias = ColumnAlias r alias . ColumnAliasId <$> getNextCounter
  
  makeTableAlias :: Range -> Text -> Parser (TableAlias Range)
  makeTableAlias r alias = TableAlias r alias . TableAliasId <$> getNextCounter
  
  makeDummyAlias :: Range -> Parser (ColumnAlias Range)
  makeDummyAlias r = makeColumnAlias r "?column?"
  
  
  makeExprAlias :: Expr RawNames Range -> Parser (ColumnAlias Range)
  makeExprAlias (BinOpExpr info _ _ _) = makeDummyAlias info
  makeExprAlias (UnOpExpr info _ _) = makeDummyAlias info
  makeExprAlias (LikeExpr info _ _ _ _) = makeDummyAlias info
  makeExprAlias (CaseExpr info _ _) = makeDummyAlias info
  makeExprAlias (ColumnExpr info (QColumnName _ _ name)) = makeColumnAlias info name
  makeExprAlias (ConstantExpr info _) = makeDummyAlias info
  makeExprAlias (InListExpr info _ _) = makeDummyAlias info
  makeExprAlias (InSubqueryExpr info _ _) = makeDummyAlias info
  makeExprAlias (BetweenExpr info _ _ _) = makeDummyAlias info
  makeExprAlias (OverlapsExpr info _ _) = makeDummyAlias info
  makeExprAlias (AtTimeZoneExpr info _ _) = makeColumnAlias info "timezone" -- because reasons
  
  -- function expressions get the name of the function
  makeExprAlias (FunctionExpr info (QFunctionName _ _ name) _ _ _ _ _) = makeColumnAlias info name
  makeExprAlias (SubqueryExpr info _) = makeDummyAlias info
  makeExprAlias (ArrayExpr info _) = makeDummyAlias info  -- might actually be "array", but I'm not sure how to check
  makeExprAlias (ExistsExpr info _) = makeDummyAlias info
  makeExprAlias (FieldAccessExpr _ _ _) = fail "Unsupported struct access in Vertica: unused datatype in this dialect"
  makeExprAlias (ArrayAccessExpr _ _ _) = fail "Unsupported array access in Vertica: unused datatype in this dialect"
  makeExprAlias (TypeCastExpr _ _ expr _) = makeExprAlias expr
  makeExprAlias (VariableSubstitutionExpr _) = fail "Unsupported variable substitution in Vertica: unused datatype in this dialect"
  
  
  aliasP :: Expr RawNames Range -> Parser (ColumnAlias Range)
  aliasP expr = choice
      [ try $ do
          optional Tok.asP
          (name, r) <- choice
              [ Tok.columnNameP
              , first TL.decodeUtf8 <$> Tok.stringP
              ]
          makeColumnAlias r name
  
      , do
          _ <- Tok.asP
          _ <- P.between Tok.openP Tok.closeP $ Tok.columnNameP `sepBy1` Tok.commaP
          makeExprAlias expr
  
      , makeExprAlias expr
      ]
  
  
  exprP :: Parser (Expr RawNames Range)
  exprP = orExprP
  
  parenExprP :: Parser (Expr RawNames Range)
  parenExprP = P.between Tok.openP Tok.closeP $ choice
      [ try subqueryExprP
      , exprP
      ]
  
  subqueryExprP :: Parser (Expr RawNames Range)
  subqueryExprP = do
      query <- queryP
      return $ SubqueryExpr (getInfo query) query
  
  caseExprP :: Parser (Expr RawNames Range)
  caseExprP = do
      r <- Tok.caseP
      whens <- choice
          [ P.many1 $ do
              _ <- Tok.whenP
              condition <- exprP
              _ <- Tok.thenP
              result <- exprP
              return (condition, result)
  
          , do
              expr <- exprP
              P.many1 $ do
                  whenr <- Tok.whenP
                  nullseq <- optionMaybe Tok.nullsequalP
  
                  condition <- case nullseq of
                      Nothing -> BinOpExpr whenr "=" expr <$> exprP
                      Just nullseqr -> BinOpExpr (whenr <> nullseqr) "<=>" expr <$> exprP
  
                  _ <- Tok.thenP
                  result <- exprP
                  return (condition, result)
          ]
  
      melse <- optionMaybe $ do
          _ <- Tok.elseP
          exprP
  
      r' <- Tok.endP
  
      return $ CaseExpr (r <> r') whens melse
  
  
  fieldTypeP :: Parser (Expr RawNames Range)
  fieldTypeP = do
      (ftype, r) <- Tok.fieldTypeP
      return $ ConstantExpr r $ StringConstant r $ TL.encodeUtf8 ftype
  
  functionExprP :: Parser (Expr RawNames Range)
  functionExprP = choice
      [ castFuncP
      , dateDiffFuncP
      , extractFuncP
      , try regularFuncP
      , bareFuncP
      ]
    where
      castFuncP = do
          r <- Tok.castP
          _ <- Tok.openP
          e <- exprP
          _ <- Tok.asP
          t <- choice
              [ try $ do
                  i <- Tok.intervalP
                  (unit, u) <- Tok.datePartP
                  pure $ PrimitiveDataType (i <> u) ("INTERVAL " <> TL.toUpper unit) []
              , dataTypeP
              ]
          r' <- Tok.closeP
  
          return $ TypeCastExpr (r <> r') CastFailureError e t
  
      dateDiffFuncP = do
          r <- Tok.dateDiffP
          _ <- Tok.openP
          datepart <- choice
              [ do
                  _ <- Tok.openP
                  expr <- exprP
                  _ <- Tok.closeP
  
                  pure expr
  
              , do
                  (string, r') <- Tok.stringP
                  pure $ ConstantExpr r' $ StringConstant r' string
  
              , do
                  (string, r') <- Tok.datePartP
                  pure $ ConstantExpr r' $ StringConstant r' $ TL.encodeUtf8 string
              ]
          _ <- Tok.commaP
          startExp <- exprP
          _ <- Tok.commaP
          endExp <- exprP
          r' <- Tok.closeP
  
          return $ FunctionExpr (r <> r') (QFunctionName r Nothing "datediff") notDistinct [datepart, startExp, endExp] [] Nothing Nothing
  
      extractFuncP = do
          r <- Tok.extractP
          _ <- Tok.openP
          ftype <- fieldTypeP
          _ <- Tok.fromP
          expr <- exprP
          r' <- Tok.closeP
  
          return $ FunctionExpr (r <> r') (QFunctionName r Nothing "extract") notDistinct [ftype, expr] [] Nothing Nothing
  
      regularFuncP = do
          name <- choice
              [ try $ do
                  (s, r) <- Tok.schemaNameP
                  _ <- Tok.dotP
                  (f, r') <- Tok.functionNameP
                  return $ QFunctionName (r <> r') (Just $ mkNormalSchema s r) f
  
              , do
                  (f, r) <- Tok.functionNameP
                  return $ QFunctionName r Nothing f
              ]
  
          (distinct, arguments, parameters, r') <- do
              _ <- Tok.openP
              (distinct, arguments) <- choice
                  [ case name of
                      QFunctionName _ Nothing "count" -> do
                          r' <- Tok.starP
                          return ( notDistinct
                                 , [ConstantExpr r' $ NumericConstant r' "1"]
                                 )
  
                      QFunctionName _ Nothing "substring" -> do
                          arg1 <- exprP
                          word <- (const True <$> Tok.fromP)
                              <|> (const False <$> Tok.commaP)
                          arg2 <- exprP
                          arg3 <- optionMaybe $ do
                              _ <- if word then Tok.forP else Tok.commaP
                              exprP
                          return ( notDistinct
                                 , arg1 : arg2 : maybe [] pure arg3
                                 )
  
                      _ -> fail "no special case for function"
  
                  , do
                      isDistinct <- distinctP
                      (isDistinct,) . (:[]) <$> exprP
  
                  , (notDistinct,) <$> exprP `sepBy` Tok.commaP
                  ]
  
              parameters <- option [] $ do
                  _ <- Tok.usingP
                  _ <- Tok.parametersP
  
                  flip sepBy1 Tok.commaP $ do
                      (param, paramr) <- Tok.paramNameP
                      _ <- Tok.equalP
                      expr <- exprP
                      pure (ParamName paramr param, expr)
  
              optional $ Tok.ignoreP >> Tok.nullsP
  
              r' <- Tok.closeP
              return (distinct, arguments, parameters, r')
  
          over <- optionMaybe $ try $ overP
  
          let r'' = maybe r' getInfo over <> getInfo name
  
          return $ FunctionExpr r'' name distinct arguments parameters Nothing over
  
      bareFuncP = do
          (v, r) <- choice
              [ Tok.currentDatabaseP
              , Tok.currentSchemaP
              , Tok.userP
              , Tok.currentUserP
              , Tok.sessionUserP
              , Tok.currentDateP
              , Tok.currentTimeP
              , Tok.currentTimestampP
              , Tok.localTimeP
              , Tok.localTimestampP
              , Tok.sysDateP
              ]
  
          pure $ FunctionExpr r (QFunctionName r Nothing v) notDistinct [] [] Nothing Nothing
  
  orderTopLevelP :: Parser (Range, [Order RawNames Range])
  orderTopLevelP = orderExprP False True
  
  orderInWindowClauseP :: Parser [Order RawNames Range]
  orderInWindowClauseP = snd <$> orderExprP True False
  
  orderExprP :: Bool -> Bool -> Parser (Range, [Order RawNames Range])
  orderExprP nullsClausePermitted positionalReferencesPermitted = do
      r <- Tok.orderP
      _ <- Tok.byP
      orders <- helperP `sepBy1` Tok.commaP
      let r' = getInfo $ last orders
      return (r <> r', orders)
    where
      helperP :: Parser (Order RawNames Range)
      helperP = do
          expr <- exprP
          let posOrExpr = if positionalReferencesPermitted
                          then handlePositionalReferences expr
                          else PositionOrExprExpr expr
          dir <- directionP
          nulls <- case (nullsClausePermitted, dir) of
              (False, _) -> return $ NullsAuto Nothing
              (True, OrderAsc _) -> option (NullsLast Nothing) nullsP
              (True, OrderDesc _) -> option (NullsFirst Nothing) nullsP
          let info = getInfo expr ?<> getInfo dir <> getInfo nulls
          return $ Order info posOrExpr dir nulls
  
  directionP :: Parser (OrderDirection (Maybe Range))
  directionP = option (OrderAsc Nothing) $ choice
      [ OrderAsc . Just <$> Tok.ascP
      , OrderDesc . Just <$> Tok.descP
      ]
  
  nullsP :: Parser (NullPosition (Maybe Range))
  nullsP = do
      r <- Tok.nullsP
      choice
          [ Tok.firstP >>= \ r' -> return $ NullsFirst $ Just $ r <> r'
          , Tok.lastP >>= \ r' -> return $ NullsLast $ Just $ r <> r'
          , Tok.autoP >>= \ r' -> return $ NullsAuto $ Just $ r <> r'
          ]
  
  frameP :: Parser (Frame Range)
  frameP = do
      ftype <- choice
          [ RowFrame <$> Tok.rowsP
          , RangeFrame <$> Tok.rangeP
          ]
  
      choice
          [ do
              _ <- Tok.betweenP
              start <- frameBoundP
              _ <- Tok.andP
              end <- frameBoundP
  
              let r = getInfo ftype <> getInfo end
              return $ Frame r ftype start (Just end)
  
          , do
              start <- frameBoundP
  
              let r = getInfo ftype <> getInfo start
              return $ Frame r ftype start Nothing
          ]
  
  frameBoundP :: Parser (FrameBound Range)
  frameBoundP = choice
      [ fmap Unbounded $ (<>)
          <$> Tok.unboundedP
          <*> choice [ Tok.precedingP, Tok.followingP ]
  
      , fmap CurrentRow $ (<>) <$> Tok.currentP <*> Tok.rowP
      , constantP >>= \ expr -> choice
          [ Tok.precedingP >>= \ r ->
              return $ Preceding (getInfo expr <> r) expr
  
          , Tok.followingP >>= \ r ->
              return $ Following (getInfo expr <> r) expr
          ]
      ]
  
  
  overP :: Parser (OverSubExpr RawNames Range)
  overP = do
      start <- Tok.overP
      subExpr <- choice
          [ Left <$> windowP
          , Right <$> windowNameP
          ]
      return $ case subExpr of
        Left w -> mergeWindowInfo start w
        Right wn -> OverWindowName (start <> getInfo wn) wn
    where
      windowP :: Parser (OverSubExpr RawNames Range)
      windowP = do
        start' <- Tok.openP
        expr <- choice
            [ Left <$> windowExprP start'
            , Right <$> partialWindowExprP start'
            ]
        return $ case expr of
          Left w -> OverWindowExpr (start' <> getInfo w) w
          Right pw -> OverPartialWindowExpr (start' <> getInfo pw) pw
  
      mergeWindowInfo :: Range -> OverSubExpr RawNames Range -> OverSubExpr RawNames Range
      mergeWindowInfo r = \case
          OverWindowExpr r' WindowExpr{..} ->
              OverWindowExpr (r <> r') $ WindowExpr { windowExprInfo = windowExprInfo <> r , ..}
          OverWindowName r' n -> OverWindowName (r <> r') n
          OverPartialWindowExpr r' PartialWindowExpr{..} ->
              OverPartialWindowExpr (r <> r') $ PartialWindowExpr { partWindowExprInfo = partWindowExprInfo <> r , ..}
  
  windowExprP :: Range -> Parser (WindowExpr RawNames Range)
  windowExprP start =
    do
      partition <- optionMaybe partitionP
      order <- option [] orderInWindowClauseP
      frame <- optionMaybe frameP
      end <- Tok.closeP
      let info = start <> end
      return (WindowExpr info partition order frame)
  
  partialWindowExprP :: Range -> Parser (PartialWindowExpr RawNames Range)
  partialWindowExprP start =
    do
      inherit <- windowNameP
      order <- option [] orderInWindowClauseP
      frame <- optionMaybe frameP
      end <- Tok.closeP
      let info = start <> end
      return (PartialWindowExpr info inherit Nothing order frame)
  
  windowNameP :: Parser (WindowName Range)
  windowNameP =
    do
      (name, r) <- Tok.windowNameP
      return $ WindowName r name
  
  partitionP :: Parser (Partition RawNames Range)
  partitionP = do
      r <- Tok.partitionP
      choice
          [ Tok.byP >> (exprP `sepBy1` Tok.commaP) >>= \ exprs ->
                  return $ PartitionBy
                              (sconcat $ r :| map getInfo exprs) exprs
  
          , Tok.bestP >>= \ r' -> return $ PartitionBest (r <> r')
          , Tok.nodesP >>= \ r' -> return $ PartitionNodes (r <> r')
          ]
  
  
  existsExprP :: Parser (Expr RawNames Range)
  existsExprP = do
      r <- Tok.existsP
      _ <- Tok.openP
      query <- queryP
      r' <- Tok.closeP
  
      return $ ExistsExpr (r <> r') query
  
  
  arrayExprP :: Parser (Expr RawNames Range)
  arrayExprP = do
      s <- Tok.arrayP
      _ <- Tok.openBracketP
      cs <- exprP `sepBy` Tok.commaP
      e <- Tok.closeBracketP
      pure $ ArrayExpr (s <> e) cs
  
  
  castExprP :: Parser (Expr RawNames Range)
  castExprP = foldl (flip ($)) <$> castedP <*> many castP
    where
      castedP :: Parser (Expr RawNames Range)
      castedP = choice
          [ try parenExprP
          , try existsExprP
          , try arrayExprP
          , try functionExprP
          , caseExprP
          , try $ do
              constant <- constantP
              return $ ConstantExpr (getInfo constant) constant
  
          , do
              name <- columnNameP
              return $ ColumnExpr (getInfo name) name
          ]
  
      castP :: Parser (Expr RawNames Range -> Expr RawNames Range)
      castP = do
          _ <- Tok.castOpP
          typeName <- dataTypeP
  
          let r expr = getInfo expr <> getInfo typeName
          return (\ expr -> TypeCastExpr (r expr) CastFailureError expr typeName)
  
  
  atTimeZoneExprP :: Parser (Expr RawNames Range)
  atTimeZoneExprP = foldl (flip ($)) <$> castExprP <*> many atTimeZoneP
    where
      atTimeZoneP :: Parser (Expr RawNames Range -> Expr RawNames Range)
      atTimeZoneP = do
          _ <- Tok.atP
          _ <- Tok.timezoneP
          tz <- castExprP
  
          return $ \ expr ->
              AtTimeZoneExpr (getInfo expr <> getInfo tz) expr tz
  
  
  unOpP :: Text -> Parser (Expr RawNames Range -> Expr RawNames Range)
  unOpP op = do
      r <- Tok.symbolP op
      return $ \ expr -> UnOpExpr (r <> getInfo expr) (Operator op) expr
  
  
  negateExprP :: Parser (Expr RawNames Range)
  negateExprP = do
      neg <- option id $ choice $ map unOpP [ "+", "-", "@", "~" ]
      expr <- atTimeZoneExprP
      return $ neg expr
  
  
  binOpP :: Text -> Parser (Expr RawNames Range -> Expr RawNames Range -> Expr RawNames Range)
  binOpP op = do
      r <- Tok.symbolP op
  
      let r' lhs rhs = sconcat $ r :| map getInfo [lhs, rhs]
      return $ \ lhs rhs -> BinOpExpr (r' lhs rhs) (Operator op) lhs rhs
  
  
  exponentExprP :: Parser (Expr RawNames Range)
  exponentExprP = negateExprP `chainl1` binOpP "^"
  
  
  productExprP :: Parser (Expr RawNames Range)
  productExprP = exponentExprP `chainl1` opP
    where
      opP = choice $ map binOpP [ "*", "//", "/", "%" ]
  
  
  sumExprP :: Parser (Expr RawNames Range)
  sumExprP = productExprP `chainl1` opP
    where
      opP = choice $ map binOpP [ "+", "-" ]
  
  
  bitwiseExprP :: Parser (Expr RawNames Range)
  bitwiseExprP = sumExprP `chainl1` opP
    where
      opP = choice $ map binOpP [ "&", "|", "#" ]
  
  
  bitShiftExprP :: Parser (Expr RawNames Range)
  bitShiftExprP = bitwiseExprP `chainl1` opP
    where
      opP = choice $ map binOpP [ "<<", ">>" ]
  
  
  notP :: Parser (Expr RawNames Range -> Expr RawNames Range)
  notP = (\ r -> UnOpExpr r "NOT") <$> Tok.notP
  
  isExprP :: Parser (Expr RawNames Range)
  isExprP = do
      expr <- bitShiftExprP
      is <- fmap (foldl (.) id) $ many $ choice
          [ do
              _ <- Tok.isP
  
              not_ <- option id notP
  
              (not_ .) <$> choice
                  [ Tok.trueP >>= \ r -> return (UnOpExpr r "ISTRUE")
                  , Tok.falseP >>= \ r -> return (UnOpExpr r "ISFALSE")
                  , Tok.nullP >>= \ r -> return (UnOpExpr r "ISNULL")
                  , Tok.unknownP >>= \ r -> return (UnOpExpr r "ISUNKNOWN")
                  ]
          , Tok.isnullP >>= \ r -> return (UnOpExpr r "ISNULL")
          , Tok.notnullP >>= \ r -> return (UnOpExpr r "NOT" . UnOpExpr r "ISNULL")
          ]
  
      return $ is expr
  
  
  appendExprP :: Parser (Expr RawNames Range)
  appendExprP = isExprP `chainl1` binOpP "||"
  
  
  inExprP :: Parser (Expr RawNames Range)
  inExprP = do
      expr <- appendExprP
      not_ <- option id notP
      in_ <- foldl (.) id <$> many inP
  
      return $ not_ $ in_ expr
  
    where
      inP = do
          _ <- Tok.inP
          _ <- Tok.openP
          list <- choice
              [ Left <$> queryP
              , Right <$> exprP `sepBy1` Tok.commaP
              ]
  
          r <- Tok.closeP
  
          return $ case list of
              Left query ->
                  \ expr -> InSubqueryExpr (getInfo expr <> r) query expr
  
              Right constants ->
                  \ expr -> InListExpr (getInfo expr <> r) constants expr
  
  betweenExprP :: Parser (Expr RawNames Range)
  betweenExprP = do
      expr <- inExprP
      between <- foldl (.) id <$> many betweenP
  
      return $ between expr
    where
      betweenP = do
          _ <- Tok.betweenP
          start <- bitShiftExprP
          _ <- Tok.andP
          end <- bitShiftExprP
  
          let r expr = getInfo expr <> getInfo end
          return $ \ expr -> BetweenExpr (r expr) start end expr
  
  
  overlapsExprP :: Parser (Expr RawNames Range)
  overlapsExprP = try overlapsP <|> betweenExprP
    where
      overlapsP = do
          let pair :: Parser a -> Parser ((a, a), Range)
              pair p = do
                  r <- Tok.openP
                  s <- p
                  _ <- Tok.commaP
                  e <- p
                  r' <- Tok.closeP
                  return ((s, e), r <> r')
  
          (lhs, r) <- pair exprP
          _ <- Tok.overlapsP
          (rhs, r') <- pair exprP
          return $ OverlapsExpr (r <> r') lhs rhs
  
  
  likeExprP :: Parser (Expr RawNames Range)
  likeExprP = do
      expr <- overlapsExprP
      like <- option id comparisonP
      return $ like expr
    where
      comparisonP :: Parser (Expr RawNames Range -> Expr RawNames Range)
      comparisonP = choice
          [ do
              comparison <- symbolComparisonP
              pattern <- Pattern <$> overlapsExprP
              return $ comparison pattern
          , do
              comparison <- textComparisonP
              pattern <- Pattern <$> overlapsExprP
              escape <- optionMaybe $ do
                  _ <- Tok.escapeP
                  Escape <$> exprP
  
              return $ comparison escape pattern
          ]
  
  
      symbolComparisonP :: Parser (Pattern RawNames Range -> Expr RawNames Range -> Expr RawNames Range)
      symbolComparisonP = choice $
          let r expr pattern = getInfo expr <> getInfo pattern
           in [ do
                  _ <- Tok.likeOpP
                  return $ \ pattern expr -> LikeExpr (r pattern expr) "LIKE" Nothing pattern expr
  
              , do
                  _ <- Tok.iLikeOpP
                  return $ \ pattern expr -> LikeExpr (r pattern expr) "ILIKE" Nothing pattern expr
  
              , do
                  _ <- Tok.notLikeOpP
                  return $ \ pattern expr ->
                      UnOpExpr (r pattern expr) "NOT" $ LikeExpr (r pattern expr) "LIKE" Nothing pattern expr
  
              , do
                  _ <- Tok.notILikeOpP
                  return $ \ pattern expr ->
                      UnOpExpr (r pattern expr) "NOT" $ LikeExpr (r pattern expr) "ILIKE" Nothing pattern expr
              , do
                  _ <- Tok.regexMatchesP
                  return $ \ pattern expr ->
                      BinOpExpr (r pattern expr) "REGEX MATCHES" expr $ patternExpr pattern
  
              , do
                  _ <- Tok.regexIgnoreCaseMatchesP
                  return $ \ pattern expr ->
                      BinOpExpr (r pattern expr) "REGEX IGNORE-CASE MATCHES" expr $ patternExpr pattern
  
              , do
                  _ <- Tok.notRegexMatchesP
                  return $ \ pattern expr ->
                      UnOpExpr (r pattern expr) "NOT" $
                               BinOpExpr (r pattern expr) "REGEX MATCHES" expr $ patternExpr pattern
  
              , do
                  _ <- Tok.notRegexIgnoreCaseMatchesP
                  return $ \ pattern expr ->
                      UnOpExpr (r pattern expr) "NOT" $
                               BinOpExpr (r pattern expr) "REGEX IGNORE-CASE MATCHES" expr $ patternExpr pattern
  
              ]
  
      textComparisonP :: Parser (Maybe (Escape RawNames Range) -> Pattern RawNames Range -> Expr RawNames Range -> Expr RawNames Range)
      textComparisonP = do
          not_ <- option id notP
  
          like <- choice
              [ Tok.likeP >>= \ r -> return $ LikeExpr r "LIKE"
              , Tok.iLikeP >>= \ r -> return $ LikeExpr r "ILIKE"
              , Tok.likeBP >>= \ r -> return $ LikeExpr r "LIKE"
              , Tok.iLikeBP >>= \ r -> return $ LikeExpr r "ILIKE"
              ]
  
          return $ \ escape pattern expr -> not_ $ like escape pattern expr
  
  
  mkBinOp :: (Text, a) -> Expr r a -> Expr r a -> Expr r a
  mkBinOp (op, r) = BinOpExpr r (Operator op)
  
  inequalityExprP :: Parser (Expr RawNames Range)
  inequalityExprP = likeExprP `chainl1` (mkBinOp <$> Tok.inequalityOpP)
  
  
  equalityExprP :: Parser (Expr RawNames Range)
  equalityExprP = inequalityExprP `chainl1` (mkBinOp <$> Tok.equalityOpP)
  
  
  notExprP :: Parser (Expr RawNames Range)
  notExprP = do
      nots <- appEndo . fold . reverse . map Endo <$> many notP
      expr <- equalityExprP
      return $ nots expr
  
  
  andExprP :: Parser (Expr RawNames Range)
  andExprP = notExprP `chainl1`
      (Tok.andP >>= \ r -> return $ BinOpExpr r "AND")
  
  
  orExprP :: Parser (Expr RawNames Range)
  orExprP = andExprP `chainl1` (Tok.orP >>= \ r -> return (BinOpExpr r "OR"))
  
  
  singleTableP :: Parser (Tablish RawNames Range)
  singleTableP = try subqueryP <|> try tableP <|> parenthesizedJoinP
    where
      subqueryP = do
          r <- Tok.openP
          query <- queryP
          _ <- Tok.closeP
          optional Tok.asP
          (name, r') <- Tok.tableNameP
  
          alias <- makeTableAlias r' name
          return $ TablishSubQuery (r <> r')
                                   (TablishAliasesT alias)
                                   query
  
      tableP = do
          name <- tableNameP
          maybe_alias <- optionMaybe $ do
              optional Tok.asP
              (alias, r) <- Tok.tableNameP
              makeTableAlias r alias
  
          let r = case maybe_alias of
                  Nothing -> getInfo name
                  Just alias -> getInfo alias <> getInfo name
              aliases = maybe TablishAliasesNone TablishAliasesT maybe_alias
  
          return $ TablishTable r aliases name
  
      parenthesizedJoinP = do
          tablish <- P.between Tok.openP Tok.closeP $ do
              table <- singleTableP
              joins <- fmap (appEndo . fold . reverse) $ many1 $ Endo <$> joinP
              return $ joins table
  
          optional $ do
              optional Tok.asP
              void Tok.tableNameP
  
          pure tablish
  
  
  optionalParensP :: Parser a -> Parser a
  optionalParensP p = try p <|> P.between Tok.openP Tok.closeP p
  
  manyParensP :: Parser a -> Parser a
  manyParensP p = try p <|> P.between Tok.openP Tok.closeP (manyParensP p)
  
  tablishP :: Parser (Tablish RawNames Range)
  tablishP = do
      table <- singleTableP
      joins <- fmap (appEndo . fold . reverse) $ many $ Endo <$> joinP
      return $ joins table
  
  joinP :: Parser (Tablish RawNames Range -> Tablish RawNames Range)
  joinP = regularJoinP <|> naturalJoinP <|> crossJoinP
  
  regularJoinP :: Parser (Tablish RawNames Range -> Tablish RawNames Range)
  regularJoinP = do
      maybeJoinType <- optionMaybe $ innerJoinTypeP <|> outerJoinTypeP
      joinType <- Tok.joinP >>= \ r -> return $ case maybeJoinType of
          Nothing -> JoinInner r
          Just joinType -> (<> r) <$> joinType
  
      rhs <- singleTableP
      condition <- choice
          [ do
              _ <- Tok.onP <?> "condition in join clause"
              JoinOn <$> exprP
          , do
              s <- Tok.usingP <?> "using in join clause"
              _ <- Tok.openP
              names <- flip sepBy1 Tok.commaP $ do
                  (name, r) <- Tok.columnNameP
                  pure $ QColumnName r None name
              e <- Tok.closeP
              return $ JoinUsing (s <> e) names
          ]
  
      let r lhs = getInfo lhs <> getInfo rhs <> getInfo condition
      return $ \ lhs ->
          TablishJoin (r lhs) joinType condition lhs rhs
  
  outerJoinTypeP :: Parser (JoinType Range)
  outerJoinTypeP = do
      joinType <- choice
          [ Tok.leftP >>= \ r -> return $ JoinLeft r
          , Tok.rightP >>= \ r -> return $ JoinRight r
          , Tok.fullP >>= \ r -> return $ JoinFull r
          ]
  
      optional Tok.outerP
  
      return joinType
  
  innerJoinTypeP :: Parser (JoinType Range)
  innerJoinTypeP = Tok.innerP >>= \ r -> return $ JoinInner r
  
  naturalJoinP :: Parser (Tablish RawNames Range -> Tablish RawNames Range)
  naturalJoinP = do
      r <- Tok.naturalP
      maybeJoinType <- optionMaybe $ innerJoinTypeP <|> outerJoinTypeP
      joinType <- Tok.joinP >>= \ r' -> return $ case maybeJoinType of
          Nothing -> JoinInner r
          Just joinType -> (const $ r <> r') <$> joinType
  
      rhs <- singleTableP
  
      let r' lhs = getInfo lhs <> getInfo rhs
      return $ \ lhs -> TablishJoin (r' lhs) joinType (JoinNatural r Unused) lhs rhs
  
  crossJoinP :: Parser (Tablish RawNames Range -> Tablish RawNames Range)
  crossJoinP = do
      r <- Tok.crossP
      r'<- Tok.joinP
      rhs <- singleTableP
  
      let r'' lhs = getInfo lhs <> getInfo rhs
          joinInfo = r <> r'
          true' = JoinOn $ ConstantExpr joinInfo $ BooleanConstant joinInfo True
      return $ \ lhs ->
          TablishJoin (r'' lhs) (JoinInner joinInfo) true' lhs rhs
  
  
  createProjectionPrefixP :: Parser Range
  createProjectionPrefixP = do
      s <- Tok.createP
      e <- Tok.projectionP
      pure $ s <> e
  
  
  createProjectionP :: Parser (CreateProjection RawNames Range)
  createProjectionP = do
      s <- createProjectionPrefixP
  
      createProjectionIfNotExists <- ifNotExistsP
      createProjectionName <- projectionNameP
      createProjectionColumns <- optionMaybe $ try columnListP
      _ <- Tok.asP
      createProjectionQuery <- queryP
  
      createProjectionSegmentation <- optionMaybe $ choice
          [ do
              s' <- Tok.unsegmentedP
              choice
                  [ do
                      _ <- Tok.nodeP
                      node <- nodeNameP
                      let e' = getInfo node
                      pure $ UnsegmentedOneNode (s' <> e') node
                  , do
                      _ <- Tok.allP
                      e' <- Tok.nodesP
                      pure $ UnsegmentedAllNodes (s' <> e')
                  ]
          , do
              s' <- Tok.segmentedP
              _ <- Tok.byP
              expr <- exprP
              list <- nodeListP
  
              pure $ SegmentedBy (s' <> getInfo list) expr list
          ]
  
      createProjectionKSafety <- optionMaybe $ do
          s' <- Tok.ksafeP
          choice
              [ do
                  (n, e') <- integerP
                  pure $ KSafety (s' <> e') (Just n)
  
              , pure $ KSafety s' Nothing
              ]
  
      let createProjectionInfo =
              sconcat $ s :| catMaybes [ Just $ getInfo createProjectionQuery
                                       , getInfo <$> createProjectionSegmentation
                                       , getInfo <$> createProjectionKSafety
                                       ]
  
      pure CreateProjection{..}
  
    where
      columnListP :: Parser (NonEmpty (ProjectionColumn Range))
      columnListP = do
          _ <- Tok.openP
          c:cs <- flip sepBy1 Tok.commaP $ do
              (projectionColumnName, s) <- Tok.columnNameP
              projectionColumnAccessRank <- optionMaybe $ do
                  s' <- Tok.accessRankP
                  (n, e') <- integerP
                  pure $ AccessRank (s' <> e') n
  
              projectionColumnEncoding <- optionMaybe $ do
                  _ <- Tok.encodingP
                  Tok.encodingTypeP
  
              let projectionColumnInfo =
                      sconcat $ s :| catMaybes [ getInfo <$> projectionColumnAccessRank
                                               , getInfo <$> projectionColumnEncoding ]
  
              pure ProjectionColumn{..}
  
          _ <- Tok.closeP
          pure (c:|cs)
  
  
  multipleRenameP :: Parser (MultipleRename RawNames Range)
  multipleRenameP = do
      s <- Tok.alterP
      _ <- Tok.tableP
  
      sources <- tableNameP `sepBy1` Tok.commaP
  
      _ <- Tok.renameP
      _ <- Tok.toP
  
      targets <- map (\ uqtn -> uqtn { tableNameSchema = Nothing }) <$> unqualifiedTableNameP `sepBy1` Tok.commaP
  
      when (length sources /= length targets) $ fail "multi-renames require the same number of sources and targets"
  
      let e = getInfo $ last targets
          pairs = zip sources targets
          toAlterTableRename = \ (from, to) ->
              AlterTableRenameTable (getInfo from <> getInfo to) from to
          renames = map toAlterTableRename pairs
      pure $ MultipleRename (s <> e) renames
  
  
  setSchemaP :: Parser (SetSchema RawNames Range)
  setSchemaP = do
      s <- Tok.alterP
      _ <- Tok.tableP
  
      table <- tableNameP
  
      _ <- Tok.setP
      _ <- Tok.schemaP
  
      (schema, r) <- Tok.schemaNameP
  
      e <- option r $ choice [Tok.restrictP, Tok.cascadeP]
  
      pure $ SetSchema (s <> e) table $ mkNormalSchema schema r
  
  
  renameProjectionP :: Parser Range
  renameProjectionP = do
      s <- Tok.alterP
      _ <- Tok.projectionP
  
      _ <- projectionNameP
  
      _ <- Tok.renameP
      _ <- Tok.toP
  
      to <- projectionNameP
  
      pure $ s <> getInfo to
  
  
  alterResourcePoolPrefixP :: Parser Range
  alterResourcePoolPrefixP = do
      s <- Tok.alterP
      _ <- Tok.resourceP
      e <- Tok.poolP
      pure $ s <> e
  
  alterResourcePoolP :: Parser Range
  alterResourcePoolP = do
      s <- alterResourcePoolPrefixP
      ts <- P.many Tok.notSemicolonP
      pure $ case reverse ts of
          [] -> s
          e:_ -> s <> e
  
  
  createResourcePoolPrefixP :: Parser Range
  createResourcePoolPrefixP = do
      s <- Tok.createP
      _ <- Tok.resourceP
      e <- Tok.poolP
      pure $ s <> e
  
  createResourcePoolP :: Parser Range
  createResourcePoolP = do
      s <- createResourcePoolPrefixP
      ts <- P.many Tok.notSemicolonP
      pure $ case reverse ts of
          [] -> s
          e:_ -> s <> e
  
  
  dropResourcePoolPrefixP :: Parser Range
  dropResourcePoolPrefixP = do
      s <- Tok.dropP
      _ <- Tok.resourceP
      e <- Tok.poolP
      pure $ s <> e
  
  dropResourcePoolP :: Parser Range
  dropResourcePoolP = do
      s <- dropResourcePoolPrefixP
      e <- Tok.notSemicolonP -- the pool's name
      pure $ s <> e
  
  
  createFunctionPrefixP :: Parser Range
  createFunctionPrefixP = do
      s <- Tok.createP
      _ <- optional $ Tok.orP >> Tok.replaceP
      e <- choice
           [ do
                 _ <- optional $ Tok.transformP <|> Tok.analyticP <|> Tok.aggregateP
                 Tok.functionP
           , Tok.filterP
           , Tok.parserP
           , Tok.sourceP
           ]
      pure $ s <> e
  
  
  createFunctionP :: Parser Range
  createFunctionP = do
      s <- createFunctionPrefixP
      ts <- P.many Tok.notSemicolonP
      pure $ case reverse ts of
          [] -> s
          e:_ -> s <> e
  
  
  alterTableAddConstraintP :: Parser Range
  alterTableAddConstraintP = do
      s <- Tok.alterP
      _ <- Tok.tableP
  
      _ <- tableNameP
  
      _ <- Tok.addP
      e <- tableConstraintP
  
      pure $ s <> e
  
  tableConstraintP :: Parser Range
  tableConstraintP = do
      s <- optionMaybe $ do
          s <- Tok.constraintP
          _ <- Tok.constraintNameP
          return s
  
      e <- choice
          [ do
              _ <- Tok.primaryP
              _ <- Tok.keyP
              e <- columnListP
              option e (Tok.enabledP <|> Tok.disabledP)
          , do
              _ <- Tok.uniqueP
              e <- columnListP
              option e (Tok.enabledP <|> Tok.disabledP)
          , do
              _ <- Tok.foreignP
              _ <- Tok.keyP
              _ <- columnListP
              _ <- Tok.referencesP
              e <- getInfo <$> tableNameP
              option e columnListP
          , do
              _ <- Tok.checkP
              e <- getInfo <$> exprP
              option e (Tok.enabledP <|> Tok.disabledP)
          ]
  
      return (maybe e id s <> e)
    where
      columnListP :: Parser Range
      columnListP = do
          s <- Tok.openP
          _ <- Tok.columnNameP `sepBy1` Tok.commaP
          e <- Tok.closeP
          return (s <> e)
  
  
  exportToStdoutP :: Parser Range
  exportToStdoutP = do
      s <- Tok.exportP
      _ <- Tok.toP
      _ <- Tok.stdoutP
      _ <- Tok.fromP
      _ <- tableNameP
      _ <- Tok.openP
      _ <- Tok.columnNameP `sepBy1` Tok.commaP
      e <- Tok.closeP
  
      pure $ s <> e
  
  
  setSessionPrefixP :: Parser Range
  setSessionPrefixP = do
      s <- Tok.setP
      e <- Tok.sessionP
      return $ s <> e
  
  
  setSessionP :: Parser Range
  setSessionP = do
      s <- setSessionPrefixP
      ts <- P.many Tok.notSemicolonP
      pure $ case reverse ts of
          [] -> s
          e:_ -> s <> e
  
  
  setTimeZoneP :: Parser Range
  setTimeZoneP = do
      s <- Tok.setP
      _ <- Tok.timezoneP
      _ <- Tok.toP
      e <- choice [ Tok.defaultP
                  , snd <$> Tok.stringP
                  , Tok.intervalP >> snd <$> Tok.stringP
                  ]
      return $ s <> e
  
  
  connectP :: Parser Range
  connectP = do
      s <- Tok.connectP
      _ <- Tok.toP
      _ <- Tok.verticaP
      _ <- Tok.databaseNameP
      _ <- Tok.userP
      _ <- Tok.userNameP
      _ <- Tok.passwordP
      e <- snd <$> Tok.stringP <|> snd <$> starsP
      e' <- option e $ do
          _ <- Tok.onP
          _ <- Tok.stringP
          _ <- Tok.commaP
          snd <$> Tok.numberP
      pure $ s <> e'
    where
      starsP = do
          rs <- P.many1 Tok.starP
          let text = TL.take (fromIntegral $ length rs) $ TL.repeat '*'
              r = head rs <> last rs
          pure (text, r)
  
  
  disconnectP :: Parser Range
  disconnectP = do
      s <- Tok.disconnectP
      (_, e) <- Tok.databaseNameP
      pure $ s <> e
  
  createAccessPolicyP :: Parser Range
  createAccessPolicyP = do
      s <- Tok.createP
      _ <- Tok.accessP
      _ <- Tok.policyP
      _ <- Tok.onP
      _ <- tableNameP
      _ <- Tok.forP
      _ <- Tok.columnP
      _ <- Tok.columnNameP
      _ <- exprP
      e <- choice [ Tok.enableP, Tok.disableP ]
      pure $ s <> e
  
  
  copyFromP :: Parser Range
  copyFromP = do
      s <- Tok.copyP
      e <- getInfo <$> tableNameP
  
      e' <- consumeOrderedOptions e $
          [ ingestionColumnListP (getInfo <$> exprP)
          , ingestionColumnOptionP
          , fromP -- you need **either** a FROM or a SOURCE clause, but let's not be fussy
          , fileStorageFormatP
          ]
      e'' <- consumeUnorderedOptions e' $
          [ do
              _ <- optional Tok.withP
              choice [ fileSourceP
                     , fileFilterP
                     , fileParserP
                     ]
          , delimiterAsP
          , trailingNullColsP
          , nullAsP
          , escapeFormatP
          , enclosedByP
          , recordTerminatorP
          , try $ skipRecordsP
          , try $ skipBytesP
          , trimByteP
          , rejectMaxP
          , rejectedDataOnNodeP
          , exceptionsOnNodeP
          , Tok.enforceLengthP
          , errorToleranceP
          , abortOnErrorP
          , optional Tok.storageP >> loadMethodP
          , streamNameP
          , noCommitP
          ]
  
      return $ s <> e''
  
    where
      onNodeP :: Range -> Parser Range
      onNodeP r = do
          s <- option r $ choice
                   [ try $ Tok.onP >> snd <$> Tok.nodeNameP
                   , Tok.onP >> Tok.anyP >> Tok.nodeP
                   ]
          e <- option s compressionP
          return $ s <> e
  
      fromP :: Parser Range
      fromP = do
          outerS <- Tok.fromP
          outerE <- choice $
              [ do
                  s <- Tok.stdinP
                  e <- option s compressionP
                  return $ s <> e
              , do
                  (_, s) <- Tok.stringP
                  e <- last <$> ((onNodeP s) `sepBy1` Tok.commaP)
                  return $ s <> e
              , do
                  s <- Tok.localP
                  e' <- choice [ do
                                    e <- Tok.stdinP
                                    option e compressionP
                              , let pathToDataP = do
                                        e <- snd <$> Tok.stringP
                                        option e compressionP
                                 in last <$> (pathToDataP `sepBy1` Tok.commaP)
                              ]
                  return $ s <> e'
              , do
                  s <- Tok.verticaP
                  _ <- Tok.databaseNameP
                  _ <- Tok.dotP
                  e <- getInfo <$> tableNameP
                  e' <- option e $ do
                      _ <- Tok.openP
                      _ <- Tok.columnNameP `sepBy1` Tok.commaP
                      Tok.closeP
                  return $ s <> e'
              ]
          return $ outerS <> outerE
  
  
  showP :: Parser Range
  showP = do
      s <- Tok.showP
      es <- many1 Tok.notSemicolonP
      return $ s <> last es
  
  
  mergeP :: Parser (Merge RawNames Range)
  mergeP = do
      r1 <- Tok.mergeP
  
      _ <- Tok.intoP
      mergeTargetTable <- tableNameP
      mergeTargetAlias <- optionMaybe tableAliasP
  
      _ <- Tok.usingP
      mergeSourceTable <- tableNameP
      mergeSourceAlias <- optionMaybe tableAliasP
  
      _ <- Tok.onP
      mergeCondition <- exprP
  
      -- lookahead
      mergeUpdateDirective <- optionMaybe $ do
          _ <- try $ P.lookAhead $ Tok.whenP >> Tok.matchedP
          _ <- Tok.whenP
          _ <- Tok.matchedP
          _ <- Tok.thenP
          _ <- Tok.updateP
          _ <- Tok.setP
          NE.fromList <$> colValP `sepBy1` Tok.commaP
  
      (mergeInsertDirectiveColumns, mergeInsertDirectiveValues, r2) <- option (Nothing, Nothing, Just r1) $ do
          _ <- Tok.whenP
          _ <- Tok.notP
          _ <- Tok.matchedP
          _ <- Tok.thenP
          _ <- Tok.insertP
          cols <- optionMaybe $ NE.fromList <$> P.between Tok.openP Tok.closeP (oqColumnNameP `sepBy1` Tok.commaP)
          _ <- Tok.valuesP
          _ <- Tok.openP
          vals <- NE.fromList <$> defaultExprP `sepBy1` Tok.commaP
          e <- Tok.closeP
          return (cols, Just vals, Just e)
  
      when ((mergeUpdateDirective, mergeInsertDirectiveValues) == (Nothing, Nothing)) $
          fail "MERGE requires at least one of UPDATE and INSERT"
  
      let mLastUpdate = fmap (getInfo . snd . NE.last) mergeUpdateDirective
          mLastInsert = r2
          r3 = sconcat $ NE.fromList $ catMaybes [mLastUpdate, mLastInsert]
          mergeInfo = r1 <> r3
  
      return Merge{..}
  
    where
      tableAliasP :: Parser (TableAlias Range)
      tableAliasP = do
          (name, r) <- Tok.tableNameP
          makeTableAlias r name
  
      colValP :: Parser (ColumnRef RawNames Range, DefaultExpr RawNames Range)
      colValP = do
          col <- oqColumnNameP
          _ <- Tok.equalP
          val <- defaultExprP
          return (col { columnNameTable = Nothing }, val)
  