1
Fork 0

overhaul website

This commit is contained in:
Jake Wheat 2024-01-26 16:29:58 +00:00
parent 45669ed7d3
commit 681cbfc416
19 changed files with 1008 additions and 1584 deletions

View file

@ -3,6 +3,7 @@
# there is no real reason to actually use the makefile except for a
# very small amount of convenience, apart from the website build
# and website consistency checks
.PHONY : build
build :
@ -19,6 +20,7 @@ test-coverage :
.PHONY : clean
clean :
cabal clean
cd website && cabal clean
rm -Rf build/
.PHONY : parserexe
@ -26,7 +28,7 @@ parserexe :
cabal build -fparserexe SimpleSQLParserTool
.PHONY : all
all : build test parserexe
all : build test parserexe website
###############################################
@ -39,39 +41,33 @@ all : build test parserexe
website : website-non-haddock build-haddock
.PHONY : website-non-haddock
website-non-haddock : build/main.css build/ocean.css build/index.html build/supported_sql.html \
build/test_cases.html build/contributing.html
website-non-haddock : build/main.css build/main1.css build/index.html \
build/supported_sql.html build/test_cases.html
build/main.css : website/main.css
mkdir -p build
cp website/main.css build
build/ocean.css : website/ocean.css
# todo: combine main and main1, change the one bit they can't share with sed
# to create the additional main1 as part of the build
build/main1.css : website/main1.css
mkdir -p build
cp website/ocean.css build
cp website/main1.css build
build/index.html : website/index.asciidoc website/AddLinks.hs
asciidoctor website/index.asciidoc -o - | cabal -v0 exec runhaskell website/AddLinks.hs > build/index.html
build/index.html : website/index.md website/template.pandoc
mkdir -p build
pandoc -s --template website/template.pandoc -V toc-title:"Table of contents" -c main.css -f markdown -t html --toc=true --metadata title="Simple SQL Parser" website/index.md > build/index.html
build/supported_sql.html : website/supported_sql.asciidoc website/AddLinks.hs
asciidoctor website/supported_sql.asciidoc -o - | cabal -v0 exec runhaskell website/AddLinks.hs > build/supported_sql.html
build/supported_sql.html : website/supported_sql.md website/template.pandoc
mkdir -p build
pandoc -s --template website/template.pandoc -V toc-title:"Table of contents" -c main.css -f markdown -t html --toc=true --metadata title="Simple SQL Parser supported SQL" website/supported_sql.md > build/supported_sql.html
build/contributing.html : website/contributing.asciidoc website/AddLinks.hs
asciidoctor website/contributing.asciidoc -o - | cabal -v0 exec runhaskell website/AddLinks.hs > build/contributing.html
build/test_cases.html : website/RenderTestCases.hs
cabal -v0 exec runhaskell -- --ghc-arg=-package=pretty-show -itools website/RenderTestCases.hs > build/test_cases.asciidoc
asciidoctor build/test_cases.asciidoc -o - | \
sed -e "s/max-width:62\.5em//g" > build/test_cases.html
# TODO: reduce the text size on the test cases page
# TODO: use scrollbars inside the tables
# TODO: make the tables autowidth
# -e "s/(code.*)font-size:1em/\1font-size:0.8em/g"
rm build/test_cases.asciidoc
# the tests don't render right if the TestCases aren't all at the same level
# of group nesting, which should be fixed - if this isn't the case, it
# will silently not render some of the tests
build/test_cases.html : website/RenderTestCases.hs website/template1.pandoc
mkdir -p build
# no idea why not using --disable-optimisation on cabal build, but putting -O0
# in the cabal file (and then cabal appears to say it's still using -O1
# is faster
cd website/ && cabal build RenderTestCases && cabal run RenderTestCases | pandoc -s -N --template template1.pandoc -V toc-title:"Simple SQL Parser test case examples" -c main1.css -f markdown -t html --toc=true --metadata title="Simple SQL Parse test case examples" > ../build/test_cases.html
# works here, but not in a recipe. amazing
# GHC_VER="$(shell ghc --numeric-version)"
@ -85,3 +81,13 @@ build-haddock :
$(eval GHC_VER="$(shell ghc --numeric-version)")
$(eval SSP_VER="$(shell cat simple-sql-parser.cabal |grep -P '^version:' | awk '{print $$2}')")
cp -R dist-newstyle/build/x86_64-linux/ghc-${GHC_VER}/simple-sql-parser-${SSP_VER}/doc/html/simple-sql-parser/* build/haddock/
# check the website pages code snippets
.PHONY : doctool
doctool :
cabal build -fparserexe SimpleSQLParserTool
silverbane website/index.md
.PHONY : really-all
really-all : build test parserexe website doctool

View file

@ -1,40 +1,4 @@
:toc: right
:sectnums:
:toclevels: 10
:source-highlighter: pygments
= Contributing
== Contributing to simple sql parser
Guidelines:
If you add something to the public api, follow the pattern already set for haddock.
If something isn't ANSI SQL, add it under a dialect flag which isn't enabled in the ANSI dialect.
If you add dialect flags, add them to the appropriate dialects, create a new one if the dialect doesn't already exist. The current dialects are very much WIP, improve them if you see a gap you need fixing.
Add tests for anything you add, modify or fix.
Tests should provide examples of SQL that now parses and what it parses to.
When it's ready, make a pull request on github.
== Key design notes
The parsing is done using the Megaparsec library. The parser uses a separate lexer, also implemented with Megaparsec.
The dialect system was introduced as a way to deal with a messy problem. Users of the library are able to decide what to consider reserved keywords - this is better than asking them to modify the library source.
A tradeoff is all code that uses the library needs to be prepared to deal with/ignore parts of the abstract syntax which supports all features from all dialects.
== Legal business
All contributions remain copyright of the person who wrote them. By contributing to the main repository, including but not limited to via a pull request, the copyright holder agrees to license those contributions under the BSD 3-clause license. This includes all contributions already made to the project.
== Release checklist
# Release checklist
Check the version in the cabal file - update it if it hasn't already been updated. git grep for any other mentions of the version number that need updating.
@ -42,17 +6,26 @@ Update the changelog, use git diff or similar to try to reduce the chance of mis
Run the tests (if any fail at the point of thinking about a release, then something has gone horribly wrong ...)
----
~~~~
cabal test
----
~~~~
Generate the website
Do the cabal checks:
----
make website
----
~~~~
cabal update
cabal outdated
cabal check
~~~~
It's a bit wonky so try running it a second time if it fails.
Check everything:
~~~~
make clean
make really-all
~~~~
TODO: you need silverbane to check the examples in the index.md.
Then:
@ -60,64 +33,55 @@ Then:
* check all the tests are rendered on the example page -> need to find a robust way of doing this, because there are huge numbers and it's impossible to eyeball and tell if it's good unless you somehow spot a problem.
* check the examples on the main page to check if they need updating
Do the cabal checks:
----
cabal update
cabal outdated
cabal check
----
Update stack.yaml to the latest lts - check this page: https://www.stackage.org/ . While updating, check the extra-deps field, if there are any there, see if they can be removed.
Install latest stack and check it works - maybe the stack.yaml file needs a tweak, maybe the cabal file.
----
~~~~
ghcup list
ghcup install stack [LATEST FROM THE LIST]
ghcup install stack [latest from the list on stackage.org]
stack test
----
~~~~
Run the tests on the previous 2 ghcs latest point releases, and the latest ghc, each with the latest cabal-install they support (e.g. as of the start of 2024, these three ghc versions are 9.8.1, 9.6.4, 9.4.8). This is now trivial to do with ghcup, amazing progress in Haskell tools in recent years.
Build the release tarball, run a test with an example using this tarball:
----
~~~~
cabal sdist
mkdir temp-build
# get the path to the tar.gz from the output of cabal sdist
cp simple-sql-parser/main/dist-newstyle/sdist/simple-sql-parser-0.X.X.tar.gz temp-build
cd temp-build
cabal init -n
cp ../tools/SimpleSqlParserTool.hs app/Main.hs
----
cp ../examples/SimpleSQLParserTool.hs app/Main.hs
~~~~
Add these to the build-depends: for the Main in the new cabal file, temp-build.cabal:
Add these to the build-depends: for the Main in temp-build.cabal:
----
~~~~
simple-sql-parser == 0.X.X,
pretty-show,
text
----
~~~~
Add a cabal.project file containing:
----
Add a cabal.project.local file containing:
~~~~
packages:
./
./simple-sql-parser-0.X.X.tar.gz
----
~~~~
Run the test:
----
~~~~
cabal run temp-build -- parse -c "select 1"
----
~~~~
Example of output on success:
----
~~~~
$ cabal run temp-build -- parse -c "select 1"
Build profile: -w ghc-9.8.1 -O1
In order, the following will be built (use -v for more details):
@ -145,7 +109,7 @@ Building executable 'temp-build' for temp-build-0.1.0.0..
, qeFetchFirst = Nothing
}
]
----
~~~~
TODO: hlint?, how to do a spell check, what about automatic code formatting?
@ -156,4 +120,6 @@ Upload candidate to hackage, run a test with example using this package
If all good, release the candidate - a button on the hackage website.
when and how do you add a tag to git?
Todo: try to turn as much of this into a script, with a nice report as possible, order this list properly, say what you need to check in more detail, say what else you need to redo if any steps need actions.

View file

@ -1,35 +0,0 @@
import System.IO
import System.Environment
main :: IO ()
main = do
[a] <- getArgs
r <- readFile a
let ls = lines r
a = noAdjacentBlankLines ls
b = concat $ combineGroups $ group [] a
putStrLn $ unlines b
noAdjacentBlankLines [] = []
noAdjacentBlankLines [a] = [a]
noAdjacentBlankLines ("":xs@("":_)) = noAdjacentBlankLines xs
noAdjacentBlankLines (x:xs) = x:noAdjacentBlankLines xs
group :: [String] -> [String] -> [[String]]
group acc [] = [acc]
group acc ("":xs) = reverse ("":acc) : group [] xs
group acc (x:xs) = group (x : acc) xs
combineGroups :: [[String]] -> [[String]]
combineGroups [] = []
combineGroups (x@(('<':_):_):xs) | gs <- map trim x
, ns <- trim $ unwords gs
, length ns < 80 = [ns ++ "\n"] : combineGroups xs
combineGroups (x:xs) = x:combineGroups xs
trim :: String -> String
trim = x . x
where
x = dropWhile (==' ') . reverse

View file

@ -1,24 +0,0 @@
--import System.IO
import System.Environment
main :: IO ()
main = do
[a] <- getArgs
r <- readFile a
let ls = lines r
putStrLn $ unlines $ map dedupeSpaces ls
dedupeSpaces :: String -> String
dedupeSpaces [] = []
-- don't start until after the leading spaces
-- including literate haskell source lines
dedupeSpaces xs@(x:_) | x `notElem` " >" = dedupeSpaces' xs
dedupeSpaces (x:xs) = x : dedupeSpaces xs
dedupeSpaces' :: String -> String
dedupeSpaces' (' ':xs@(' ':_)) = dedupeSpaces' xs
dedupeSpaces' (x:xs) = x : dedupeSpaces' xs
dedupeSpaces' [] = []

View file

@ -1,7 +0,0 @@
import Language.SQL.SimpleSQL.ErrorMessages
main :: IO ()
main = putStrLn $ pExprs valueExpressions queryExpressions

View file

@ -1,49 +0,0 @@
-- Simple example to show parsing some SQL then pretty printing the AST
{-# LANGUAGE OverloadedStrings #-}
import System.Environment
import Text.Show.Pretty
import System.IO
import Language.SQL.SimpleSQL.Parse
(parseStatements
,ParseError
,prettyError
,ansi2011)
import Language.SQL.SimpleSQL.Syntax (Statement)
import qualified Data.Text as T
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 (T.pack src)
either (error . T.unpack . prettyError)
(putStrLn . ppShow)
parsed

View file

@ -1,34 +0,0 @@
-- Little hack to add links to the navigation bars
main :: IO ()
main = interact addLinks
addLinks :: String -> String
addLinks [] = error "not found"
addLinks ('<':'/':'u':'l':'>':'\n':'<':'/':'d':'i':'v':'>':xs) =
"</ul>" ++ linkSection ++ "\n</div>" ++ xs
addLinks (x:xs) = x : addLinks xs
linkSection :: String
linkSection =
"<hr />\n\
\<ul class=\"sectlevel1\">\n\
\<div id=\"toctitle\">Links</div>\n\
\<li><a href=\"index.html\">Index</a></li>\n\
\<li><a href='haddock/index.html'>Haddock</li>\n\
\<li><a href=\"supported_sql.html\" class=\"bare\">Supported SQL</a></li>\n\
\<li><a href=\"test_cases.html\">Test cases</a></li>\n\
\<li><a href=\"contributing.html\">Contributing</a></li>\n\
\</ul>\n\
\<br />\n\
\<ul class=\"sectlevel1\">\n\
\<li><a href=\"http://jakewheat.github.io/simple-sql-parser/latest\" class=\"bare\">Homepage</a></li>\n\
\<li><a href=\"http://hackage.haskell.org/package/simple-sql-parser\" class=\"bare\">Hackage</a></li>\n\
\<li><a href=\"https://github.com/JakeWheat/simple-sql-parser\" class=\"bare\">Repository</a></li>\n\
\<li><a href=\"https://github.com/JakeWheat/simple-sql-parser/issues\" class=\"bare\">Bug tracker</a></li>\n\
\<li><a href=\"https://github.com/JakeWheat/simple-sql-parser/blob/master/changelog\" class=\"bare\">Changes</a></li>\n\
\<li><a href=\"http://jakewheat.github.io/simple-sql-parser/\" class=\"bare\">Other versions</a></li>\n\
\</li><li>jakewheat@tutanota.com</li>\n\
\</ul>\n"

View file

@ -1,91 +1,82 @@
-- Converts the test data to asciidoc
-- Converts the test data to markdown
-- it uses raw html for the table parts
{-# LANGUAGE OverloadedStrings #-}
import Language.SQL.SimpleSQL.Tests
import Text.Show.Pretty
import Control.Monad.State
import Text.Show.Pretty (ppShow)
import qualified Language.SQL.SimpleSQL.Parse as P
import qualified Language.SQL.SimpleSQL.Lex as L
import Data.List
import Control.Monad (when, unless)
import Data.Text (Text)
import qualified Data.Text as T
import Prelude hiding (putStrLn)
import Data.Text.IO (putStrLn)
import qualified Data.Text.Lazy as L
import qualified Data.Text.Lazy.IO as L
data TableItem = Heading Int Text
| Row Text Text
data TableItem = Heading Int L.Text
| Row L.Text L.Text
doc :: Int -> TestItem -> [TableItem]
-- filter out some groups of tests
doc n (Group nm _) | "generated" `T.isInfixOf` nm = []
doc _ (Group nm _) | "generated" `T.isInfixOf` nm = []
doc n (Group nm is) =
Heading n nm
Heading n (L.fromStrict nm)
: concatMap (doc (n + 1)) is
doc _ (TestScalarExpr _ str e) =
[Row str (T.pack $ ppShow e)]
[Row (L.fromStrict str) (L.pack $ ppShow e)]
doc _ (TestQueryExpr _ str e) =
[Row str (T.pack $ ppShow e)]
[Row (L.fromStrict str) (L.pack $ ppShow e)]
doc _ (TestStatement _ str e) =
[Row str (T.pack $ ppShow e)]
[Row (L.fromStrict str) (L.pack $ ppShow e)]
doc _ (TestStatements _ str e) =
[Row str (T.pack $ ppShow e)]
[Row (L.fromStrict str) (L.pack $ ppShow e)]
doc _ (ParseQueryExpr d str) =
[Row str (showResult $ P.parseQueryExpr d "" Nothing str)]
[Row (L.fromStrict str) (showResult $ P.parseQueryExpr d "" Nothing str)]
doc _ (ParseQueryExprFails d str) =
[Row str (showResult $ P.parseQueryExpr d "" Nothing str)]
[Row (L.fromStrict str) (showResult $ P.parseQueryExpr d "" Nothing str)]
doc _ (ParseScalarExprFails d str) =
[Row str (showResult $ P.parseScalarExpr d "" Nothing str)]
[Row (L.fromStrict str) (showResult $ P.parseScalarExpr d "" Nothing str)]
doc _ (LexTest d str t) =
[Row str (showResultL $ L.lexSQL d "" Nothing str)]
doc _ (LexTest d str _) =
[Row (L.fromStrict str) (showResultL $ L.lexSQL d "" Nothing str)]
doc _ (LexFails d str) =
[Row str (showResultL $ L.lexSQL d "" Nothing str)]
[Row (L.fromStrict str) (showResultL $ L.lexSQL d "" Nothing str)]
showResult :: Show a => Either P.ParseError a -> Text
showResult = either (("Left\n" <>) . P.prettyError) (T.pack . ppShow)
showResult :: Show a => Either P.ParseError a -> L.Text
showResult = either (("Left\n" <>) . L.fromStrict . P.prettyError) (L.pack . ppShow)
showResultL :: Show a => Either L.ParseError a -> Text
showResultL = either (("Left\n" <>) . L.prettyError) (T.pack . ppShow)
showResultL :: Show a => Either L.ParseError a -> L.Text
showResultL = either (("Left\n" <>) . L.fromStrict . L.prettyError) (L.pack . ppShow)
-- TODO: should put the dialect in the html output
render :: [TableItem] -> IO ()
render :: [TableItem] -> L.Text
render = go False
where
go t (Heading level title : is) = do
when t $ putStrLn "|==="
go _t (Heading level title : is) =
"</table>\n"
<>
-- slight hack
when (level > 1) $
putStrLn $ "\n" <> T.replicate level "=" <> " " <> title
go False is
go t (Row sql hask : is) = do
unless t $ putStrLn "[cols=\"2\"]\n|==="
let sql' = "\n[source,sql]\n----\n" <> sql <> "\n----\n"
hask' = "\n[source,haskell]\n----\n" <> hask <> "\n----\n"
putStrLn $ "a| " <> escapePipe sql'
<> "a| " <> escapePipe hask' <> " "
go True is
go t [] = when t $ putStrLn "|==="
escapePipe t = T.pack $ escapePipe' $ T.unpack t
(if (level > 1)
then "\n" <> L.replicate (fromIntegral $ level - 1) "#" <> " " <> title <> "\n"
else "")
<> go False is
go t (Row sql hask : is) =
(if (not t)
then "<table>\n"
else "")
<> let sql' = "\n~~~~{.sql}\n" <> sql <> "\n~~~~\n"
hask' = "\n~~~~{.haskell}\n" <> hask <> "\n~~~~\n"
in "<tr><td>\n" <> sql' <> "</td><td>\n" <> hask' <> "</td></tr>\n"
<> go True is
go _t [] = "</table>\n"
{-escapePipe t = T.pack $ escapePipe' $ T.unpack t
escapePipe' [] = []
escapePipe' ('\\':'|':xs) = '\\' : '\\' : '\\' : '|' : escapePipe' xs
escapePipe' ('|':xs) = '\\' : '|' : escapePipe' xs
escapePipe' (x:xs) = x : escapePipe' xs
escapePipe' (x:xs) = x : escapePipe' xs-}
main :: IO ()
main = do
putStrLn "\n:toc:\n\
\:toc-placement: macro\n\
\:sectnums:\n\
\:toclevels: 10\n\
\:sectnumlevels: 10\n\
\:source-highlighter: pygments\n\n\
\= simple-sql-parser examples/test cases\n\n\
\toc::[]\n"
render $ doc 1 testData
main = L.putStrLn $ render $ doc 1 testData

View file

@ -1,438 +0,0 @@
: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.7.0. Documentation for other
versions is available here:
http://jakewheat.github.io/simple-sql-parser/.
Status: usable for parsing a substantial amount of SQL. Adding support
for new SQL is relatively easy. Expect a little bit of churn on the AST
types when support for new SQL features is added.
This version is tested with GHC 9.8.1, 9.6.4, and 9.4.8.
== 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 = SQDefault
, qeSelectList =
[ ( BinOp
(Iden [ Name Nothing "a" ])
[ Name Nothing "+" ]
(BinOp
(Iden [ Name Nothing "b" ])
[ Name Nothing "*" ]
(Iden [ Name Nothing "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 = SQDefault
, qeSelectList =
[ ( Iden [ Name Nothing "s_name" ] , Nothing )
, ( App [ Name Nothing "count" ] [ Star ]
, Just (Name Nothing "numwait")
)
]
, qeFrom =
[ TRSimple [ Name Nothing "supplier" ]
, TRAlias
(TRSimple [ Name Nothing "lineitem" ])
(Alias (Name Nothing "l1") Nothing)
, TRSimple [ Name Nothing "orders" ]
, TRSimple [ Name Nothing "nation" ]
]
, qeWhere =
Just
(BinOp
(BinOp
(BinOp
(BinOp
(BinOp
(BinOp
(BinOp
(BinOp
(Iden [ Name Nothing "s_suppkey" ])
[ Name Nothing "=" ]
(Iden [ Name Nothing "l1" , Name Nothing "l_suppkey" ]))
[ Name Nothing "and" ]
(BinOp
(Iden [ Name Nothing "o_orderkey" ])
[ Name Nothing "=" ]
(Iden [ Name Nothing "l1" , Name Nothing "l_orderkey" ])))
[ Name Nothing "and" ]
(BinOp
(Iden [ Name Nothing "o_orderstatus" ])
[ Name Nothing "=" ]
(StringLit "'" "'" "F")))
[ Name Nothing "and" ]
(BinOp
(Iden [ Name Nothing "l1" , Name Nothing "l_receiptdate" ])
[ Name Nothing ">" ]
(Iden [ Name Nothing "l1" , Name Nothing "l_commitdate" ])))
[ Name Nothing "and" ]
(SubQueryExpr
SqExists
Select
{ qeSetQuantifier = SQDefault
, qeSelectList = [ ( Star , Nothing ) ]
, qeFrom =
[ TRAlias
(TRSimple [ Name Nothing "lineitem" ])
(Alias (Name Nothing "l2") Nothing)
]
, qeWhere =
Just
(BinOp
(BinOp
(Iden [ Name Nothing "l2" , Name Nothing "l_orderkey" ])
[ Name Nothing "=" ]
(Iden [ Name Nothing "l1" , Name Nothing "l_orderkey" ]))
[ Name Nothing "and" ]
(BinOp
(Iden [ Name Nothing "l2" , Name Nothing "l_suppkey" ])
[ Name Nothing "<>" ]
(Iden [ Name Nothing "l1" , Name Nothing "l_suppkey" ])))
, qeGroupBy = []
, qeHaving = Nothing
, qeOrderBy = []
, qeOffset = Nothing
, qeFetchFirst = Nothing
}))
[ Name Nothing "and" ]
(PrefixOp
[ Name Nothing "not" ]
(SubQueryExpr
SqExists
Select
{ qeSetQuantifier = SQDefault
, qeSelectList = [ ( Star , Nothing ) ]
, qeFrom =
[ TRAlias
(TRSimple [ Name Nothing "lineitem" ])
(Alias (Name Nothing "l3") Nothing)
]
, qeWhere =
Just
(BinOp
(BinOp
(BinOp
(Iden [ Name Nothing "l3" , Name Nothing "l_orderkey" ])
[ Name Nothing "=" ]
(Iden
[ Name Nothing "l1" , Name Nothing "l_orderkey" ]))
[ Name Nothing "and" ]
(BinOp
(Iden [ Name Nothing "l3" , Name Nothing "l_suppkey" ])
[ Name Nothing "<>" ]
(Iden
[ Name Nothing "l1" , Name Nothing "l_suppkey" ])))
[ Name Nothing "and" ]
(BinOp
(Iden [ Name Nothing "l3" , Name Nothing "l_receiptdate" ])
[ Name Nothing ">" ]
(Iden
[ Name Nothing "l3" , Name Nothing "l_commitdate" ])))
, qeGroupBy = []
, qeHaving = Nothing
, qeOrderBy = []
, qeOffset = Nothing
, qeFetchFirst = Nothing
})))
[ Name Nothing "and" ]
(BinOp
(Iden [ Name Nothing "s_nationkey" ])
[ Name Nothing "=" ]
(Iden [ Name Nothing "n_nationkey" ])))
[ Name Nothing "and" ]
(BinOp
(Iden [ Name Nothing "n_name" ])
[ Name Nothing "=" ]
(StringLit "'" "'" "INDIA")))
, qeGroupBy = [ SimpleGroup (Iden [ Name Nothing "s_name" ]) ]
, qeHaving = Nothing
, qeOrderBy =
[ SortSpec (Iden [ Name Nothing "numwait" ]) Desc NullsOrderDefault
, SortSpec
(Iden [ Name Nothing "s_name" ]) DirDefault 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]
----
{-# LANGUAGE OverloadedStrings #-}
import System.Environment
import Text.Show.Pretty
import System.IO
import Language.SQL.SimpleSQL.Parse
(parseStatements
,ParseError
,prettyError
,ansi2011)
import Language.SQL.SimpleSQL.Syntax (Statement)
import qualified Data.Text as T
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 (T.pack src)
either (error . T.unpack . prettyError)
(putStrLn . ppShow)
parsed
----
== Installation
Use cabal, stack or your usual system to work with the released package.
Working with the latest development version:
----
git clone https://github.com/JakeWheat/simple-sql-parser.git
cd simple-sql-parser
cabal 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 test
----
Or you can run them directly which gives more options. The tests use
tasty, which provides the command line options. --hide-successes
with --ansi-tricks=false so it works is a good option to use:
----
cabal run test:Tests -- --hide-successes --ansi-tricks=false
----
== 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.
There is a related tutorial on implementing a SQL parser here:
http://jakewheat.github.io/intro_to_parsing/ (TODO: this is out of date, hopefully it will be updated at some point)
== Contributing
See link:contributing.html[].
== 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/
* Contact: +++jakewheat@tutanota.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] (TODO: this is out of date, hopefully it will be updated at some point).

238
website/index.md Normal file
View file

@ -0,0 +1,238 @@
# Overview
A parser for SQL in Haskell. Also includes a pretty printer which
formats SQL.
This is the documentation for version 0.7.0. Documentation for other
versions is available here:
<http://jakewheat.github.io/simple-sql-parser/>.
Status: usable for parsing a substantial amount of SQL. Adding support
for new SQL is easy. Expect a little bit of churn on the AST types
when support for new SQL features is added.
This version is tested with GHC 9.8.1, 9.6.4, and 9.4.8.
# Examples
Parse a SQL statement:
~~~~{.haskell sb-session='cabal repl --repl-options=-XOverloadedStrings' sb-prompt='ghci> ' sb-no-initial-text=}
ghci> import Language.SQL.SimpleSQL.Parse
ghci> import qualified Data.Text as T
ghci> either (T.unpack . prettyError) show $ parseStatement ansi2011 "" Nothing "select a + b * c"
"SelectStatement (Select {qeSetQuantifier = SQDefault, qeSelectList = [(BinOp (Iden [Name Nothing \"a\"]) [Name Nothing \"+\"] (BinOp (Iden [Name Nothing \"b\"]) [Name Nothing \"*\"] (Iden [Name Nothing \"c\"])),Nothing)], qeFrom = [], qeWhere = Nothing, qeGroupBy = [], qeHaving = Nothing, qeOrderBy = [], qeOffset = Nothing, qeFetchFirst = Nothing})"
~~~~
The result printed readably:
~~~~{.haskell sb-run='cabal run -fparserexe SimpleSQLParserTool -- parse -c "select a + b * c"' sb-cwd='..'}
[ SelectStatement
Select
{ qeSetQuantifier = SQDefault
, qeSelectList =
[ ( BinOp
(Iden [ Name Nothing "a" ])
[ Name Nothing "+" ]
(BinOp
(Iden [ Name Nothing "b" ])
[ Name Nothing "*" ]
(Iden [ Name Nothing "c" ]))
, Nothing
)
]
, qeFrom = []
, qeWhere = Nothing
, qeGroupBy = []
, qeHaving = Nothing
, qeOrderBy = []
, qeOffset = Nothing
, qeFetchFirst = Nothing
}
]
~~~~
Formatting SQL, TPC-H query 21:
~~~~{.sql sb-file='tpch21.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;
~~~~
Output from the simple-sql-parser pretty printer:
~~~~{.haskell sb-run='cabal run -fparserexe SimpleSQLParserTool -- format website/tpch21.sql' sb-cwd='..'}
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;
~~~~
# Supported SQL overview
* 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 [supported_sql.html](supported_sql.html) page for details on the supported SQL.
Here is all the [test_cases.html](test_cases.html) rendered in a webpage so you can get
an idea of what it supports, and what various instances of SQL parse to.
# Installation
This package is on hackage, use it in the usual way. You can install
the SimpleSQLParserTool demo exe using:
~~~~
cabal install -fparserexe simple-sql-parser
~~~~
# 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. You can expect bugs to get fixed.
Feature requests are welcome, but be aware that there is no-one
generally available to work on these, so you should either make a pull
request, or find someone willing to implement the features and make a
pull request.
There is a related tutorial on implementing a SQL parser here:
<http://jakewheat.github.io/intro_to_parsing/> (TODO: this is out of
date, in the process of being updated)
# Modifying the library
Get the latest development version:
~~~~
git clone https://github.com/JakeWheat/simple-sql-parser.git
cd simple-sql-parser
cabal build
~~~~
You can run the tests using cabal:
~~~~
cabal test
~~~~
Or use the makefile target
~~~~
make test
~~~~
When you add support for new syntax: add some tests. If you modify or
fix something, and it doesn't have tests, add some. If the syntax
isn't in ANSI SQL, guard it behind a dialect flag. If you write
support for a dialect that isn't mention, add a new dialect.
Make sure all the tests still pass, then send a pull request on Github.
# Links
* Haddock: [haddock/index.html](haddock/index.html)
* Supported SQL: [supported_sql.html](supported_sql.html)
* Test cases: [test_cases.html](test_cases.html)
* Homepage: <http://jakewheat.github.io/simple-sql-parser/latest>
* Hackage: <http://hackage.haskell.org/package/simple-sql-parser>
* Source 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/>
* Contact: jakewheat@tutanota.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:
<https://github.com/JakeWheat/intro_to_parsing/blob/master/SimpleSQLQueryParser0.lhs>
(TODO: this is out of date, in the process of being updated).

View file

@ -1,138 +1,116 @@
h1, h2 {
display:block;
background-color: #f0f0f0;
border-top: thin #c0c0c0 solid;
/*position:relative;*/
padding-left:1ex;
/*z-index: -10;*/
}
h1 {
font-size: x-large;
/*left: -3ex;*/
margin-top: 3ex;
/*width: 100%;*/
}
h2 {
font-size: large;
/*left: -1.5ex;*/
margin-top: 1.5ex;
/*width: 100%;*/
}
body {
margin: 0 auto;
hyphens: auto;
overflow-wrap: break-word;
text-rendering: optimizeLegibility;
font-kerning: normal;
color:#000000;
margin-left: 5em;
margin-right: 5em;
margin-bottom: 5em;
margin-top: 2em;
left:auto;
right:0;
margin-right:30em;
max-width: 120ex;
}
pre {
padding: 1em;
background:#fafafa;
}
code {
padding: 0;
margin: 0;
font-size:15px;
}
#TOC {
float:right;
z-index:10;
background-color: #f0f0f0;
border: thin #c0c0c0 solid;
padding:2ex;
}
.header {
/*position:relative;
left: -4ex;*/
border-top: thin #c0c0c0 solid;
border-bottom: thin #c0c0c0 solid;
display:inline;
padding: 1ex;
background-color: #f0f0f0;
font-weight: bold;
}
.footer {
text-align: center;
font-size: small;
}
pre {
padding: 0.5ex;
}
pre {
background-color: #f0f0f0;
}
/*
.SqlPostgresql pre.sourceCode {
padding: 0.5em;
background-color: #f0f6f6;
}
.sql pre.sourceCode {
padding: 0.5em;
background-color: #f0f6f6;
}
.GeneratedSql .SqlPostgresql pre.sourceCode, .SqlPostgresql .GeneratedSql pre.sourceCode {
padding: 0.5em;
background-color: #f0f6e0;
}
.UnusedSql .SqlPostgresql pre.sourceCode, .SqlPostgresql .UnusedSql pre.sourceCode {
padding: 0.5em;
background-color: #e9e9e9;
}
.haskell,.Haskell pre.sourceCode {
background-color: #f5f5d9;
}
.sh pre.sourceCode {
padding: 0.5em;
background-color: #f0f0f0;
}
*/
table, tr, td {
border-collapse:collapse;
cell-padding:2px;
cell-spacing:2px;
/* padding:2px
spacing:2px
margin:2px*/
vertical-align:top;
}
td pre {
width: 98%;
height: 98%;
vertical-align:top;
}
table {
width:100%;
table-layout:fixed;
}
td {
width: 50%;
vertical-align:top;
position:fixed;
width:25em;
left:0;
top:0;
z-index:1000;
height:100%;
overflow:auto;
}
hr {
border: 0;
color: black;
background-color: black;
height: 1px;
width: 75%;
left:auto;
right:0;
border-top: 1px solid #858585;
border-right-width: 0;
border-left: 1px solid #858585;
margin-top: 0 !important;
background: #fafafa;
border-top-width: 0 !important;
border-bottom-width: 0 !important;
padding-top: 0em;
padding-right: 1em;
padding-bottom: 1.25em;
padding-left: 1em;
margin: 0;
font-size: .9em;
font-family:"Noto Serif","Open Sans","DejaVu Sans",sans-serif;
font-style: normal;
color: #000000;
font-weight: 400;
}
.tablediv {
width:100%;
#TOC li {
list-style: none;
line-height:1.3334;
margin-top:.3334em
}
/* higlighting kate */
#TOC ul {
padding-left: 1.3em;
}
#TOC a:not(:hover) {
text-decoration: none;
}
h1,h2,h3,h4,h5,h6 {
font-family:"Open Sans","DejaVu Sans",sans-serif;
font-weight:300;
font-style:normal;
color:#000000;
text-rendering:optimizeLegibility;
margin-top:1em;
margin-bottom:.5em;
line-height:1.0125em
}
h1 {
border-top: 1px solid #858585;
padding-top:1em;
}
.title {
color:#000000;
font-size: 3em;
border-top: none;
padding-top:0em;
}
a {
font-weight:400;
font-style:normal;
color: #05117f;
}
hr{border:solid #858585;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0}
table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode, table.sourceCode pre
{ /*margin: 2; padding: 2; border: 2; */ vertical-align: baseline; border: none; }
td.lineNumbers { border-right: 1px solid #AAAAAA; text-align: right; color: #AAAAAA; padding-right: 5px; padding-left: 5px; }
td.sourceCode { padding-left: 5px; }
pre.sourceCode { }
pre.sourceCode span.Normal { }
pre.sourceCode span.Keyword { color: #007020; font-weight: bold; }
pre.sourceCode span.DataType { color: #902000; }
pre.sourceCode span.DecVal { color: #40a070; }
pre.sourceCode span.BaseN { color: #40a070; }
pre.sourceCode span.Float { color: #40a070; }
pre.sourceCode span.Char { color: #4070a0; }
pre.sourceCode span.String { color: #4070a0; }
pre.sourceCode span.Comment { color: #60a0b0; font-style: italic; }
pre.sourceCode span.Others { color: #007020; }
pre.sourceCode span.Alert { color: red; font-weight: bold; }
pre.sourceCode span.Function { color: #06287e; }
pre.sourceCode span.RegionMarker { }
pre.sourceCode span.Error { color: red; font-weight: bold; }

130
website/main1.css Normal file
View file

@ -0,0 +1,130 @@
body {
margin: 0 auto;
hyphens: auto;
overflow-wrap: break-word;
text-rendering: optimizeLegibility;
font-kerning: normal;
color:#000000;
margin-left: 5em;
margin-right: 5em;
margin-bottom: 5em;
margin-top: 2em;
/*left:auto;
right:0;
margin-right:30em;*/
/*max-width: 120ex;*/
}
pre {
padding: 1em;
background:#fafafa;
}
code {
padding: 0;
margin: 0;
font-size:15px;
}
#TOC {
/*position:fixed;
width:25em;
left:0;
top:0;
z-index:1000;
height:100%;
overflow:auto;
left:auto;
right:0;
border-top: 1px solid #858585;
border-right-width: 0;
border-left: 1px solid #858585;
margin-top: 0 !important;
background: #fafafa;
border-top-width: 0 !important;
border-bottom-width: 0 !important;
padding-top: 0em;
padding-right: 1em;
padding-bottom: 1.25em;
padding-left: 1em;
margin: 0;*/
font-size: .9em;
font-family:"Noto Serif","Open Sans","DejaVu Sans",sans-serif;
font-style: normal;
color: #000000;
font-weight: 400;
}
#TOC li {
list-style: none;
line-height:1.3334;
margin-top:.3334em
}
#TOC ul {
padding-left: 1.3em;
}
#TOC a:not(:hover) {
text-decoration: none;
}
h1,h2,h3,h4,h5,h6 {
font-family:"Open Sans","DejaVu Sans",sans-serif;
font-weight:300;
font-style:normal;
color:#000000;
text-rendering:optimizeLegibility;
margin-top:1em;
margin-bottom:.5em;
line-height:1.0125em
}
/*h1 {
border-top: 1px solid #858585;
padding-top:1em;
}*/
.title {
color:#000000;
font-size: 3em;
border-top: none;
padding-top:0em;
}
a {
font-weight:400;
font-style:normal;
color: #05117f;
}
hr{border:solid #858585;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0}
table {
width: 100%;
border-spacing: 0;
border-collapse: collapse;
}
td {
width: 50%;
vertical-align: top;
border:solid #c5c5c5;
border-width:1px;
}

View file

@ -1,546 +0,0 @@
/* @group Fundamentals */
* { margin: 0; padding: 0 }
/* Is this portable? */
html {
background-color: white;
width: 100%;
height: 100%;
}
body {
background: white;
color: black;
text-align: left;
min-height: 100%;
position: relative;
}
p {
margin: 0.8em 0;
}
ul, ol {
margin: 0.8em 0 0.8em 2em;
}
dl {
margin: 0.8em 0;
}
dt {
font-weight: bold;
}
dd {
margin-left: 2em;
}
a { text-decoration: none; }
a[href]:link { color: rgb(196,69,29); }
a[href]:visited { color: rgb(171,105,84); }
a[href]:hover { text-decoration:underline; }
/* @end */
/* @group Fonts & Sizes */
/* Basic technique & IE workarounds from YUI 3
For reasons, see:
http://yui.yahooapis.com/3.1.1/build/cssfonts/fonts.css
*/
body {
font:13px/1.4 sans-serif;
*font-size:small; /* for IE */
*font:x-small; /* for IE in quirks mode */
}
h1 { font-size: 146.5%; /* 19pt */ }
h2 { font-size: 131%; /* 17pt */ }
h3 { font-size: 116%; /* 15pt */ }
h4 { font-size: 100%; /* 13pt */ }
h5 { font-size: 100%; /* 13pt */ }
select, input, button, textarea {
font:99% sans-serif;
}
table {
font-size:inherit;
font:100%;
}
pre, code, kbd, samp, tt, .src {
font-family:monospace;
*font-size:108%;
line-height: 124%;
}
.links, .link {
font-size: 85%; /* 11pt */
}
#module-header .caption {
font-size: 182%; /* 24pt */
}
.info {
font-size: 85%; /* 11pt */
}
#table-of-contents, #synopsis {
/* font-size: 85%; /* 11pt */
}
/* @end */
/* @group Common */
.caption, h1, h2, h3, h4, h5, h6 {
font-weight: bold;
color: rgb(78,98,114);
margin: 0.8em 0 0.4em;
}
* + h1, * + h2, * + h3, * + h4, * + h5, * + h6 {
margin-top: 2em;
}
h1 + h2, h2 + h3, h3 + h4, h4 + h5, h5 + h6 {
margin-top: inherit;
}
ul.links {
list-style: none;
text-align: left;
float: right;
display: inline-table;
margin: 0 0 0 1em;
}
ul.links li {
display: inline;
border-left: 1px solid #d5d5d5;
white-space: nowrap;
padding: 0;
}
ul.links li a {
padding: 0.2em 0.5em;
}
.hide { display: none; }
.show { display: inherit; }
.clear { clear: both; }
.collapser {
background-image: url(minus.gif);
background-repeat: no-repeat;
}
.expander {
background-image: url(plus.gif);
background-repeat: no-repeat;
}
p.caption.collapser,
p.caption.expander {
background-position: 0 0.4em;
}
.collapser, .expander {
padding-left: 14px;
margin-left: -14px;
cursor: pointer;
}
pre {
padding: 0.25em;
margin: 0.8em 0;
background: rgb(229,237,244);
overflow: auto;
border-bottom: 0.25em solid white;
/* white border adds some space below the box to compensate
for visual extra space that paragraphs have between baseline
and the bounding box */
}
.src {
background: #f0f0f0;
padding: 0.2em 0.5em;
}
.keyword { font-weight: normal; }
.def { font-weight: bold; }
/* @end */
/* @group Page Structure */
#content {
margin: 0 auto;
padding: 0 2em 6em;
}
#package-header {
background: rgb(41,56,69);
border-top: 5px solid rgb(78,98,114);
color: #ddd;
padding: 0.2em;
position: relative;
text-align: left;
}
#package-header .caption {
background: url(hslogo-16.png) no-repeat 0em;
color: white;
margin: 0 2em;
font-weight: normal;
font-style: normal;
padding-left: 2em;
}
#package-header a:link, #package-header a:visited { color: white; }
#package-header a:hover { background: rgb(78,98,114); }
#module-header .caption {
color: rgb(78,98,114);
font-weight: bold;
border-bottom: 1px solid #ddd;
}
table.info {
float: right;
padding: 0.5em 1em;
border: 1px solid #ddd;
color: rgb(78,98,114);
background-color: #fff;
max-width: 40%;
border-spacing: 0;
position: relative;
top: -0.5em;
margin: 0 0 0 2em;
}
.info th {
padding: 0 1em 0 0;
}
div#style-menu-holder {
position: relative;
z-index: 2;
display: inline;
}
#style-menu {
position: absolute;
z-index: 1;
overflow: visible;
background: #374c5e;
margin: 0;
text-align: center;
right: 0;
padding: 0;
top: 1.25em;
}
#style-menu li {
display: list-item;
border-style: none;
margin: 0;
padding: 0;
color: #000;
list-style-type: none;
}
#style-menu li + li {
border-top: 1px solid #919191;
}
#style-menu a {
width: 6em;
padding: 3px;
display: block;
}
#footer {
background: #ddd;
border-top: 1px solid #aaa;
padding: 0.5em 0;
color: #666;
text-align: center;
position: absolute;
bottom: 0;
width: 100%;
height: 3em;
}
/* @end */
/* @group Front Matter */
#table-of-contents {
float: right;
clear: right;
background: #faf9dc;
border: 1px solid #d8d7ad;
padding: 0.5em 1em;
max-width: 20em;
margin: 0.5em 0 1em 1em;
}
#table-of-contents .caption {
text-align: center;
margin: 0;
}
#table-of-contents ul {
list-style: none;
margin: 0;
}
#table-of-contents ul ul {
margin-left: 2em;
}
#description .caption {
display: none;
}
#synopsis {
display: none;
}
.no-frame #synopsis {
display: block;
position: fixed;
right: 0;
height: 80%;
top: 10%;
padding: 0;
}
#synopsis .caption {
float: left;
width: 29px;
color: rgba(255,255,255,0);
height: 110px;
margin: 0;
font-size: 1px;
padding: 0;
}
#synopsis p.caption.collapser {
background: url(synopsis.png) no-repeat -64px -8px;
}
#synopsis p.caption.expander {
background: url(synopsis.png) no-repeat 0px -8px;
}
#synopsis ul {
height: 100%;
overflow: auto;
padding: 0.5em;
margin: 0;
}
#synopsis ul ul {
overflow: hidden;
}
#synopsis ul,
#synopsis ul li.src {
background-color: #faf9dc;
white-space: nowrap;
list-style: none;
margin-left: 0;
}
/* @end */
/* @group Main Content */
#interface div.top { margin: 2em 0; }
#interface h1 + div.top,
#interface h2 + div.top,
#interface h3 + div.top,
#interface h4 + div.top,
#interface h5 + div.top {
margin-top: 1em;
}
#interface p.src .link {
float: right;
color: #919191;
border-left: 1px solid #919191;
background: #f0f0f0;
padding: 0 0.5em 0.2em;
margin: 0 -0.5em 0 0.5em;
}
#interface table { border-spacing: 2px; }
#interface td {
vertical-align: top;
padding-left: 0.5em;
}
#interface td.src {
white-space: nowrap;
}
#interface td.doc p {
margin: 0;
}
#interface td.doc p + p {
margin-top: 0.8em;
}
.subs dl {
margin: 0;
}
.subs dt {
float: left;
clear: left;
display: block;
margin: 1px 0;
}
.subs dd {
float: right;
width: 90%;
display: block;
padding-left: 0.5em;
margin-bottom: 0.5em;
}
.subs dd.empty {
display: none;
}
.subs dd p {
margin: 0;
}
.top p.src {
border-top: 1px solid #ccc;
}
.subs, .doc {
/* use this selector for one level of indent */
padding-left: 2em;
}
.warning {
color: red;
}
.arguments {
margin-top: -0.4em;
}
.arguments .caption {
display: none;
}
.fields { padding-left: 1em; }
.fields .caption { display: none; }
.fields p { margin: 0 0; }
/* this seems bulky to me
.methods, .constructors {
background: #f8f8f8;
border: 1px solid #eee;
}
*/
/* @end */
/* @group Auxillary Pages */
#mini {
margin: 0 auto;
padding: 0 1em 1em;
}
#mini > * {
font-size: 93%; /* 12pt */
}
#mini #module-list .caption,
#mini #module-header .caption {
font-size: 125%; /* 15pt */
}
#mini #interface h1,
#mini #interface h2,
#mini #interface h3,
#mini #interface h4 {
font-size: 109%; /* 13pt */
margin: 1em 0 0;
}
#mini #interface .top,
#mini #interface .src {
margin: 0;
}
#mini #module-list ul {
list-style: none;
margin: 0;
}
#alphabet ul {
list-style: none;
padding: 0;
margin: 0.5em 0 0;
text-align: center;
}
#alphabet li {
display: inline;
margin: 0 0.25em;
}
#alphabet a {
font-weight: bold;
}
#index .caption,
#module-list .caption { font-size: 131%; /* 17pt */ }
#index table {
margin-left: 2em;
}
#index .src {
font-weight: bold;
}
#index .alt {
font-size: 77%; /* 10pt */
font-style: italic;
padding-left: 2em;
}
#index td + td {
padding-left: 1em;
}
#module-list ul {
list-style: none;
margin: 0 0 0 2em;
}
#module-list li {
clear: right;
}
#module-list span.collapser,
#module-list span.expander {
background-position: 0 0.3em;
}
#module-list .package {
float: right;
}
/* @end */

