1
Fork 0
simple-sql-parser/website/index.asciidoc
2019-07-08 18:40:55 +01:00

405 lines
13 KiB
Plaintext

:toc: right
:sectnums:
:toclevels: 10
:source-highlighter: pygments
= simple-sql-parser
== Overview
A parser for SQL in Haskell. Also includes a pretty printer which
formats output nicely. Current target is to parse most SQL:2011
queries, plus a good subset of DDL, non-query DML, transaction
management, access control and session management.
This is the documentation for version 0.5.0. Documentation for other
versions is available here:
http://jakewheat.github.io/simple-sql-parser/.
Status: covers a lot of queries already, but the public API is
probably not very stable, since adding support for all the
not-yet-supported ANSI SQL syntax, then other dialects of SQL is
likely to change the abstract syntax types considerably.
Release 0.5.0 is a checkpoint release since there hasn't been a
release for a while.
Tested with GHC 8.6.5, 8.4.4 and 8.2.1.
== Links
* Haddock: link:haddock/index.html[]
* Supported SQL: link:supported_sql.html[]
* Test cases: link:test_cases.html[simple-sql-parser test cases]
* Homepage: http://jakewheat.github.io/simple-sql-parser/latest
* Hackage: http://hackage.haskell.org/package/simple-sql-parser
* Repository: https://github.com/JakeWheat/simple-sql-parser
* Bug tracker: https://github.com/JakeWheat/simple-sql-parser/issues
* Changes: https://github.com/JakeWheat/simple-sql-parser/blob/master/changelog
* Other versions: http://jakewheat.github.io/simple-sql-parser/
* Parent project: http://jakewheat.github.io/
* Contact: +++jakewheatmail@gmail.com+++
The simple-sql-parser is a lot less simple than it used to be. If you
just need to parse much simpler SQL than this, or want to start with a
simpler parser and modify it slightly, you could also look at the
basic query parser in the intro_to_parsing project, the code is here:
link:https://github.com/JakeWheat/intro_to_parsing/blob/master/SimpleSQLQueryParser0.lhs[SimpleSQLQueryParser].
== Feature support
* query expressions
** select lists
** from clause
** where clause
** group by clause
** having clause
** order by clause
** offset and fetch
** set operators
** common table expressions
** wide range of scalar expressions
* DDL (ansi dialect)
** create,drop schema
** create, alter, drop table
** create, drop view
** create, alter, drop domain
** create, drop assertion
** create, alter, drop sequence
* non-query DML
** delete
** truncate
** insert
** update
* Access control
** grant, revoke - permissions and roles
** create, drop role
* Transaction management
** begin, commit, rollback, savepoints
See the link:supported_sql.html[] page for details on
the supported SQL.
Here is all the link:test_cases.html[simple-sql-parser test cases]
rendered in a webpage so you can get an idea of what it supports.
== Examples
Simple expression:
[source,sql]
----
select a + b * c
----
Parsed AST:
[source,haskell]
----
Select{qeSetQuantifier = All,
qeSelectList =
[(BinOp (Iden (Name "a")) (Name "+")
(BinOp (Iden (Name "b")) (Name "*") (Iden (Name "c"))),
Nothing)],
qeFrom = [], qeWhere = Nothing, qeGroupBy = [], qeHaving = Nothing,
qeOrderBy = [], qeOffset = Nothing, qeFetchFirst = Nothing}
----
TPC-H query 21:
[source,sql]
----
select
s_name,
count(*) as numwait
from
supplier,
lineitem l1,
orders,
nation
where
s_suppkey = l1.l_suppkey
and o_orderkey = l1.l_orderkey
and o_orderstatus = 'F'
and l1.l_receiptdate > l1.l_commitdate
and exists (
select
*
from
lineitem l2
where
l2.l_orderkey = l1.l_orderkey
and l2.l_suppkey <> l1.l_suppkey
)
and not exists (
select
*
from
lineitem l3
where
l3.l_orderkey = l1.l_orderkey
and l3.l_suppkey <> l1.l_suppkey
and l3.l_receiptdate > l3.l_commitdate
)
and s_nationkey = n_nationkey
and n_name = 'INDIA'
group by
s_name
order by
numwait desc,
s_name
fetch first 100 rows only;
----
Parsed:
[source,haskell]
----
Select{qeSetQuantifier = All,
qeSelectList =
[(Iden (Name "s_name"), Nothing),
(App (Name "count") [Star], Just (Name "numwait"))],
qeFrom =
[TRSimple (Name "supplier"),
TRAlias (TRSimple (Name "lineitem")) (Alias (Name "l1") Nothing),
TRSimple (Name "orders"), TRSimple (Name "nation")],
qeWhere =
Just
(BinOp
(BinOp
(BinOp
(BinOp
(BinOp
(BinOp
(BinOp
(BinOp (Iden (Name "s_suppkey")) (Name "=")
(BinOp (Iden (Name "l1")) (Name ".")
(Iden (Name "l_suppkey"))))
(Name "and")
(BinOp (Iden (Name "o_orderkey")) (Name "=")
(BinOp (Iden (Name "l1")) (Name ".")
(Iden (Name "l_orderkey")))))
(Name "and")
(BinOp (Iden (Name "o_orderstatus")) (Name "=") (StringLit "F")))
(Name "and")
(BinOp
(BinOp (Iden (Name "l1")) (Name ".") (Iden (Name "l_receiptdate")))
(Name ">")
(BinOp (Iden (Name "l1")) (Name ".")
(Iden (Name "l_commitdate")))))
(Name "and")
(SubQueryExpr SqExists
(Select{qeSetQuantifier = All, qeSelectList = [(Star, Nothing)],
qeFrom =
[TRAlias (TRSimple (Name "lineitem"))
(Alias (Name "l2") Nothing)],
qeWhere =
Just
(BinOp
(BinOp
(BinOp (Iden (Name "l2")) (Name ".")
(Iden (Name "l_orderkey")))
(Name "=")
(BinOp (Iden (Name "l1")) (Name ".")
(Iden (Name "l_orderkey"))))
(Name "and")
(BinOp
(BinOp (Iden (Name "l2")) (Name ".")
(Iden (Name "l_suppkey")))
(Name "<>")
(BinOp (Iden (Name "l1")) (Name ".")
(Iden (Name "l_suppkey"))))),
qeGroupBy = [], qeHaving = Nothing, qeOrderBy = [],
qeOffset = Nothing, qeFetchFirst = Nothing})))
(Name "and")
(PrefixOp (Name "not")
(SubQueryExpr SqExists
(Select{qeSetQuantifier = All, qeSelectList = [(Star, Nothing)],
qeFrom =
[TRAlias (TRSimple (Name "lineitem"))
(Alias (Name "l3") Nothing)],
qeWhere =
Just
(BinOp
(BinOp
(BinOp
(BinOp (Iden (Name "l3")) (Name ".")
(Iden (Name "l_orderkey")))
(Name "=")
(BinOp (Iden (Name "l1")) (Name ".")
(Iden (Name "l_orderkey"))))
(Name "and")
(BinOp
(BinOp (Iden (Name "l3")) (Name ".")
(Iden (Name "l_suppkey")))
(Name "<>")
(BinOp (Iden (Name "l1")) (Name ".")
(Iden (Name "l_suppkey")))))
(Name "and")
(BinOp
(BinOp (Iden (Name "l3")) (Name ".")
(Iden (Name "l_receiptdate")))
(Name ">")
(BinOp (Iden (Name "l3")) (Name ".")
(Iden (Name "l_commitdate"))))),
qeGroupBy = [], qeHaving = Nothing, qeOrderBy = [],
qeOffset = Nothing, qeFetchFirst = Nothing}))))
(Name "and")
(BinOp (Iden (Name "s_nationkey")) (Name "=")
(Iden (Name "n_nationkey"))))
(Name "and")
(BinOp (Iden (Name "n_name")) (Name "=") (StringLit "INDIA"))),
qeGroupBy = [SimpleGroup (Iden (Name "s_name"))],
qeHaving = Nothing,
qeOrderBy =
[SortSpec (Iden (Name "numwait")) Desc NullsOrderDefault,
SortSpec (Iden (Name "s_name")) Asc NullsOrderDefault],
qeOffset = Nothing, qeFetchFirst = Just (NumLit "100")})
----
Output from the simple-sql-parser pretty printer:
[source,sql]
----
select s_name, count(*) as numwait
from supplier,
lineitem as l1,
orders,
nation
where s_suppkey = l1.l_suppkey
and o_orderkey = l1.l_orderkey
and o_orderstatus = 'F'
and l1.l_receiptdate > l1.l_commitdate
and exists (select *
from lineitem as l2
where l2.l_orderkey = l1.l_orderkey
and l2.l_suppkey <> l1.l_suppkey)
and not exists (select *
from lineitem as l3
where l3.l_orderkey = l1.l_orderkey
and l3.l_suppkey <> l1.l_suppkey
and l3.l_receiptdate > l3.l_commitdate)
and s_nationkey = n_nationkey
and n_name = 'INDIA'
group by s_name
order by numwait desc, s_name
fetch first 100 rows only;
----
Parsing some SQL and printing the AST:
[source,haskell]
----
import System.Environment
import Text.Show.Pretty
import System.IO
import Language.SQL.SimpleSQL.Parse
(parseStatements
,ParseError
,peFormattedError)
import Language.SQL.SimpleSQL.Syntax (ansi2011, Statement)
main :: IO ()
main = do
args <- getArgs
case args of
[] -> do
-- read from stdin
c <- getContents
doIt c
["-s", sql] -> do
-- parse arg given
doIt sql
[f] ->
-- read file
withFile f ReadMode (\h -> do
x <- hGetContents h
doIt x)
_ -> do
putStrLn "use no arguments to stream sql from stdin, e.g.:\n\
\ cat some.sql | SimpleSQLParserExample\n\
\n\
\use -s to parse sql on command line, e.g.:\n\
\ SimpleSQLParserExample -s \"select * from t\"\n\
\use a single arg to parse a file, e.g.\n\
\ SimpleSQLParserExample some.sql"
doIt :: String -> IO ()
doIt src = do
let parsed :: Either ParseError [Statement]
parsed = parseStatements ansi2011 "" Nothing src
either (error . peFormattedError)
(putStrLn . ppShow)
parsed
----
== Installation
Installing the latest release from Hackage.
----
cabal v2-update && cabal v2-install simple-sql-parser
----
Working with the latest development version:
----
git clone https://github.com/JakeWheat/simple-sql-parser.git
cd simple-sql-parser
cabal v2-build
----
=== Running the tests
Get the source using 'cabal unpack' or 'git clone', then change to the
source directory.
You can run the tests using cabal:
----
cabal v2-test
----
Or you can run them directly which gives more options. The tests use
tasty, which provides the command line options.
----
cabal v2-build --enable-tests
dist-newstyle/build/x86_64-linux/ghc-8.6.5/simple-sql-parser-0.5.0/t/Tests/build/Tests/Tests
----
--hide-successes is a good option to use:
----
dist-newstyle/build/x86_64-linux/ghc-8.6.5/simple-sql-parser-0.5.0/t/Tests/build/Tests/Tests --hide-successes
----
Right now this option doesn't seem to be working for me - it still
outputs a huge amount of text when all the tests pass.
== Reporting bugs
Please report bugs here:
https://github.com/JakeWheat/simple-sql-parser/issues
A good bug report (or feature request) should have an example of the
SQL which is failing.
Feature requests are welcome, but please note that there is no-one
generally available to work on these, so you should either make a pull
request, or find someone willing to write the fixes and make a pull
request.