queryparser 0.1.0.0 → 0.1.0.1
raw patch · 11 files changed
+370/−87 lines, 11 filesdep ~base
Dependency ranges changed: base
Files
- CHANGELOG.md +11/−0
- FUTURE.md +70/−0
- README.md +179/−0
- compat/Test/QuickCheck.hs +35/−0
- queryparser.cabal +11/−8
- src/Data/Functor/Identity/Orphans.hs +0/−31
- src/Data/Maybe/More.hs +0/−27
- src/Database/Sql/Type/Names.hs +2/−3
- src/Database/Sql/Type/Query.hs +3/−3
- src/Database/Sql/Util/Scope.hs +15/−15
- stack.yaml +44/−0
+ CHANGELOG.md view
@@ -0,0 +1,11 @@+# Versioning in a multi-package repo+You'll notice that this repo contains multiple package, each with its own versioning. To manage that, each package updates independently for minor and build changes, but major changes must be coordinated and linked across all packages.++The changelog is organized to reflect this. All packages maintain their own changelog with independent minor and build changes. However, major changes require coordination, which start here.++# 0.1 to 0.2++## 0.1.0 to 0.1.1+- Upgraded to GHC8, base 4.9+- Added docs describing future development +- Replaced redundant `overJust`
+ FUTURE.md view
@@ -0,0 +1,70 @@+# Areas of future interest++Here are some areas of future interest. Consider them an enumeration of "R&D+areas" rather than a technical roadmap:++* The most obvious work is to **expand the parsing coverage**, both by adding+ support for more SQL dialects (e.g. Postgres, Mysql, Sqlite) and by adding+ support for the long tail of infrequently-used language features in existing+ SQL dialects.++* Another logical extension is to add support for **query translation**. It+ would require implementing a `render` function as the inverse of the `parse`+ function: `render` would transform an AST into a string in a particular+ dialect. Once implemented, query translation would be accomplished by parsing+ a query in one dialect, then rendering it into another dialect.++* A more exploratory project is to implement **type-checking of SQL**, with the+ goal of detecting errors in queries through static analysis. The principle is+ to define the types of columns, then use type-inference rules to look for+ type errors in queries, such as "column A and column B are being compared for+ equality, but have incompatible types".++ * In practice, this would probably start by seeding the catalog information+ with annotations for the business types of certain columns. Then, the+ types of other columns would be inferred, using known or observed+ relationships between columns. For example, a known foreign-key+ relationship would generate the inference that the foreign key has the+ same type as the primary key. Alternatively, a list of candidate+ relationships could be generated by applying type-inference rules to the+ stream of queries. For example, if two columns are related by an equality+ or inequality operator, then they have compatible types.++* There are non-trivial use cases for **concrete evaluation of queries**. At+ first glance, the idea of "put in sample data, get sample results" may seem+ redundant when one could just use an actual database. However, concrete+ evaluation would allow QuickCheck for queries. Imagine an interface that let+ users specify a query to be tested, as well as post-conditions in the form of+ SQL queries that relate the original data to the output data. The QuickCheck+ test would generate arbitrary input data sets, run the query, and assert the+ post-conditions. It would then produce minimized examples failing those+ post-conditions. Now, imagine applying that to a set of queries, such as the+ steps in an ETL to produce a dimensionally modeled table.++ * Post-condition queries would produce one row with one column with a+ boolean value (True or False). Such a query could also be called an+ assertion, predicate, or property.++* There are similar use cases for **generating arbitrary queries**. Arbitrary+ queries would allow for QuickCheck testing of databases themselves,+ particularly for catching errors in parsing. Subsequently, generating+ arbitrary table data would permit catching errors in execution.++* We could enrich our understanding of data access patterns by adding support+ for **fingerprinting of queries**, to categorize similar queries together. A+ single query could have multiple fingerprints under different fingerprinting+ algorithms. For example, a "table fingerprint" could be generated by hashing+ a sorted list of all the tables that appeared in the query. A "template+ fingerprint" could be generated by removing all constants and literals in the+ query, then hashing the resulting querystring. This algorithm would give the+ same fingerprint for `SELECT * FROM foo WHERE date > '2018-01-01'` and+ `SELECT * FROM foo WHERE date > '2018-01-02'`.++* We could **improve SQL hygiene** by adding support for query standardization+ in the same spirit as [gofmt](https://blog.golang.org/go-fmt-your-code).+ Query standardization would include auto-formatting of whitespace like line+ breaks and indentation and afford communally-owned queries all the same+ benefits described in the gofmt blogpost: queries would become easier to+ read, easier to write, easier to maintain, and less controversial. More+ aggressive formatting changes would also be possible, such as removing unused+ clauses in a query.
+ README.md view
@@ -0,0 +1,179 @@+# queryparser++Parsing and analysis of Vertica, Hive, and Presto SQL.+++## Gentle introduction++Queryparser supports parsing for three sql-dialects (Vertica, Hive, and+Presto).++Each dialect implements its own tokenization and parsing logic. There is a+single abstract syntax tree (AST) for representing queries of any dialect. This+AST is defined in Database/Sql/Type.hs and Database/Sql/Type/Query.hs.++The parsing logic produces an AST with table and column identifiers that are+"raw" or optionally qualified. Frequently, it is desirable to convert the AST+over raw names to an AST over resolved names, where identifiers are fully+qualified. This transformation is called "name resolution" or simply+"resolution". It requires as input the full list of columns in every table and+the full list of tables in every schema, otherwise known as "catalog+information".++An example of resolution:++ * `SELECT column_c FROM table_t` is a query with raw names+ * `SELECT schema_s.table_t.column_c FROM schema_s.table_t` is the same query with resolved names++Various utility functions ("analyses") have been defined for ASTs over resolved+names, such as:++ * what tables appear in the query?+ * what columns appear in the query, per clause?+ * what is the table/column lineage of a query?+ * what sets of columns does a query compare for equality?++The analyses are the main value-add of this library.+++## Demo++Enough talk, let's show what it can do!++ $ stack ghci+ ...+ ...> :set prompt "> "+ > import Demo+ > -- for the purposes of our demo, we have two tables: foo with columns a,b,c and bar with columns x,y,z+ > demoAllAnalyses "SELECT * FROM foo" -- note that the SELECT * expands to a,b,c+ Tables accessed:+ public.foo+ Columns accessed by clause:+ public.foo.a SELECT+ public.foo.b SELECT+ public.foo.c SELECT+ Joins:+ no joins+ Table lineage:+ no tables modified+ > demoAllAnalyses "SELECT * FROM bar" -- and here the SELECT * expands to x,y,z+ Tables accessed:+ public.bar+ Columns accessed by clause:+ public.bar.x SELECT+ public.bar.y SELECT+ public.bar.z SELECT+ Joins:+ no joins+ Table lineage:+ no tables modified+ > demoAllAnalyses "SELECT x, count(1) FROM foo JOIN bar ON foo.a = bar.y WHERE z IS NOT NULL GROUP BY 1 ORDER BY 2 DESC, b"+ Tables accessed:+ public.bar+ public.foo+ Columns accessed by clause:+ public.bar.x GROUPBY+ public.bar.x SELECT+ public.bar.y JOIN+ public.bar.z WHERE+ public.foo.a JOIN+ public.foo.b ORDER+ Joins:+ public.bar.y <-> public.foo.a+ Table lineage:+ no tables modified+ > -- let's play with some queries that modify table-data!+ > demoTableLineage "INSERT INTO foo SELECT * FROM bar"+ public.foo after the query depends on public.bar, public.foo before the query+ > demoTableLineage "TRUNCATE TABLE foo"+ public.foo no longer has data+ > demoTableLineage "ALTER TABLE bar, foo RENAME TO baz, bar"+ public.bar after the query depends on public.foo before the query+ public.baz after the query depends on public.bar before the query+ public.foo no longer has data+ > -- let's explore a few subtler behaviors of the "joins" analysis (admittedly, something of a misnomer)+ > demoJoins "SELECT * FROM foo JOIN bar ON a=x AND b+c = y+z"+ public.bar.x <-> public.foo.a+ public.bar.y <-> public.foo.b+ public.bar.y <-> public.foo.c+ public.bar.z <-> public.foo.b+ public.bar.z <-> public.foo.c+ > demoJoins "SELECT a FROM foo UNION SELECT x FROM bar"+ public.bar.x <-> public.foo.a++Spin up your own ghci and paste in your own queries!+++## Requirements++To build, you need:++- stack+- docker (recommended)++### OS X++You can install stack on OS X with brew:++ brew install ghc haskell-stack++To install docker, use the default installer found on https://docs.docker.com/mac/++To allow stack to see the docker daemon, add `eval "$(docker-machine+env default)"` to your bashrc or equivalent. (This is following https://docs.docker.com/machine/reference/env/)++### Linux & Other++Follow the directions at https://github.com/commercialhaskell/stack#how-to-install+++## Installation++Once you've got what you need, check out the repo and change to the directory.++Stack will download other dependencies for you:++ stack setup+++Now you can build the package with:++ stack build+++Or run the tests with:++ stack test+++Or run the benchmarks with:++ stack bench+++Or pull things up in ghci with:++ stack ghci++## Contributing++If you'd like to contribute to the repo, use the above installation instructions to get started.++When you're ready, make sure that the code compiles with the `Development` flag, i.e.:++ stack build --flag queryparser:development++## Use++Mostly it boils down to this function:++ parse :: Text -> Either ParseError SqlQuery++To parse some sql from the repl,++ parse "SELECT 1;"++## Areas of future interest++There is substantial room for future work in Queryparser. For more details, see+[Areas of future interest](FUTURE.md).
+ compat/Test/QuickCheck.hs view
@@ -0,0 +1,35 @@+-- 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.++{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# LANGUAGE PackageImports #-}+{-# LANGUAGE CPP #-}++module Test.QuickCheck (module X) where++import "QuickCheck" Test.QuickCheck as X++#if !MIN_VERSION_QuickCheck(2,9,0)+import Data.Functor.Identity++instance Arbitrary a => Arbitrary (Identity a) where+ arbitrary = Identity <$> arbitrary+ shrink (Identity x) = Identity <$> shrink x+#endif
queryparser.cabal view
@@ -6,7 +6,7 @@ -- PVP summary: +-+------- breaking API changes -- | | +----- non-breaking API additions -- | | | +--- code changes with no API change-version: 0.1.0.0+version: 0.1.0.1 -- A short (one-line) description of the package. synopsis: Analysis and parsing library for SQL queries.@@ -43,7 +43,11 @@ -- Extra files to be distributed with the package, such as examples or a -- README.--- extra-source-files:+extra-source-files:+ README.md+ CHANGELOG.md+ FUTURE.md+ stack.yaml -- Constraint on the version of Cabal needed to build this package. cabal-version: >=1.10@@ -78,8 +82,7 @@ , Database.Sql.Util.Schema -- Modules included in this library but not exported.- other-modules: Data.Maybe.More- , Data.Functor.Identity.Orphans+ other-modules: Test.QuickCheck default-extensions: OverloadedStrings , LambdaCase@@ -89,7 +92,7 @@ , FlexibleInstances -- Other library packages from which modules are imported.- build-depends: base >=4.8 && <4.9+ build-depends: base >=4.9 && <4.11 , text >=1.2 && <1.3 , bytestring , containers@@ -106,10 +109,10 @@ , predicate-class -- Directories containing source files.- hs-source-dirs: src+ hs-source-dirs: src, compat - ghc-options: -Wall+ ghc-options: -Wall -Wno-redundant-constraints if flag(development) ghc-options: -Werror@@ -120,7 +123,7 @@ benchmark queryparser-bench type: exitcode-stdio-1.0 main-is: Bench.hs- build-depends: base >=4.8 && <4.9+ build-depends: base >=4.9 && <4.11 , queryparser , criterion , text
− src/Data/Functor/Identity/Orphans.hs
@@ -1,31 +0,0 @@--- 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.--{-# OPTIONS_GHC -fno-warn-orphans #-}--module Data.Functor.Identity.Orphans where--import Data.Functor.Identity--import Test.QuickCheck--instance Arbitrary a => Arbitrary (Identity a) where- arbitrary = Identity <$> arbitrary- shrink (Identity x) = Identity <$> shrink x
− src/Data/Maybe/More.hs
@@ -1,27 +0,0 @@--- 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.--module Data.Maybe.More where--overJust :: Applicative f => (t -> f a) -> Maybe t -> f (Maybe a)-overJust _ Nothing = pure Nothing-overJust f (Just x) = Just <$> f x--
src/Database/Sql/Type/Names.hs view
@@ -41,7 +41,6 @@ import Data.Semigroup import Data.String import Data.Functor.Identity-import Data.Functor.Identity.Orphans () import Data.Data (Data, Typeable) import qualified Data.Map as M@@ -484,11 +483,11 @@ ] instance (ToJSON (f (QSchemaName f a)), ToJSON a) => ToJSON (QFunctionName f a) where- toJSON (QFunctionName info schema function) = object+ toJSON (QFunctionName info schema fn) = object [ "tag" .= String "QFunctionName" , "info" .= info , "schema" .= schema- , "function" .= function+ , "function" .= fn ]
src/Database/Sql/Type/Query.hs view
@@ -897,10 +897,10 @@ , "query" .= query ] - toJSON (FunctionExpr info function distinct args params filter' over) = object+ toJSON (FunctionExpr info fn distinct args params filter' over) = object [ "tag" .= String "FunctionExpr" , "info" .= info- , "function" .= function+ , "function" .= fn , "distinct" .= distinct , "args" .= args , "params" .= params@@ -1401,7 +1401,7 @@ info <- o .: "info" value <- TL.encodeUtf8 <$> o .: "value" <|> BL.pack <$> o .: "value"- <|> fail "expected string or array for StringConstant value" + <|> fail "expected string or array for StringConstant value" pure $ StringConstant info value String "NumericConstant" ->
src/Database/Sql/Util/Scope.hs view
@@ -44,8 +44,8 @@ import Prelude hiding ((&&), (||), not) import Data.Predicate.Class import Data.Maybe (mapMaybe)-import Data.Maybe.More (overJust) import Data.Either (lefts, rights)+import Data.Traversable (traverse) import Database.Sql.Type import qualified Data.List.NonEmpty as NonEmpty@@ -242,11 +242,11 @@ resolveSelectAndOrders :: Select RawNames a -> [Order RawNames a] -> Resolver (WithColumnsAndOrders (Select ResolvedNames)) a resolveSelectAndOrders Select{..} orders = do- (selectFrom', columns) <- overJust resolveSelectFrom selectFrom >>= \case+ (selectFrom', columns) <- traverse resolveSelectFrom selectFrom >>= \case Nothing -> pure (Nothing, []) Just (WithColumns selectFrom' columns) -> pure (Just selectFrom', columns) - selectTimeseries' <- overJust (bindColumns columns . resolveSelectTimeseries) selectTimeseries+ selectTimeseries' <- traverse (bindColumns columns . resolveSelectTimeseries) selectTimeseries maybeBindTimeSlice selectTimeseries' $ do selectCols' <- bindColumns columns $ resolveSelectColumns columns selectCols@@ -256,10 +256,10 @@ SelectScope{..} <- (\ f -> f columns selectedAliases) <$> asks selectScope - selectHaving' <- bindForHaving $ overJust resolveSelectHaving selectHaving- selectWhere' <- bindForWhere $ overJust resolveSelectWhere selectWhere- selectGroup' <- bindForGroup $ overJust (resolveSelectGroup selectedExprs) selectGroup- selectNamedWindow' <- bindForNamedWindow $ overJust resolveSelectNamedWindow selectNamedWindow+ selectHaving' <- bindForHaving $ traverse resolveSelectHaving selectHaving+ selectWhere' <- bindForWhere $ traverse resolveSelectWhere selectWhere+ selectGroup' <- bindForGroup $ traverse (resolveSelectGroup selectedExprs) selectGroup+ selectNamedWindow' <- bindForNamedWindow $ traverse resolveSelectNamedWindow selectNamedWindow orders' <- bindForOrder $ mapM (resolveOrder selectedExprs) orders let select = Select { selectCols = selectCols' , selectFrom = selectFrom'@@ -341,7 +341,7 @@ when (tableType /= Table) $ fail $ "delete only works on tables; can't delete on a " ++ show tableType let QTableName tableInfo _ _ = tableName bindColumns [(Just $ RTableRef fqtn table, map (\ (QColumnName () None column) -> RColumnRef $ QColumnName tableInfo (pure fqtn) column) columnsList)] $ do- expr' <- overJust resolveExpr expr+ expr' <- traverse resolveExpr expr pure $ Delete info tableName' expr' @@ -357,7 +357,7 @@ WithColumns createTableDefinition' columns <- resolveTableDefinition fqtn createTableDefinition bindColumns columns $ do- createTableExtra' <- overJust (resolveCreateTableExtra (Proxy :: Proxy d)) createTableExtra+ createTableExtra' <- traverse (resolveCreateTableExtra (Proxy :: Proxy d)) createTableExtra pure $ CreateTable { createTableName = createTableName' , createTableDefinition = createTableDefinition'@@ -413,7 +413,7 @@ resolveColumnDefinition :: ColumnDefinition d RawNames a -> Resolver (ColumnDefinition d ResolvedNames) a resolveColumnDefinition ColumnDefinition{..} = do- columnDefinitionDefault' <- overJust resolveExpr columnDefinitionDefault+ columnDefinitionDefault' <- traverse resolveExpr columnDefinitionDefault pure $ ColumnDefinition { columnDefinitionDefault = columnDefinitionDefault' , ..@@ -521,13 +521,13 @@ resolveExpr :: Expr RawNames a -> Resolver (Expr ResolvedNames) a resolveExpr (BinOpExpr info op lhs rhs) = BinOpExpr info op <$> resolveExpr lhs <*> resolveExpr rhs -resolveExpr (CaseExpr info whens else_) = CaseExpr info <$> mapM resolveWhen whens <*> overJust resolveExpr else_+resolveExpr (CaseExpr info whens else_) = CaseExpr info <$> mapM resolveWhen whens <*> traverse resolveExpr else_ where resolveWhen (when_, then_) = (,) <$> resolveExpr when_ <*> resolveExpr then_ resolveExpr (UnOpExpr info op expr) = UnOpExpr info op <$> resolveExpr expr resolveExpr (LikeExpr info op escape pattern expr) = do- escape' <- overJust (fmap Escape . resolveExpr . escapeExpr) escape+ escape' <- traverse (fmap Escape . resolveExpr . escapeExpr) escape pattern' <- Pattern <$> resolveExpr (patternExpr pattern) expr' <- resolveExpr expr pure $ LikeExpr info op escape' pattern' expr'@@ -548,7 +548,7 @@ resolveRange (from, to) = (,) <$> resolveExpr from <*> resolveExpr to resolveExpr (FunctionExpr info name distinct args params filter' over) =- FunctionExpr info name distinct <$> mapM resolveExpr args <*> mapM resolveParam params <*> overJust resolveFilter filter' <*> overJust resolveOverSubExpr over+ FunctionExpr info name distinct <$> mapM resolveExpr args <*> mapM resolveParam params <*> traverse resolveFilter filter' <*> traverse resolveOverSubExpr over where resolveParam (param, expr) = (param,) <$> resolveExpr expr -- T482568: expand named windows on resolve@@ -580,7 +580,7 @@ -> Resolver (WindowExpr ResolvedNames) a resolveWindowExpr WindowExpr{..} = do- windowExprPartition' <- overJust resolvePartition windowExprPartition+ windowExprPartition' <- traverse resolvePartition windowExprPartition windowExprOrder' <- mapM (resolveOrder []) windowExprOrder pure $ WindowExpr { windowExprPartition = windowExprPartition'@@ -794,7 +794,7 @@ resolveSelectTimeseries :: SelectTimeseries RawNames a -> Resolver (SelectTimeseries ResolvedNames) a resolveSelectTimeseries SelectTimeseries{..} = do- selectTimeseriesPartition' <- overJust resolvePartition selectTimeseriesPartition+ selectTimeseriesPartition' <- traverse resolvePartition selectTimeseriesPartition selectTimeseriesOrder' <- resolveExpr selectTimeseriesOrder pure $ SelectTimeseries { selectTimeseriesPartition = selectTimeseriesPartition'
+ stack.yaml view
@@ -0,0 +1,44 @@+# For more information, see: https://github.com/commercialhaskell/stack/blob/release/doc/yaml_configuration.md++# Specifies the GHC version and set of packages available (e.g., lts-3.5, nightly-2015-09-21, ghc-7.10.2)+resolver: lts-10.4++# Local packages, usually specified by relative directory name+packages:+- '.'+- 'test'+- 'demo'+- 'misc/predicate-class'+- 'dialects/vertica'+- 'dialects/hive'+- 'dialects/presto'++docker:+ enable: true++# Packages to be pulled from upstream that are not in the resolver (e.g., acme-missiles-0.3)+extra-deps: [ 'test-framework-quickcheck-0.3.0'+ , 'file-location-0.4.9.1'+ , 'fixed-list-0.1.6'+ ]++# Override default flag values for local packages and extra-deps+flags: {}++# Extra package databases containing global packages+extra-package-dbs: []++# Control whether we use the GHC we find on the path+# system-ghc: true++# Require a specific version of stack, using version ranges+# require-stack-version: -any # Default+# require-stack-version: >= 0.1.4.0++# Override the architecture used by stack, especially useful on Windows+# arch: i386+# arch: x86_64++# Extra directories used by stack for building+# extra-include-dirs: [/path/to/dir]+# extra-lib-dirs: [/path/to/dir]