View file

@ -0,0 +1,49 @@
cabal-version: 2.2
name: simple-sql-parser
version: 0.7.0
executable RenderTestCases
main-is: RenderTestCases.hs
hs-source-dirs: .,..,../tests
Build-Depends: base >=4 && <5,
text,
megaparsec,
prettyprinter,
parser-combinators,
mtl,
containers,
tasty,
tasty-hunit,
pretty-show,
default-language: Haskell2010
ghc-options: -Wall -O0
other-modules:
Language.SQL.SimpleSQL.CreateIndex
Language.SQL.SimpleSQL.CustomDialect
Language.SQL.SimpleSQL.Dialect
Language.SQL.SimpleSQL.EmptyStatement
Language.SQL.SimpleSQL.FullQueries
Language.SQL.SimpleSQL.GroupBy
Language.SQL.SimpleSQL.Lex
Language.SQL.SimpleSQL.LexerTests
Language.SQL.SimpleSQL.MySQL
Language.SQL.SimpleSQL.Odbc
Language.SQL.SimpleSQL.Oracle
Language.SQL.SimpleSQL.Parse
Language.SQL.SimpleSQL.Postgres
Language.SQL.SimpleSQL.Pretty
Language.SQL.SimpleSQL.QueryExprComponents
Language.SQL.SimpleSQL.QueryExprs
Language.SQL.SimpleSQL.SQL2011AccessControl
Language.SQL.SimpleSQL.SQL2011Bits
Language.SQL.SimpleSQL.SQL2011DataManipulation
Language.SQL.SimpleSQL.SQL2011Queries
Language.SQL.SimpleSQL.SQL2011Schema
Language.SQL.SimpleSQL.ScalarExprs
Language.SQL.SimpleSQL.Syntax
Language.SQL.SimpleSQL.TableRefs
Language.SQL.SimpleSQL.TestTypes
Language.SQL.SimpleSQL.Tests
Language.SQL.SimpleSQL.Tpch

