Odbc comes with some small 'extensions' to sql syntax. The basic idea is to wrap a few places in SQL where you write ansi standard sql, and delimit it with a {} pair, and then some simple parser in the odbc driver can convert this into the dialect specific sql for your non-ansi-sql supporting product circa 1989. In this way, you can achieve a little bit of SQL DBMS dialect independence. I think it is sensible to support the odbc syntax directly in your main SQL parser instead of in the driver, so here it is. Summary of ODBC syntax extensions: Here is microsoft's docs which are OK: https://msdn.microsoft.com/en-us/library/ms715364(v=vs.85).aspx 1. date, time, and timestamp literals 2. scalar function calls 3. escapes for like strings 4. outer joins 5. calling a procedure = date, time and timestamp literals These abstract out the particular formats (because everyone has to put the years and months in different places, and use slashes or dashes or dots or whatever). {d 'yyyy-mm-dd'} {t 'HH:MM:ss[.f...]} {ts 'yyyy-mm-dd HH:MM:ss[.f...]'} The point of this is to have a fixed format for dates/times which the vendor specific driver vendor can convert into the format expected by the vendor's sql dbms. One thing that isn't clear to me is is whitespace allowed between { and d, or ' and } for instance, and is different whitespace other than a single space between the d and the ' for instance allowed? = scalar function calls {fn scalar-function-call} e.g. {fn curdate())} Note: these can nest, e.g.: INSERT INTO Orders (OrderID, CustID, OpenDate, SalesPerson, Status) VALUES (?, ?, {fn CONVERT({fn CURDATE()}, SQL_DATE)}, ?, ?) The purpose of these is to take the ansi standard functions, and sql server/access functions, and the extract operator, which isn't quite a normal function, and then allow the driver to convert the function names and arguments into the database specific alternative (e.g. with a different function name, or the argument order changed). = escape for a like string this is the ansi standard: [ NOT ] LIKE [ ESCAPE ] The odbc syntax to abstract this is: [ NOT ] LIKE {escape 'escape-character'} e.g. like '[[' {escape '['} -> like '[[' escape [ This isn't currently supported in hssqlppp. = outer join This is basically a weird wrapper which allows the driver to replace 'ansi' outer join syntax with the old oracle outer join syntax if this is what the sql dbms supports. You write something like: select * from {oj t1 left outer join t1 on t1.a = t2.b} and then in theory, an older dbms which doesn't support this syntax can conver this to: select * from t1,t2 where t1.a(+) = t2.b I may have got the (+) in the wrong place. In hssqlppp, the {oj ... } is treated like trefparens: select * from {oj t1 left outer join t1 on t1.a = t2.b} is basically the same as select * from (t1 left outer join t1 on t1.a = t2.b) = calling a procedure {[?=]call procedure-name[([parameter][,[parameter]]...)]} this isn't currently supported in hssqlppp. There is additional stuff in the typechecking, see the odbc typechecking tests for more info. The type checker doesn't test the date/time literals inside the literal like other literals in hssqlppp, and these are treated as sugar, and oj does nothing, but there is a bit of work to deal with the functions. The odbc syntax is currently available in all dialects. > {-# LANGUAGE OverloadedStrings #-} > module Database.HsSqlPpp.Tests.Parsing.OdbcParsing (odbcParsing) where > > import Database.HsSqlPpp.Syntax > import Database.HsSqlPpp.Tests.Parsing.Utils > import Database.HsSqlPpp.Tests.TestTypes > import Database.HsSqlPpp.Dialect > odbcParsing:: Item > odbcParsing = > Group "odbcParsing" [ > Group "datetime" [ > e "{d '2000-01-01'}" (OdbcLiteral ea OLDate "2000-01-01") > ,e "{t '12:00:01.1'}" (OdbcLiteral ea OLTime "12:00:01.1") > ,e "{ts '2000-01-01 12:00:01.1'}" > (OdbcLiteral ea OLTimestamp "2000-01-01 12:00:01.1") > ] > ,Group "functions" [ > e "{fn CHARACTER_LENGTH(string_exp)}" > $ OdbcFunc ea (App ea (name "CHARACTER_LENGTH") > [ei "string_exp"]) > ,e "{fn EXTRACT(year from my_date)}" > $ OdbcFunc ea (Extract ea ExtractYear $ ei "my_date") > ,e "{fn now()}" > $ OdbcFunc ea (App ea (name "now") []) > ,e "{fn CONVERT('2000-01-01', SQL_DATE)}" > $ OdbcFunc ea (App ea (name "CONVERT") > [StringLit ea "2000-01-01" > ,ei "SQL_DATE"]) > ,e "{fn CONVERT({fn CURDATE()}, SQL_DATE)}" > $ OdbcFunc ea (App ea (name "CONVERT") > [OdbcFunc ea (App ea (name "CURDATE") []) > ,ei "SQL_DATE"]) > ] > ,Group "outer join" [ > ParseQueryExpr defaultParseFlags > "select * from {oj t1 left outer join t2 on true}" > $ makeSelect > {selSelectList = sl [si $ Star ea] > ,selTref = [OdbcTableRef ea (JoinTref ea (tref "t1") Unnatural LeftOuter Nothing > (tref "t2") (Just $ JoinOn ea (BooleanLit ea True)))]}] > ,Group "check parsing bugs" [ > ParseQueryExpr defaultParseFlags > "select {fn CONVERT(cint,SQL_BIGINT)} from t;" > $ makeSelect > {selSelectList = sl [si $ OdbcFunc ea (App ea (name "CONVERT") > [ei "cint" > ,ei "SQL_BIGINT"])] > ,selTref = [tref "t"]}] > ,Group "outer join" [ > tsql "select * from {oj t1 left outer join t2 on true}" > [QueryStatement ea $ makeSelect > {selSelectList = sl [si $ Star ea] > ,selTref = [OdbcTableRef ea (JoinTref ea (tref "t1") Unnatural LeftOuter Nothing > (tref "t2") (Just $ JoinOn ea (BooleanLit ea True)))]}]] > ,Group "check parsing bugs" [ > tsql "select {fn CONVERT(cint,SQL_BIGINT)} from t;" > [QueryStatement ea $ makeSelect > {selSelectList = sl [si $ OdbcFunc ea (App ea (name "convert") > [ei "cint" > ,ei "SQL_BIGINT"])] > ,selTref = [tref "t"]}]] > ] > where > e = ParseScalarExpr defaultParseFlags > tsql = ParseProcSql defaultParseFlags {pfDialect=sqlServerDialect}