View file

@ -1,185 +0,0 @@
:toc: right
:sectnums:
:toclevels: 10
:source-highlighter: pygments
= simple-sql-parser supported SQL
== Overview
This page has more details on the supported SQL in simple-sql-parser.
See the link:test_cases.html[simple-sql-parser test cases] page for
examples.
The target dialect of SQL at this time is ISO/ANSI SQL:2011. The
parser supports queries, DDL, non-query DML, access control and
transaction management syntax. The parser and syntax does not follow
the standard grammar closely - they permit a lot of things which the
grammar in the standard forbids. The intended usage is that an
additional pass over the ast can be made if you want to carefully
prohibit everything that the standard doesn't allow.
Apart from this permissiveness, some work has been put into trying to
get good parser error messages.
== Queries
=== Select lists
Supports scalar expressions, aliases with optional 'as'.
Doesn't support 'select * as (a,b,c) from t' yet.
=== Set quantifiers on select
Supports 'select distinct' and explicit 'select all'.
=== From clause
* aliases
* subqueries
* functions
* joins
- natural
- inner
- left/right/full outer
- cross
- on expressions
- using lists
- lateral
=== Group by clause
Supports scalar expressions, group by (), cube, rollup, grouping
parentheses and grouping sets with nested grouping expressions.
=== Order by clause
Supports scalar expressions, asc/desc and nulls first/last.
=== Offset and fetch
Supports 'offset n rows' and 'fetch first n rows only'.
=== Set operators
Union, except, intersect + all/distinct and corresponding.
=== Table value constructor
For example: values (1,2),(3,4).
=== Explicit table
For example: 'table t', which is shorthand for 'select * from t'.
=== Scalar expressions
The scalar expressions type and parser is used in many contexts,
including:
* select lists;
* where clause expressions;
* group by clause expressions;
* having clause expressions;
* order by clause expressions;
* offset and fetch clause expressions;
* table value constructors.
This doesn't exactly follow the ANSI Standards, which have separate
grammars for most of these.
The supported scalar expressions include:
* basic string literals in single quotes
* number literals: digits.digitse+-exp
* explicitly typed literal, e.g. int '3'
* binary operators
- comparisons: = != <> <= >= < >
- arithmetic: + - / * % ^
- logic: and, or
- bitwise: & | (and ^ as above)
- string: ||, like, not like
- other: overlaps, is similar to, is not similar too, is distinct
from, is not distinct from
* prefix unary operators
- +, -
- not
- ~
* postfix unary
- is null, is not null
- is true, is not true, is false, is not false, is unknown, is not unknown
* other operators
- extract (extract(day from dt))
- position (position string1 in string2)
- substring (substring(x from 2 for 4))
- convert (convert(string using conversion))
- translate (translate(string using translation))
- overlay (overlay (string placing embedded_string from start for
length))
- trim (trim(leading '_' from s))
- between (a between 1 and 5)
- in list (a in (1,2,3,4))
- cast (cast(a as int))
* subqueries
- in subquery
- any/some/all
- exists
* case expressions
* parentheses
* quoted and unquoted identifiers
* a.b qualified identifiers
* \*, a.*
* functions: f(a,b)
* aggregates: agg(distinct a order by b)
* window functions: sum(x) over (partition by y order by z)
plus some explicit frame support (same as in postgres 9.3)
* row constructors, e.g. where (a,b) = any (select a,b from t)
* ? used in parameterized queries
== DDL
* schemas
** create, drop + drop restrict
* tables
** create table
*** constraints: named, null, unique, primary key, foreign key (matches, on update/delete)
*** identity (the weird ansi version), defaults
*** defaults
** alter table
*** defaults, null, set data type, drop column, constraints
** drop table + restrict
** create, drop view
** create, alter, drop domain
*** defaults, constraints
** create, drop assertion
** create, alter, drop sequence
== Non-query DML
** delete
*** delete from
*** as alias
*** where
** truncate
*** with identity options
** insert
*** values, general queries, defaults
** update
*** including row updates
== Access Control
** grant privileges
*** all, grant option, table, domain, type, sequence, role, etc.
** revoke
** create role, drop role
== Transaction management
* begin, commit, rollback
* savepoints

175
website/supported_sql.md Normal file
View file

@ -0,0 +1,175 @@
# Overview
This page has more details on the supported SQL in simple-sql-parser.
See the [simple-sql-parser test cases](test_cases.html) page for
examples.
The target dialect of SQL at this time is ISO/ANSI SQL:2011. The
parser supports queries, DDL, non-query DML, access control and
transaction management syntax. The parser and syntax does not follow
the standard grammar closely - they permit a lot of things which the
grammar in the standard forbids. The intended usage is that an
additional pass over the ast can be made if you want to carefully
prohibit everything that the standard doesn't allow.
Apart from this permissiveness, some work has been put into trying to
get good parser error messages.
# Queries
## Select lists
Supports scalar expressions, aliases with optional 'as'.
## Set quantifiers on select
Supports 'select distinct' and explicit 'select all'.
## From clause
* aliases
* subqueries
* functions
* joins
- natural
- inner
- left/right/full outer
- cross
- on expressions
- using lists
- lateral
## Group by clause
Supports scalar expressions, group by (), cube, rollup, grouping
parentheses and grouping sets with nested grouping expressions.
## Order by clause
Supports scalar expressions, asc/desc and nulls first/last.
## Offset and fetch
Supports 'offset n rows' and 'fetch first n rows only'.
## Set operators
Union, except, intersect + all/distinct and corresponding.
## Table value constructor
Example: 'values (1,2),(3,4)'.
## Explicit table
Example: 'table t', which is shorthand for 'select * from t'.
## Scalar expressions
The scalar expressions type and parser is used in many contexts,
including:
* select lists;
* where clause expressions;
* group by clause expressions;
* having clause expressions;
* order by clause expressions;
* offset and fetch clause expressions;
* table value constructors.
This doesn't exactly follow the ANSI Standards, which have separate
grammars for most of these.
The supported scalar expressions include:
* basic string literals in single quotes
* number literals: digits.digitse+-exp
* explicitly typed literal, e.g. int '3'
* binary operators
- comparisons: = != <> <= >= < >
- arithmetic: + - / * % ^
- logic: and, or
- bitwise: & | (and ^ as above)
- string: ||, like, not like
- other: overlaps, is similar to, is not similar too, is distinct
from, is not distinct from
* prefix unary operators
- +, -
- not
- ~
* postfix unary
- is null, is not null
- is true, is not true, is false, is not false, is unknown, is not unknown
* other operators
- extract (extract(day from dt))
- position (position string1 in string2)
- substring (substring(x from 2 for 4))
- convert (convert(string using conversion))
- translate (translate(string using translation))
- overlay (overlay (string placing embedded_string from start for
length))
- trim (trim(leading '_' from s))
- between (a between 1 and 5)
- in list (a in (1,2,3,4))
- cast (cast(a as int))
* subqueries
- in subquery
- any/some/all
- exists
* case expressions
* parentheses
* quoted and unquoted identifiers
* a.b qualified identifiers
* \*, a.*
* functions: f(a,b)
* aggregates: agg(distinct a order by b)
* window functions: sum(x) over (partition by y order by z)
plus some explicit frame support (same as in postgres 9.3)
* row constructors, e.g. where (a,b) = any (select a,b from t)
* ? used in parameterized queries
# DDL
* schemas
* create, drop + drop restrict
* tables
* create table
* constraints: named, null, unique, primary key, foreign key (matches, on update/delete)
* identity (the weird ansi version), defaults
* defaults
* alter table
* defaults, null, set data type, drop column, constraints
* drop table + restrict
* create, drop view
* create, alter, drop domain
* defaults, constraints
* create, drop assertion
* create, alter, drop sequence
# Non-query DML
* delete
* delete from
* as alias
* where
* truncate
* with identity options
* insert
* values, general queries, defaults
* update
* including row updates
# Access Control
* grant privileges
* all, grant option, table, domain, type, sequence, role, etc.
* revoke
* create role, drop role
# Transaction management
* begin, commit, rollback
* savepoints

93
website/template.pandoc Normal file
View file

@ -0,0 +1,93 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="$lang$" xml:lang="$lang$"$if(dir)$ dir="$dir$"$endif$>
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
$for(author-meta)$
<meta name="author" content="$author-meta$" />
$endfor$
$if(date-meta)$
<meta name="dcterms.date" content="$date-meta$" />
$endif$
$if(keywords)$
<meta name="keywords" content="$for(keywords)$$keywords$$sep$, $endfor$" />
$endif$
$if(description-meta)$
<meta name="description" content="$description-meta$" />
$endif$
<title>$if(title-prefix)$$title-prefix$ $endif$$pagetitle$</title>
<style>
$styles.html()$
</style>
$for(css)$
<link rel="stylesheet" href="$css$" />
$endfor$
$for(header-includes)$
$header-includes$
$endfor$
$if(math)$
$if(mathjax)$
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
$endif$
$math$
$endif$
</head>
<body>
$for(include-before)$
$include-before$
$endfor$
$if(title)$
<header id="title-block-header">
<h1 class="title">$title$</h1>
$if(subtitle)$
<p class="subtitle">$subtitle$</p>
$endif$
$for(author)$
<p class="author">$author$</p>
$endfor$
$if(date)$
<p class="date">$date$</p>
$endif$
$if(abstract)$
<div class="abstract">
<div class="abstract-title">$abstract-title$</div>
$abstract$
</div>
$endif$
</header>
$endif$
$body$
$for(include-after)$
$include-after$
$endfor$
$if(toc)$
<nav id="$idprefix$TOC" role="doc-toc">
$if(toc-title)$
<h2 id="$idprefix$toc-title">$toc-title$</h2>
$endif$
$table-of-contents$
<hr />
<h2 id="toc-title">Links</h2>
<ul>
<li><a href="index.html">Index</a></li>
<li><a href='haddock/index.html'>Haddock</li>
<li><a href="supported_sql.html">Supported SQL</a></li>
<li><a href="test_cases.html">Test cases</a></li>
</ul>
<ul>
<li><a href="http://jakewheat.github.io/simple-sql-parser/latest">Homepage</a></li>
<li><a href="http://hackage.haskell.org/package/simple-sql-parser">Hackage</a></li>
<li><a href="https://github.com/JakeWheat/simple-sql-parser">Source repository</a></li>
<li><a href="https://github.com/JakeWheat/simple-sql-parser/issues">Bug tracker</a></li>
<li><a href="https://github.com/JakeWheat/simple-sql-parser/blob/master/changelog">Changes</a></li>
<li><a href="http://jakewheat.github.io/simple-sql-parser/">Other versions</a></li>
<li>jakewheat@tutanota.com</li>
</ul>
</nav>
$endif$
</body>
</html>

76
website/template1.pandoc Normal file
View file

@ -0,0 +1,76 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="$lang$" xml:lang="$lang$"$if(dir)$ dir="$dir$"$endif$>
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
$for(author-meta)$
<meta name="author" content="$author-meta$" />
$endfor$
$if(date-meta)$
<meta name="dcterms.date" content="$date-meta$" />
$endif$
$if(keywords)$
<meta name="keywords" content="$for(keywords)$$keywords$$sep$, $endfor$" />
$endif$
$if(description-meta)$
<meta name="description" content="$description-meta$" />
$endif$
<title>$if(title-prefix)$$title-prefix$ $endif$$pagetitle$</title>
<style>
$styles.html()$
</style>
$for(css)$
<link rel="stylesheet" href="$css$" />
$endfor$
$for(header-includes)$
$header-includes$
$endfor$
$if(math)$
$if(mathjax)$
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
$endif$
$math$
$endif$
</head>
<body>
$if(toc)$
<nav id="$idprefix$TOC" role="doc-toc">
$if(toc-title)$
<h2 id="$idprefix$toc-title">$toc-title$</h2>
$endif$
$table-of-contents$
</nav>
$endif$
$for(include-before)$
$include-before$
$endfor$
$if(title)$
<header id="title-block-header">
<h1 class="title">$title$</h1>
$if(subtitle)$
<p class="subtitle">$subtitle$</p>
$endif$
$for(author)$
<p class="author">$author$</p>
$endfor$
$if(date)$
<p class="date">$date$</p>
$endif$
$if(abstract)$
<div class="abstract">
<div class="abstract-title">$abstract-title$</div>
$abstract$
</div>
$endif$
</header>
$endif$
$body$
$for(include-after)$
$include-after$
$endfor$
</body>
</html>

40
website/tpch21.sql Normal file
View file

@ -0,0 +1,40 @@
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;