diff --git a/.ghci b/.ghci deleted file mode 100644 index 5794fd15..00000000 --- a/.ghci +++ /dev/null @@ -1,16 +0,0 @@ -:set -isrc -itest -optP-includedist/build/autogen/cabal_macros.h -:set -Wall -fno-warn-name-shadowing - -:set -XDoAndIfThenElse -:set -XOverloadedStrings -:set -XBangPatterns -:set -XViewPatterns -:set -XTypeOperators - --- for tests -:set -XNamedFieldPuns -:set -XRank2Types -:set -XRecordWildCards -:set -XPatternGuards -:set -XScopedTypeVariables -:set -fno-warn-unused-do-bind diff --git a/.gitignore b/.gitignore index d13de590..6af0ca3d 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,7 @@ dist/ .cabal-sandbox/ .stack-work/ cabal.sandbox.config -/dist-newstyle \ No newline at end of file +/dist-newstyle +.ghc.environment.* +cabal.project.local +dump-core diff --git a/.travis.yml b/.travis.yml index f8653a76..28b6df3d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,73 +1,164 @@ -# This file has been generated -- see https://github.com/hvr/multi-ghc-travis +# This Travis job script has been generated by a script via +# +# haskell-ci '--config' 'cabal.haskell-ci' '--output' '.travis.yml' 'postgresql-simple.cabal' +# +# For more information, see https://github.com/haskell-CI/haskell-ci +# +# version: 0.5.20190916 +# language: c -sudo: false - +dist: xenial +git: + # whether to recursively clone submodules + submodules: false +branches: + only: + - master +services: + - postgresql +addons: + postgresql: "10" cache: directories: - - $HOME/.cabsnap - $HOME/.cabal/packages - + - $HOME/.cabal/store before_cache: - - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log - - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.tar - + - rm -fv $CABALHOME/packages/hackage.haskell.org/build-reports.log + # remove files that are regenerated by 'cabal update' + - rm -fv $CABALHOME/packages/hackage.haskell.org/00-index.* + - rm -fv $CABALHOME/packages/hackage.haskell.org/*.json + - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.cache + - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar + - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar.idx + - rm -rfv $CABALHOME/packages/head.hackage matrix: include: - - env: CABALVER=1.18 GHCVER=7.8.4 - compiler: ": #GHC 7.8.4" - addons: { apt: {packages: [cabal-install-1.18,ghc-7.8.4], sources: [hvr-ghc]}, postgresql: "9.3"} - - env: CABALVER=1.22 GHCVER=7.10.2 - compiler: ": #GHC 7.10.2" - addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.2], sources: [hvr-ghc]}, postgresql: "9.3"} - - env: CABALVER=1.24 GHCVER=8.0.1 - compiler: ": #GHC 8.0.1" - addons: {apt: {packages: [cabal-install-1.24,ghc-8.0.1], sources: [hvr-ghc]}, postgresql: "9.3"} - + - compiler: ghc-8.8.1 + addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.8.1","cabal-install-3.0"]}} + - compiler: ghc-8.6.5 + addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.6.5","cabal-install-3.0"]}} + - compiler: ghc-8.4.4 + addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.4.4","cabal-install-3.0"]}} + - compiler: ghc-8.2.2 + addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.2.2","cabal-install-3.0"]}} + - compiler: ghc-8.0.2 + addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.0.2","cabal-install-3.0"]}} + - compiler: ghc-7.10.3 + addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-7.10.3","cabal-install-3.0"]}} + - compiler: ghc-7.8.4 + addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-7.8.4","cabal-install-3.0"]}} + - compiler: ghc-7.6.3 + addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-7.6.3","cabal-install-3.0"]}} before_install: - - unset CC - - export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH - - createdb `whoami` || true - + - HC=$(echo "/opt/$CC/bin/ghc" | sed 's/-/\//') + - WITHCOMPILER="-w $HC" + - HADDOCK=$(echo "/opt/$CC/bin/haddock" | sed 's/-/\//') + - HCPKG="$HC-pkg" + - unset CC + - CABAL=/opt/ghc/bin/cabal + - CABALHOME=$HOME/.cabal + - export PATH="$CABALHOME/bin:$PATH" + - TOP=$(pwd) + - "HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\\d+)\\.(\\d+)\\.(\\d+)(\\.(\\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))')" + - echo $HCNUMVER + - CABAL="$CABAL -vnormal+nowrap+markoutput" + - set -o pipefail + - | + echo 'function blue(s) { printf "\033[0;34m" s "\033[0m " }' >> .colorful.awk + echo 'BEGIN { state = "output"; }' >> .colorful.awk + echo '/^-----BEGIN CABAL OUTPUT-----$/ { state = "cabal" }' >> .colorful.awk + echo '/^-----END CABAL OUTPUT-----$/ { state = "output" }' >> .colorful.awk + echo '!/^(-----BEGIN CABAL OUTPUT-----|-----END CABAL OUTPUT-----)/ {' >> .colorful.awk + echo ' if (state == "cabal") {' >> .colorful.awk + echo ' print blue($0)' >> .colorful.awk + echo ' } else {' >> .colorful.awk + echo ' print $0' >> .colorful.awk + echo ' }' >> .colorful.awk + echo '}' >> .colorful.awk + - cat .colorful.awk + - | + color_cabal_output () { + awk -f $TOP/.colorful.awk + } + - echo text | color_cabal_output install: - - cabal --version - - echo "$(ghc --version) [$(ghc --print-project-git-commit-id 2> /dev/null || echo '?')]" - - if [ -f $HOME/.cabal/packages/hackage.haskell.org/00-index.tar.gz ]; - then - zcat $HOME/.cabal/packages/hackage.haskell.org/00-index.tar.gz > - $HOME/.cabal/packages/hackage.haskell.org/00-index.tar; - fi - - travis_retry cabal update -v - - sed -i 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config - - cabal install --only-dependencies --enable-tests --enable-benchmarks --dry -v > installplan.txt - - sed -i -e '1,/^Resolving /d' installplan.txt; cat installplan.txt - -# check whether current requested install-plan matches cached package-db snapshot - - if diff -u installplan.txt $HOME/.cabsnap/installplan.txt; - then - echo "cabal build-cache HIT"; - rm -rfv .ghc; - cp -a $HOME/.cabsnap/ghc $HOME/.ghc; - cp -a $HOME/.cabsnap/lib $HOME/.cabsnap/share $HOME/.cabsnap/bin $HOME/.cabal/; - else - echo "cabal build-cache MISS"; - rm -rf $HOME/.cabsnap; - mkdir -p $HOME/.ghc $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin; - cabal install --only-dependencies --enable-tests --enable-benchmarks; - fi - -# snapshot package-db on cache miss - - if [ ! -d $HOME/.cabsnap ]; - then - echo "snapshotting package-db to build-cache"; - mkdir $HOME/.cabsnap; - cp -a $HOME/.ghc $HOME/.cabsnap/ghc; - cp -a $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin installplan.txt $HOME/.cabsnap/; - fi - -# Here starts the actual work to be performed for the package under test; -# any command which exits with a non-zero exit code causes the build to fail. + - ${CABAL} --version + - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]" + - TEST=--enable-tests + - BENCH=--enable-benchmarks + - HEADHACKAGE=false + - rm -f $CABALHOME/config + - | + echo "verbose: normal +nowrap +markoutput" >> $CABALHOME/config + echo "remote-build-reporting: anonymous" >> $CABALHOME/config + echo "write-ghc-environment-files: always" >> $CABALHOME/config + echo "remote-repo-cache: $CABALHOME/packages" >> $CABALHOME/config + echo "logs-dir: $CABALHOME/logs" >> $CABALHOME/config + echo "world-file: $CABALHOME/world" >> $CABALHOME/config + echo "extra-prog-path: $CABALHOME/bin" >> $CABALHOME/config + echo "symlink-bindir: $CABALHOME/bin" >> $CABALHOME/config + echo "installdir: $CABALHOME/bin" >> $CABALHOME/config + echo "build-summary: $CABALHOME/logs/build.log" >> $CABALHOME/config + echo "store-dir: $CABALHOME/store" >> $CABALHOME/config + echo "install-dirs user" >> $CABALHOME/config + echo " prefix: $CABALHOME" >> $CABALHOME/config + echo "repository hackage.haskell.org" >> $CABALHOME/config + echo " url: http://hackage.haskell.org/" >> $CABALHOME/config + - | + echo "program-default-options" >> $CABALHOME/config + echo " ghc-options: $GHCJOBS +RTS -M6G -RTS" >> $CABALHOME/config + - cat $CABALHOME/config + - rm -fv cabal.project cabal.project.local cabal.project.freeze + - travis_retry ${CABAL} v2-update -v + # Generate cabal.project + - rm -rf cabal.project cabal.project.local cabal.project.freeze + - touch cabal.project + - | + echo "packages: ." >> cabal.project + - | + - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(postgresql-simple)$' || true) | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done" + - cat cabal.project || true + - cat cabal.project.local || true + - if [ -f "./configure.ac" ]; then (cd "." && autoreconf -i); fi + - ${CABAL} v2-freeze $WITHCOMPILER ${TEST} ${BENCH} | color_cabal_output + - "cat cabal.project.freeze | sed -E 's/^(constraints: *| *)//' | sed 's/any.//'" + - rm cabal.project.freeze + - ${CABAL} v2-build $WITHCOMPILER ${TEST} ${BENCH} --dep -j2 all | color_cabal_output + - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --dep -j2 all | color_cabal_output script: - - if [ -f configure.ac ]; then autoreconf -i; fi - - cabal configure --enable-tests --enable-benchmarks -v2 # -v2 provides useful information for debugging - - cabal build # this builds all libraries and executables (including tests/benchmarks) - - cabal test + - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX) + # Packaging... + - ${CABAL} v2-sdist all | color_cabal_output + # Unpacking... + - mv dist-newstyle/sdist/*.tar.gz ${DISTDIR}/ + - cd ${DISTDIR} || false + - find . -maxdepth 1 -type f -name '*.tar.gz' -exec tar -xvf '{}' \; + - find . -maxdepth 1 -type f -name '*.tar.gz' -exec rm '{}' \; + - PKGDIR_postgresql_simple="$(find . -maxdepth 1 -type d -regex '.*/postgresql-simple-[0-9.]*')" + # Generate cabal.project + - rm -rf cabal.project cabal.project.local cabal.project.freeze + - touch cabal.project + - | + echo "packages: ${PKGDIR_postgresql_simple}" >> cabal.project + - | + - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(postgresql-simple)$' || true) | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done" + - cat cabal.project || true + - cat cabal.project.local || true + # Building... + # this builds all libraries and executables (without tests/benchmarks) + - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks all | color_cabal_output + # Building with tests and benchmarks... + # build & run tests, build benchmarks + - ${CABAL} v2-build $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output + # Testing... + - ${CABAL} v2-test $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output + # cabal check... + - (cd ${PKGDIR_postgresql_simple} && ${CABAL} -vnormal check) + # haddock... + - ${CABAL} v2-haddock $WITHCOMPILER --with-haddock $HADDOCK ${TEST} ${BENCH} all | color_cabal_output + # Building without installed constraints for packages in global-db... + - rm -f cabal.project.local + - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks all | color_cabal_output + +# REGENDATA ["--config","cabal.haskell-ci","--output",".travis.yml","postgresql-simple.cabal"] +# EOF diff --git a/CHANGES.md b/CHANGES.md index 822faccd..ccc895f4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,7 +1,88 @@ -### Unreleased +### Version 0.6.2 (2019-04-26) - * Added `ToField` instances for case-insensitive strict and lazy text. + * Define `MonadFail Ok`. + +### Version 0.6.1 (2019-03-04) + + * Escape double '??' to a literal '? + Thanks to Felix Paulusma for the implementation. + https://github.com/phadej/postgresql-simple/pull/5 + + * Mention GHC Generics support in the documentation. + Thanks to Gabriel Gonzalez for the implementation. + https://github.com/phadej/postgresql-simple/pull/6 + + * Better error message for "Query resulted in a command response + Thanks to Max Amanshauser for the implementation. + https://github.com/phadej/postgresql-simple/pull/7 + + * fromJSONField: Include JSONPath on JSON parse errors + Thanks to Simon Hengel for the implementation. + https://github.com/phadej/postgresql-simple/pull/2 + + * No TH in implementation + https://github.com/phadej/postgresql-simple/pull/4 + +### Version 0.6 (2018-10-16) + + * *Breaking change*: Use `Only` package's `Only` for a common 1-tuple. + + Consider a downstream library depending already both on + `Only` and `postgresql-simple` package. This library my define + a `MyClass` with instances for `Only.Only` and `PostgreSQL.Only`. + As now these types are the same, a library would break. + Therefore I consider "merging" types a breaking change. + + There are two ways for adopting this change in that scenario: + + - Either CPP-guard `PostgreSQL.Only` instance with + + ```haskell + #if !MIN_VERSION_postgresql_simple(0,6,0) + instance MyClass (PostgreSQL.Only a) where ... + #endif + ``` + + - or simply remove it and add `postgresql-simple >=0.6` lower bound, + making sure that there's only single `Only`. + + * Add `ToField` instances for case-insensitive strict and lazy text. Thanks to Max Tagher for the implementation. + https://github.com/lpsmith/postgresql-simple/pull/232 + + * Add support to CockroachDB. + Thanks to Georte Steel. + https://github.com/lpsmith/postgresql-simple/pull/245 + + * Add Generic ConnectInfo instance + Thanks to Dmitry Dzhus. + https://github.com/lpsmith/postgresql-simple/pull/235 + + * Add `fromFieldRange :: Typeable a => FieldParser a -> FieldParser (PGRange a)` + https://github.com/lpsmith/postgresql-simple/pull/221 + + * Add `fromFieldJSONByteString :: FieldParser ByteString` + https://github.com/lpsmith/postgresql-simple/pull/222/files + + * Fix off-by-one error in year builder. + Thanks to Nathan Ferris Hunter. + https://github.com/lpsmith/postgresql-simple/pull/230 + + * Extend ToRow and FromRow to tuples of size 18 + Thanks to Bardur Arantsson. + https://github.com/lpsmith/postgresql-simple/pull/229 + + * Add `Vector` and `Vector.Unboxed` `query` variants. + These are more memory efficient + (especially, if you anyway will convert to some vector) + https://github.com/phadej/1 + + * Documentation improvements + https://github.com/lpsmith/postgresql-simple/pull/227 + https://github.com/lpsmith/postgresql-simple/pull/236 + +### Version 0.5.4.0 (2018-05-23) + * Support GHC-8.4 (Semigroup/Monoid) ### Version 0.5.3.0 (2017-05-15) * Refactored some rudimentary cursor handling code out of the @@ -260,7 +341,7 @@ * De-emphasized connect and ConnectInfo in favor of connectPostgreSQL. ### Version 0.4.2.2 (2014-05-15) - * Fixed compatibility with scientific-0.3.*, thanks to Adam Bergmark + * Fixed compatibility with scientific-0.3.\*, thanks to Adam Bergmark * Improved documentation of the FromField module, as well as the fold, foldWithOptions, executeMany, and returning operators. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..3df30fc0 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,12 @@ +Patches welcome! + +- If you are only going to bump bounds: + - If it's really **only bounds**, please simply open an issue (so you'll have a URL to refer to). I have a semi-automated process to make revisions, pull requests only disturb it. + - If patch includes **source code change** (i.e. I'll need to make a release), and it's a patch to support **newer base/GHC version**: + - Amend `tested-with` to include that GHC + - Regenerate `.travis.yml` with `haskell-ci regenerate` (get the latest from [GitHub haskell-ci/haskell-ci](https://github.com/haskell-ci/haskell-ci)) + +- Don't edit `CHANGELOG.md`, rather include a copyable entry in your pull request description. I often process pull requests in bulk, and everyone editing the `CHANGELOG.md` causes unnecessary conflicts. + +- I use [`stylish-haskell`](https://github.com/jaspervdj/stylish-haskell) to format imports. I encourage you to use it too, when contributing. +- General code style is 4 spaces, just look around how it looks, it's not so strict. diff --git a/bench/Select.hs b/bench/Select.hs new file mode 100644 index 00000000..ac442a39 --- /dev/null +++ b/bench/Select.hs @@ -0,0 +1,32 @@ +{-# LANGUAGE OverloadedStrings #-} +module Main where + +import Database.PostgreSQL.Simple +import qualified Database.PostgreSQL.Simple.Vector as V +import qualified Database.PostgreSQL.Simple.Vector.Unboxed as VU + +import System.Environment (getArgs) +import Data.Foldable (Foldable, foldl') +import qualified Data.Vector.Unboxed as VU + +main :: IO () +main = do + args <- getArgs + conn <- connectPostgreSQL "" + case args of + ("vector":_) -> do + result <- V.query_ conn "SELECT * FROM generate_series(1, 10000000);" + print (process result) + ("unboxed":_) -> do + -- dummy column + result <- VU.query_ conn "SELECT (NULL :: VOID), * FROM generate_series(1, 10000000);" + print (process' result) + _ -> do + result <- query_ conn "SELECT * FROM generate_series(1, 10000000);" + print (process result) + +process :: Foldable f => f (Only Int) -> Int +process = foldl' (\x (Only y) -> max x y) 0 + +process' :: VU.Vector ((), Int) -> Int +process' = VU.foldl' (\x (_, y) -> max x y) 0 diff --git a/cabal.haskell-ci b/cabal.haskell-ci new file mode 100644 index 00000000..b6d4e71f --- /dev/null +++ b/cabal.haskell-ci @@ -0,0 +1,5 @@ +branches: master +postgresql: True + +-- services aren't supported on Travis on macOS +-- osx: 8.4.4 diff --git a/cabal.project b/cabal.project index e6fdbadb..b0a523c7 100644 --- a/cabal.project +++ b/cabal.project @@ -1 +1,2 @@ packages: . +tests: true diff --git a/postgresql-simple.cabal b/postgresql-simple.cabal index 61c39c8d..25c228bb 100644 --- a/postgresql-simple.cabal +++ b/postgresql-simple.cabal @@ -1,136 +1,191 @@ -Name: postgresql-simple -Version: 0.5.3.0 -Synopsis: Mid-Level PostgreSQL client library -Description: - Mid-Level PostgreSQL client library, forked from mysql-simple. -License: BSD3 -License-file: LICENSE -Author: Bryan O'Sullivan, Leon P Smith -Maintainer: Leon P Smith -Copyright: (c) 2011 MailRank, Inc. - (c) 2011-2015 Leon P Smith -Category: Database -Build-type: Simple - -Cabal-version: >= 1.9.2 - +cabal-version: 1.12 +name: postgresql-simple +version: 0.6.2 +x-revision: 2 +synopsis: Mid-Level PostgreSQL client library +description: + Mid-Level PostgreSQL client library, forked from mysql-simple. + +license: BSD3 +license-file: LICENSE +author: Bryan O'Sullivan, Leon P Smith +maintainer: Oleg Grenrus +copyright: + (c) 2011 MailRank, Inc. + (c) 2011-2018 Leon P Smith + (c) 2018-2019 Oleg Grenrus + +category: Database +build-type: Simple extra-source-files: - CONTRIBUTORS - CHANGELOG.md - -Library - hs-source-dirs: src - Exposed-modules: - Database.PostgreSQL.Simple - Database.PostgreSQL.Simple.Arrays - Database.PostgreSQL.Simple.Copy - Database.PostgreSQL.Simple.Cursor - Database.PostgreSQL.Simple.FromField - Database.PostgreSQL.Simple.FromRow - Database.PostgreSQL.Simple.LargeObjects - Database.PostgreSQL.Simple.HStore - Database.PostgreSQL.Simple.HStore.Internal - Database.PostgreSQL.Simple.Notification - Database.PostgreSQL.Simple.Ok - Database.PostgreSQL.Simple.Range - Database.PostgreSQL.Simple.SqlQQ - Database.PostgreSQL.Simple.Time - Database.PostgreSQL.Simple.Time.Internal - Database.PostgreSQL.Simple.ToField - Database.PostgreSQL.Simple.ToRow - Database.PostgreSQL.Simple.Transaction - Database.PostgreSQL.Simple.TypeInfo - Database.PostgreSQL.Simple.TypeInfo.Macro - Database.PostgreSQL.Simple.TypeInfo.Static - Database.PostgreSQL.Simple.Types - Database.PostgreSQL.Simple.Errors --- Other-modules: - Database.PostgreSQL.Simple.Internal - - Other-modules: - Database.PostgreSQL.Simple.Compat - Database.PostgreSQL.Simple.HStore.Implementation - Database.PostgreSQL.Simple.Internal.PQResultUtils - Database.PostgreSQL.Simple.Time.Implementation - Database.PostgreSQL.Simple.Time.Internal.Parser - Database.PostgreSQL.Simple.Time.Internal.Printer - Database.PostgreSQL.Simple.TypeInfo.Types - - Build-depends: - aeson >= 0.6, - attoparsec >= 0.10.3, - base >= 4.6 && < 5, - bytestring >= 0.9, - bytestring-builder, - case-insensitive, - containers, - hashable, - Only, - postgresql-libpq >= 0.9 && < 0.10, - template-haskell, - text >= 0.11.1, - time, - transformers, - uuid-types >= 1.0.0, - scientific, - semigroups, - vector - - if !impl(ghc >= 7.6) - Build-depends: - ghc-prim - - extensions: DoAndIfThenElse, OverloadedStrings, BangPatterns, ViewPatterns - TypeOperators - - ghc-options: -Wall -fno-warn-name-shadowing + CONTRIBUTORS + CHANGES.md + test/results/malformed-input.expected + test/results/unique-constraint-violation.expected + +tested-with: + GHC ==7.6.3 + || ==7.8.4 + || ==7.10.3 + || ==8.0.2 + || ==8.2.2 + || ==8.4.4 + || ==8.6.5 + || ==8.8.1 + +library + default-language: Haskell2010 + hs-source-dirs: src + exposed-modules: + Database.PostgreSQL.Simple + Database.PostgreSQL.Simple.Arrays + Database.PostgreSQL.Simple.Copy + Database.PostgreSQL.Simple.Cursor + Database.PostgreSQL.Simple.Errors + Database.PostgreSQL.Simple.FromField + Database.PostgreSQL.Simple.FromRow + Database.PostgreSQL.Simple.HStore + Database.PostgreSQL.Simple.HStore.Internal + Database.PostgreSQL.Simple.Internal + Database.PostgreSQL.Simple.LargeObjects + Database.PostgreSQL.Simple.Notification + Database.PostgreSQL.Simple.Ok + Database.PostgreSQL.Simple.Range + Database.PostgreSQL.Simple.SqlQQ + Database.PostgreSQL.Simple.Time + Database.PostgreSQL.Simple.Time.Internal + Database.PostgreSQL.Simple.ToField + Database.PostgreSQL.Simple.ToRow + Database.PostgreSQL.Simple.Transaction + Database.PostgreSQL.Simple.TypeInfo + Database.PostgreSQL.Simple.TypeInfo.Macro + Database.PostgreSQL.Simple.TypeInfo.Static + Database.PostgreSQL.Simple.Types + Database.PostgreSQL.Simple.Vector + Database.PostgreSQL.Simple.Vector.Unboxed + + -- Other-modules: + other-modules: + Database.PostgreSQL.Simple.Compat + Database.PostgreSQL.Simple.HStore.Implementation + Database.PostgreSQL.Simple.Internal.PQResultUtils + Database.PostgreSQL.Simple.Time.Implementation + Database.PostgreSQL.Simple.Time.Internal.Parser + Database.PostgreSQL.Simple.Time.Internal.Printer + Database.PostgreSQL.Simple.TypeInfo.Types + + -- GHC bundled libs + build-depends: + base >=4.6.0.0 && <4.14 + , bytestring >=0.10.0.0 && <0.11 + , containers >=0.5.0.0 && <0.7 + , template-haskell >=2.8.0.0 && <2.16 + , text >=1.2.3.0 && <1.3 + , time >=1.4.0.1 && <1.10 + , transformers >=0.3.0.0 && <0.6 + + -- Other dependencies + build-depends: + aeson >=1.4.1.0 && <1.5 + , attoparsec >=0.13.2.2 && <0.14 + , bytestring-builder >=0.10.8.1.0 && <0.11 + , case-insensitive >=1.2.0.11 && <1.3 + , hashable >=1.2.7.0 && <1.4 + , Only >=0.1 && <0.1.1 + , postgresql-libpq >=0.9.4.2 && <0.10 + , scientific >=0.3.6.2 && <0.4 + , uuid-types >=1.0.3 && <1.1 + , vector >=0.12.0.1 && <0.13 + + if !impl(ghc >=8.0) + build-depends: + fail >=4.9.0.0 && <4.10 + , semigroups >=0.18.5 && <0.20 + + if !impl(ghc >=7.6) + build-depends: ghc-prim + + default-extensions: + BangPatterns + DoAndIfThenElse + OverloadedStrings + TypeOperators + ViewPatterns + + ghc-options: -Wall -fno-warn-name-shadowing source-repository head type: git - location: http://github.com/lpsmith/postgresql-simple + location: http://github.com/phadej/postgresql-simple source-repository this type: git - location: http://github.com/lpsmith/postgresql-simple - tag: v0.5.3.0 + location: http://github.com/phadej/postgresql-simple + tag: v0.6 + +test-suite inspection + if !impl(ghc >=8.0) + buildable: False + + default-language: Haskell2010 + type: exitcode-stdio-1.0 + hs-source-dirs: test + main-is: Inspection.hs + build-depends: + base + , inspection-testing >=0.4.1.1 && <0.5 + , postgresql-libpq + , postgresql-simple + , tasty + , tasty-hunit test-suite test - type: exitcode-stdio-1.0 - - hs-source-dirs: test - main-is: Main.hs + default-language: Haskell2010 + type: exitcode-stdio-1.0 + hs-source-dirs: test + main-is: Main.hs other-modules: Common Notify Serializable Time - ghc-options: -Wall -fno-warn-name-shadowing -fno-warn-unused-do-bind - - extensions: NamedFieldPuns - , OverloadedStrings - , Rank2Types - , RecordWildCards - , PatternGuards - , ScopedTypeVariables - - build-depends: base - , aeson - , base16-bytestring - , bytestring - , containers - , cryptohash - , filepath - , tasty - , tasty-hunit - , tasty-golden - , HUnit - , postgresql-simple - , text - , time - , vector - , case-insensitive - - if !impl(ghc >= 7.6) - build-depends: - ghc-prim + ghc-options: -Wall -fno-warn-name-shadowing -fno-warn-unused-do-bind + default-extensions: + NamedFieldPuns + OverloadedStrings + PatternGuards + Rank2Types + RecordWildCards + ScopedTypeVariables + + build-depends: + aeson + , base + , base16-bytestring + , bytestring + , case-insensitive + , containers + , cryptohash-md5 >=0.11.100.1 && <0.12 + , filepath + , HUnit + , postgresql-simple + , tasty + , tasty-golden + , tasty-hunit + , text + , time + , vector + + if !impl(ghc >=7.6) + build-depends: ghc-prim + +benchmark select + default-language: Haskell2010 + type: exitcode-stdio-1.0 + hs-source-dirs: bench + main-is: Select.hs + build-depends: + base + , postgresql-simple + , vector diff --git a/src/Database/PostgreSQL/Simple.hs b/src/Database/PostgreSQL/Simple.hs index 684a8399..02f7a9ec 100644 --- a/src/Database/PostgreSQL/Simple.hs +++ b/src/Database/PostgreSQL/Simple.hs @@ -276,21 +276,31 @@ parseTemplate template = skipSpace = B.dropWhile isSpace_ascii - buildQuery :: Connection -> Query -> ByteString -> [Action] -> IO Builder buildQuery conn q template xs = zipParams (split template) <$> mapM (buildAction conn q xs) xs where split s = - let (h,t) = B.break (=='?') s + -- This part escapes double '??'s to make literal '?'s possible + -- in PostgreSQL queries using the JSON operators: @?@, @?|@ and @?&@ + let (h,t) = breakOnSingleQuestionMark s in byteString h : if B.null t then [] else split (B.tail t) zipParams (t:ts) (p:ps) = t <> p <> zipParams ts ps zipParams [t] [] = t - zipParams _ _ = fmtError (show (B.count '?' template) ++ - " '?' characters, but " ++ + zipParams _ _ = fmtError (show countSingleQs ++ + " single '?' characters, but " ++ show (length xs) ++ " parameters") q xs + countSingleQs = go 0 template + where go i "" = (i :: Int) + go i bs = case qms of + ("?","?") -> go i nextQMBS + ("?",_) -> go (i+1) nextQMBS + _ -> i + where qms = B.splitAt 1 qmBS + (qmBS,nextQMBS) = B.splitAt 2 qmBS' + qmBS' = B.dropWhile (/= '?') bs -- | Execute an @INSERT@, @UPDATE@, or other SQL query that is not -- expected to return results. @@ -328,7 +338,7 @@ execute conn template qs = do -- @ -- executeMany c [sql| -- UPDATE sometable --- SET sometable.y = upd.y +-- SET y = upd.y -- FROM (VALUES (?,?)) as upd(x,y) -- WHERE sometable.x = upd.x -- |] [(1, \"hello\"),(2, \"world\")] @@ -355,6 +365,7 @@ executeMany conn q qs = do returning :: (ToRow q, FromRow r) => Connection -> Query -> [q] -> IO [r] returning = returningWith fromRow +-- | A version of 'returning' taking parser as argument returningWith :: (ToRow q) => RowParser r -> Connection -> Query -> [q] -> IO [r] returningWith _ _ _ [] = return [] returningWith parser conn q qs = do @@ -520,7 +531,7 @@ foldWithOptions_ :: (FromRow r) => -> a -- ^ Initial state for result consumer. -> (a -> r -> IO a) -- ^ Result consumer. -> IO a -foldWithOptions_ opts conn query a f = doFold opts fromRow conn query query a f +foldWithOptions_ opts conn query' a f = doFold opts fromRow conn query' query' a f -- | A version of 'foldWithOptions_' taking a parser as an argument foldWithOptionsAndParser_ :: FoldOptions @@ -530,7 +541,7 @@ foldWithOptionsAndParser_ :: FoldOptions -> a -- ^ Initial state for result consumer. -> (a -> r -> IO a) -- ^ Result consumer. -> IO a -foldWithOptionsAndParser_ opts parser conn query a f = doFold opts parser conn query query a f +foldWithOptionsAndParser_ opts parser conn query' a f = doFold opts parser conn query' query' a f doFold :: FoldOptions -> RowParser row @@ -566,8 +577,8 @@ doFold FoldOptions{..} parser conn _template q a0 f = do go = bracket declare closeCursor $ \cursor -> let loop a = fetch cursor a >>= \r -> case r of - Left a -> return a - Right a -> loop a + Left x -> return x + Right x -> loop x in loop a0 -- FIXME: choose the Automatic chunkSize more intelligently diff --git a/src/Database/PostgreSQL/Simple/Arrays.hs b/src/Database/PostgreSQL/Simple/Arrays.hs index a7ef5c4f..300f5cd7 100644 --- a/src/Database/PostgreSQL/Simple/Arrays.hs +++ b/src/Database/PostgreSQL/Simple/Arrays.hs @@ -42,9 +42,9 @@ array delim = char '{' *> option [] (arrays <|> strings) <* char '}' quoted :: Parser ByteString quoted = char '"' *> option "" contents <* char '"' where - esc = char '\\' *> (char '\\' <|> char '"') + esc' = char '\\' *> (char '\\' <|> char '"') unQ = takeWhile1 (notInClass "\"\\") - contents = mconcat <$> many (unQ <|> B.singleton <$> esc) + contents = mconcat <$> many (unQ <|> B.singleton <$> esc') -- | Recognizes a plain string literal, not containing quotes or brackets and -- not containing the delimiter character. diff --git a/src/Database/PostgreSQL/Simple/Copy.hs b/src/Database/PostgreSQL/Simple/Copy.hs index e6ebd1e3..95f19bf8 100644 --- a/src/Database/PostgreSQL/Simple/Copy.hs +++ b/src/Database/PostgreSQL/Simple/Copy.hs @@ -48,7 +48,7 @@ import qualified Data.ByteString.Char8 as B import qualified Database.PostgreSQL.LibPQ as PQ import Database.PostgreSQL.Simple import Database.PostgreSQL.Simple.Types -import Database.PostgreSQL.Simple.Internal +import Database.PostgreSQL.Simple.Internal hiding (result, row) -- | Issue a @COPY FROM STDIN@ or @COPY TO STDOUT@ query. In the former diff --git a/src/Database/PostgreSQL/Simple/Cursor.hs b/src/Database/PostgreSQL/Simple/Cursor.hs index d077739c..85babd9b 100644 --- a/src/Database/PostgreSQL/Simple/Cursor.hs +++ b/src/Database/PostgreSQL/Simple/Cursor.hs @@ -30,7 +30,7 @@ import Data.Monoid (mconcat) import Database.PostgreSQL.Simple.Compat ((<>), toByteString) import Database.PostgreSQL.Simple.FromRow (FromRow(..)) import Database.PostgreSQL.Simple.Types (Query(..)) -import Database.PostgreSQL.Simple.Internal as Base +import Database.PostgreSQL.Simple.Internal as Base hiding (result, row) import Database.PostgreSQL.Simple.Internal.PQResultUtils import Database.PostgreSQL.Simple.Transaction import qualified Database.PostgreSQL.LibPQ as PQ @@ -90,9 +90,9 @@ foldForward cursor = foldForwardWithParser cursor fromRow foldM' :: (Ord n, Num n) => (a -> n -> IO a) -> a -> n -> n -> IO a foldM' f a lo hi = loop a lo where - loop a !n - | n > hi = return a + loop x !n + | n > hi = return x | otherwise = do - a' <- f a n - loop a' (n+1) + x' <- f x n + loop x' (n+1) {-# INLINE foldM' #-} diff --git a/src/Database/PostgreSQL/Simple/FromField.hs b/src/Database/PostgreSQL/Simple/FromField.hs index 983b53a5..f29db63b 100644 --- a/src/Database/PostgreSQL/Simple/FromField.hs +++ b/src/Database/PostgreSQL/Simple/FromField.hs @@ -1,7 +1,7 @@ {-# LANGUAGE CPP, DeriveDataTypeable, DeriveFunctor #-} {-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-} {-# LANGUAGE PatternGuards, ScopedTypeVariables #-} -{-# LANGUAGE RecordWildCards, TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} {- | Module: Database.PostgreSQL.Simple.FromField @@ -117,6 +117,7 @@ import Control.Applicative ( (<|>), (<$>), pure, (*>), (<*) ) import Control.Concurrent.MVar (MVar, newMVar) import Control.Exception (Exception) import qualified Data.Aeson as JSON +import qualified Data.Aeson.Internal as JSON import qualified Data.Aeson.Parser as JSON (value') import Data.Attoparsec.ByteString.Char8 hiding (Result) import Data.ByteString (ByteString) @@ -135,7 +136,6 @@ import Database.PostgreSQL.Simple.Ok import Database.PostgreSQL.Simple.Types import Database.PostgreSQL.Simple.TypeInfo as TI import qualified Database.PostgreSQL.Simple.TypeInfo.Static as TI -import Database.PostgreSQL.Simple.TypeInfo.Macro as TI import Database.PostgreSQL.Simple.Time import Database.PostgreSQL.Simple.Arrays as Arrays import qualified Database.PostgreSQL.LibPQ as PQ @@ -244,9 +244,11 @@ tableOid Field{..} = toMaybeOid (unsafeDupablePerformIO (PQ.ftable result column then Nothing else Just x --- | If the column has a table associated with it, this returns the number --- off the associated table column. Numbering starts from 0. Analogous --- to libpq's @PQftablecol@. +-- | If the column has a table associated with it, this returns the +-- number of the associated table column. Table columns have +-- nonzero numbers. Zero is returned if the specified column is not +-- a simple reference to a table column, or when using pre-3.0 +-- protocol. Analogous to libpq's @PQftablecol@. tableColumn :: Field -> Int tableColumn Field{..} = fromCol (unsafeDupablePerformIO (PQ.ftablecol result column)) @@ -262,7 +264,7 @@ format Field{..} = unsafeDupablePerformIO (PQ.fformat result column) -- | void instance FromField () where fromField f _bs - | typeOid f /= $(inlineTypoid TI.void) = returnError Incompatible f "" + | typeOid f /= TI.voidOid = returnError Incompatible f "" | otherwise = pure () -- | For dealing with null values. Compatible with any postgresql type @@ -292,7 +294,7 @@ instance FromField Null where -- | bool instance FromField Bool where fromField f bs - | typeOid f /= $(inlineTypoid TI.bool) = returnError Incompatible f "" + | typeOid f /= TI.boolOid = returnError Incompatible f "" | bs == Nothing = returnError UnexpectedNull f "" | bs == Just "t" = pure True | bs == Just "f" = pure False @@ -300,9 +302,9 @@ instance FromField Bool where -- | \"char\", bpchar instance FromField Char where - fromField f bs = - if $(mkCompats [TI.char,TI.bpchar]) (typeOid f) - then case bs of + fromField f bs0 = + if (eq TI.charOid \/ eq TI.bpcharOid) (typeOid f) + then case bs0 of Nothing -> returnError UnexpectedNull f "" Just bs -> if B.length bs /= 1 then returnError ConversionFailed f "length not 1" @@ -339,23 +341,23 @@ instance FromField Integer where -- better accuracy convert to 'Scientific' or 'Rational' first) instance FromField Float where fromField = atto ok (realToFrac <$> pg_double) - where ok = $(mkCompats [TI.float4,TI.int2]) + where ok = eq TI.float4Oid \/ eq TI.int2Oid -- | int2, int4, float4, float8 (Uses attoparsec's 'double' routine, for -- better accuracy convert to 'Scientific' or 'Rational' first) instance FromField Double where fromField = atto ok pg_double - where ok = $(mkCompats [TI.float4,TI.float8,TI.int2,TI.int4]) + where ok = eq TI.float4Oid \/ eq TI.float8Oid \/ eq TI.int2Oid \/ eq TI.int4Oid -- | int2, int4, int8, float4, float8, numeric instance FromField (Ratio Integer) where fromField = atto ok pg_rational - where ok = $(mkCompats [TI.float4,TI.float8,TI.int2,TI.int4,TI.int8,TI.numeric]) + where ok = eq TI.float4Oid \/ eq TI.float8Oid \/ eq TI.int2Oid \/ eq TI.int4Oid \/ eq TI.int8Oid \/ eq TI.numericOid -- | int2, int4, int8, float4, float8, numeric instance FromField Scientific where fromField = atto ok rational - where ok = $(mkCompats [TI.float4,TI.float8,TI.int2,TI.int4,TI.int8,TI.numeric]) + where ok = eq TI.float4Oid \/ eq TI.float8Oid \/ eq TI.int2Oid \/ eq TI.int4Oid \/ eq TI.int8Oid \/ eq TI.numericOid unBinary :: Binary t -> t unBinary (Binary x) = x @@ -376,13 +378,13 @@ pg_rational -- | bytea, name, text, \"char\", bpchar, varchar, unknown instance FromField SB.ByteString where - fromField f dat = if typeOid f == $(inlineTypoid TI.bytea) + fromField f dat = if typeOid f == TI.byteaOid then unBinary <$> fromField f dat else doFromField f okText' pure dat -- | oid instance FromField PQ.Oid where - fromField f dat = PQ.Oid <$> atto (== $(inlineTypoid TI.oid)) decimal f dat + fromField f dat = PQ.Oid <$> atto (== TI.oidOid) decimal f dat -- | bytea, name, text, \"char\", bpchar, varchar, unknown instance FromField LB.ByteString where @@ -390,7 +392,7 @@ instance FromField LB.ByteString where unescapeBytea :: Field -> SB.ByteString -> Conversion (Binary SB.ByteString) -unescapeBytea f str = case unsafeDupablePerformIO (PQ.unescapeBytea str) of +unescapeBytea f str' = case unsafeDupablePerformIO (PQ.unescapeBytea str') of Nothing -> returnError ConversionFailed f "unescapeBytea failed" Just str -> pure (Binary str) @@ -441,48 +443,48 @@ instance FromField [Char] where -- | timestamptz instance FromField UTCTime where - fromField = ff $(inlineTypoid TI.timestamptz) "UTCTime" parseUTCTime + fromField = ff TI.timestamptzOid "UTCTime" parseUTCTime -- | timestamptz instance FromField ZonedTime where - fromField = ff $(inlineTypoid TI.timestamptz) "ZonedTime" parseZonedTime + fromField = ff TI.timestamptzOid "ZonedTime" parseZonedTime -- | timestamp instance FromField LocalTime where - fromField = ff $(inlineTypoid TI.timestamp) "LocalTime" parseLocalTime + fromField = ff TI.timestampOid "LocalTime" parseLocalTime -- | date instance FromField Day where - fromField = ff $(inlineTypoid TI.date) "Day" parseDay + fromField = ff TI.dateOid "Day" parseDay -- | time instance FromField TimeOfDay where - fromField = ff $(inlineTypoid TI.time) "TimeOfDay" parseTimeOfDay + fromField = ff TI.timeOid "TimeOfDay" parseTimeOfDay -- | timestamptz instance FromField UTCTimestamp where - fromField = ff $(inlineTypoid TI.timestamptz) "UTCTimestamp" parseUTCTimestamp + fromField = ff TI.timestamptzOid "UTCTimestamp" parseUTCTimestamp -- | timestamptz instance FromField ZonedTimestamp where - fromField = ff $(inlineTypoid TI.timestamptz) "ZonedTimestamp" parseZonedTimestamp + fromField = ff TI.timestamptzOid "ZonedTimestamp" parseZonedTimestamp -- | timestamp instance FromField LocalTimestamp where - fromField = ff $(inlineTypoid TI.timestamp) "LocalTimestamp" parseLocalTimestamp + fromField = ff TI.timestampOid "LocalTimestamp" parseLocalTimestamp -- | date instance FromField Date where - fromField = ff $(inlineTypoid TI.date) "Date" parseDate + fromField = ff TI.dateOid "Date" parseDate ff :: PQ.Oid -> String -> (B8.ByteString -> Either String a) -> Field -> Maybe B8.ByteString -> Conversion a -ff compatOid hsType parse f mstr = +ff compatOid hsType parseBS f mstr = if typeOid f /= compatOid then err Incompatible "" else case mstr of Nothing -> err UnexpectedNull "" - Just str -> case parse str of + Just str -> case parseBS str of Left msg -> err ConversionFailed msg Right val -> return val where @@ -520,10 +522,10 @@ pgArrayFieldParser fieldParser f mdat = do _ -> returnError Incompatible f "" fromArray :: FieldParser a -> TypeInfo -> Field -> Parser (Conversion [a]) -fromArray fieldParser typeInfo f = sequence . (parseIt <$>) <$> array delim +fromArray fieldParser typInfo f = sequence . (parseIt <$>) <$> array delim where - delim = typdelim (typelem typeInfo) - fElem = f{ typeOid = typoid (typelem typeInfo) } + delim = typdelim (typelem typInfo) + fElem = f{ typeOid = typoid (typelem typInfo) } parseIt item = fieldParser f' $ if item == Arrays.Plain "NULL" then Nothing else Just item' @@ -541,7 +543,7 @@ instance (FromField a, Typeable a) => FromField (IOVector a) where -- | uuid instance FromField UUID where fromField f mbs = - if typeOid f /= $(inlineTypoid TI.uuid) + if typeOid f /= TI.uuidOid then returnError Incompatible f "" else case mbs of Nothing -> returnError UnexpectedNull f "" @@ -552,22 +554,22 @@ instance FromField UUID where -- | json, jsonb instance FromField JSON.Value where - fromField f mbs = parse =<< fromFieldJSONByteString f mbs - where parse bs = case parseOnly (JSON.value' <* endOfInput) bs of + fromField f mbs = parseBS =<< fromFieldJSONByteString f mbs + where parseBS bs = case parseOnly (JSON.value' <* endOfInput) bs of Left err -> returnError ConversionFailed f err Right val -> pure val -- | Return the JSON ByteString directly fromFieldJSONByteString :: Field -> Maybe ByteString -> Conversion ByteString fromFieldJSONByteString f mbs = - if typeOid f /= $(inlineTypoid TI.json) && typeOid f /= $(inlineTypoid TI.jsonb) + if typeOid f /= TI.jsonOid && typeOid f /= TI.jsonbOid then returnError Incompatible f "" else case mbs of Nothing -> returnError UnexpectedNull f "" Just bs -> pure bs -- | Parse a field to a JSON 'JSON.Value' and convert that into a --- Haskell value using 'JSON.fromJSON'. +-- Haskell value using the 'JSON.FromJSON' instance. -- -- This can be used as the default implementation for the 'fromField' -- method for Haskell types that have a JSON representation in @@ -588,10 +590,10 @@ fromFieldJSONByteString f mbs = fromJSONField :: (JSON.FromJSON a, Typeable a) => FieldParser a fromJSONField f mbBs = do value <- fromField f mbBs - case JSON.fromJSON value of - JSON.Error err -> returnError ConversionFailed f $ - "JSON decoding error: " ++ err - JSON.Success x -> pure x + case JSON.ifromJSON value of + JSON.IError path err -> returnError ConversionFailed f $ + "JSON decoding error: " ++ (JSON.formatError path err) + JSON.ISuccess x -> pure x -- | Compatible with the same set of types as @a@. Note that -- modifying the 'IORef' does not have any effects outside @@ -608,20 +610,33 @@ instance FromField a => FromField (MVar a) where type Compat = PQ.Oid -> Bool okText, okText', okBinary, ok16, ok32, ok64, okInt :: Compat -okText = $( mkCompats [ TI.name, TI.text, TI.char, - TI.bpchar, TI.varchar ] ) -okText' = $( mkCompats [ TI.name, TI.text, TI.char, - TI.bpchar, TI.varchar, TI.unknown ] ) -okBinary = (== $( inlineTypoid TI.bytea )) -ok16 = (== $( inlineTypoid TI.int2 )) -ok32 = $( mkCompats [TI.int2,TI.int4] ) -ok64 = $( mkCompats [TI.int2,TI.int4,TI.int8] ) +okText = eq TI.nameOid \/ eq TI.textOid \/ eq TI.charOid + \/ eq TI.bpcharOid \/ eq TI.varcharOid +okText' = eq TI.nameOid \/ eq TI.textOid \/ eq TI.charOid + \/ eq TI.bpcharOid \/ eq TI.varcharOid \/ eq TI.unknownOid +okBinary = eq TI.byteaOid +ok16 = eq TI.int2Oid +ok32 = eq TI.int2Oid \/ eq TI.int4Oid +ok64 = eq TI.int2Oid \/ eq TI.int4Oid \/ eq TI.int8Oid #if WORD_SIZE_IN_BITS < 64 okInt = ok32 #else okInt = ok64 #endif +-- | eq and \/ are used to imlement what Macro stuff did, +-- i.e. mkCompats and inlineTypoid +eq :: PQ.Oid -> PQ.Oid -> Bool +eq = (==) +{-# INLINE eq #-} + +infixr 2 \/ +(\/) :: (PQ.Oid -> Bool) + -> (PQ.Oid -> Bool) + -> (PQ.Oid -> Bool) +f \/ g = \x -> f x || g x +{-# INLINE (\/) #-} + doFromField :: forall a . (Typeable a) => Field -> Compat -> (ByteString -> Conversion a) -> Maybe ByteString -> Conversion a diff --git a/src/Database/PostgreSQL/Simple/FromRow.hs b/src/Database/PostgreSQL/Simple/FromRow.hs index e2ba1af9..a448d9ae 100644 --- a/src/Database/PostgreSQL/Simple/FromRow.hs +++ b/src/Database/PostgreSQL/Simple/FromRow.hs @@ -67,6 +67,23 @@ import GHC.Generics -- in a single row of the query result. Otherwise, a 'ConversionFailed' -- exception will be thrown. -- +-- You can also derive 'FromRow' for your data type using GHC generics, like +-- this: +-- +-- @ +-- \{-# LANGUAGE DeriveAnyClass \#-} +-- \{-# LANGUAGE DeriveGeneric \#-} +-- +-- import "GHC.Generics" ('GHC.Generics.Generic') +-- import "Database.PostgreSQL.Simple" ('FromRow') +-- +-- data User = User { name :: String, fileQuota :: Int } +-- deriving ('GHC.Generics.Generic', 'FromRow') +-- @ +-- +-- Note that this only works for product types (e.g. records) and does not +-- support sum types or recursive types. +-- -- Note that 'field' evaluates its result to WHNF, so the caveats listed in -- mysql-simple and very early versions of postgresql-simple no longer apply. -- Instead, look at the caveats associated with user-defined implementations @@ -114,8 +131,8 @@ fieldWith fieldP = RP $ do else do let !result = rowresult !typeOid = unsafeDupablePerformIO (PQ.ftype result column) - !field = Field{..} - lift (lift (fieldP field (getvalue result row column))) + !field' = Field{..} + lift (lift (fieldP field' (getvalue result row column))) field :: FromField a => RowParser a field = fieldWith fromField diff --git a/src/Database/PostgreSQL/Simple/HStore/Implementation.hs b/src/Database/PostgreSQL/Simple/HStore/Implementation.hs index 6249fabb..2cc7c044 100644 --- a/src/Database/PostgreSQL/Simple/HStore/Implementation.hs +++ b/src/Database/PostgreSQL/Simple/HStore/Implementation.hs @@ -53,12 +53,12 @@ instance ToHStore HStoreBuilder where toBuilder :: HStoreBuilder -> Builder toBuilder x = case x of Empty -> mempty - Comma x -> x + Comma c -> c toLazyByteString :: HStoreBuilder -> BL.ByteString toLazyByteString x = case x of Empty -> BL.empty - Comma x -> BU.toLazyByteString x + Comma c -> BU.toLazyByteString c instance Semigroup HStoreBuilder where Empty <> x = x @@ -150,7 +150,7 @@ newtype HStoreMap = HStoreMap {fromHStoreMap :: Map Text Text} deriving (Eq, Or instance ToHStore HStoreMap where toHStore (HStoreMap xs) = Map.foldrWithKey f mempty xs - where f k v xs = hstore k v `mappend` xs + where f k v xs' = hstore k v `mappend` xs' instance ToField HStoreMap where toField xs = toField (toHStore xs) diff --git a/src/Database/PostgreSQL/Simple/Internal.hs b/src/Database/PostgreSQL/Simple/Internal.hs index 330ef08d..63365ea0 100644 --- a/src/Database/PostgreSQL/Simple/Internal.hs +++ b/src/Database/PostgreSQL/Simple/Internal.hs @@ -290,7 +290,7 @@ postgreSQLConnectionString connectInfo = fromString connstr str name field | null value = id - | otherwise = showString name . quote value . space + | otherwise = showString name . addQuotes value . space where value = field connectInfo num name field @@ -298,7 +298,7 @@ postgreSQLConnectionString connectInfo = fromString connstr | otherwise = showString name . shows value . space where value = field connectInfo - quote str rest = '\'' : foldr delta ('\'' : rest) str + addQuotes s rest = '\'' : foldr delta ('\'' : rest) s where delta c cs = case c of '\\' -> '\\' : '\\' : cs @@ -386,7 +386,7 @@ finishExecute _conn q result = do nstr <- PQ.cmdTuples result return $ case nstr of Nothing -> 0 -- is this appropriate? - Just str -> toInteger str + Just str -> mkInteger str PQ.TuplesOk -> do ncols <- PQ.nfields result throwIO $ QueryError ("execute resulted in " ++ show ncols ++ @@ -399,7 +399,7 @@ finishExecute _conn q result = do PQ.NonfatalError -> throwResultError "execute" result status PQ.FatalError -> throwResultError "execute" result status where - toInteger str = B8.foldl' delta 0 str + mkInteger str = B8.foldl' delta 0 str where delta acc c = if '0' <= c && c <= '9' @@ -414,8 +414,8 @@ throwResultError _ result status = do PQ.resultErrorField result PQ.DiagMessageDetail hint <- fromMaybe "" <$> PQ.resultErrorField result PQ.DiagMessageHint - state <- maybe "" id <$> PQ.resultErrorField result PQ.DiagSqlstate - throwIO $ SqlError { sqlState = state + state' <- maybe "" id <$> PQ.resultErrorField result PQ.DiagSqlstate + throwIO $ SqlError { sqlState = state' , sqlExecStatus = status , sqlErrorMsg = errormsg , sqlErrorDetail = detail @@ -589,3 +589,19 @@ escapeIdentifier = escapeWrap PQ.escapeIdentifier escapeByteaConn :: Connection -> ByteString -> IO (Either ByteString ByteString) escapeByteaConn = escapeWrap PQ.escapeByteaConn + +breakOnSingleQuestionMark :: ByteString -> (ByteString, ByteString) +breakOnSingleQuestionMark b = go (B8.empty, b) + where go (x,bs) = (x `B8.append` x',bs') + -- seperate from first QM + where tup@(noQ, restWithQ) = B8.break (=='?') bs + -- if end of query, just return + -- else check for second QM in 'go2' + (x', bs') = maybe tup go2 $ + -- drop found QM and peek at next char + B8.uncons restWithQ >>= B8.uncons . snd + -- another QM after the first means: + -- take literal QM and keep going. + go2 ('?', t2) = go (noQ `B8.snoc` '?',t2) + -- Anything else means + go2 _ = tup diff --git a/src/Database/PostgreSQL/Simple/Internal/PQResultUtils.hs b/src/Database/PostgreSQL/Simple/Internal/PQResultUtils.hs index 59dc81c5..d1dac274 100644 --- a/src/Database/PostgreSQL/Simple/Internal/PQResultUtils.hs +++ b/src/Database/PostgreSQL/Simple/Internal/PQResultUtils.hs @@ -14,32 +14,66 @@ module Database.PostgreSQL.Simple.Internal.PQResultUtils ( finishQueryWith + , finishQueryWithV + , finishQueryWithVU , getRowWith ) where import Control.Exception as E import Data.ByteString (ByteString) +import Data.Foldable (for_) import Database.PostgreSQL.Simple.FromField (ResultError(..)) import Database.PostgreSQL.Simple.Ok import Database.PostgreSQL.Simple.Types (Query(..)) -import Database.PostgreSQL.Simple.Internal as Base +import Database.PostgreSQL.Simple.Internal as Base hiding (result, row) import Database.PostgreSQL.Simple.TypeInfo import qualified Database.PostgreSQL.LibPQ as PQ import qualified Data.ByteString.Char8 as B +import qualified Data.Vector as V +import qualified Data.Vector.Mutable as MV +import qualified Data.Vector.Unboxed as VU +import qualified Data.Vector.Unboxed.Mutable as MVU import Control.Monad.Trans.Reader import Control.Monad.Trans.State.Strict finishQueryWith :: RowParser r -> Connection -> Query -> PQ.Result -> IO [r] -finishQueryWith parser conn q result = do +finishQueryWith parser conn q result = finishQueryWith' q result $ do + nrows <- PQ.ntuples result + ncols <- PQ.nfields result + forM' 0 (nrows-1) $ \row -> + getRowWith parser row ncols conn result + +finishQueryWithV :: RowParser r -> Connection -> Query -> PQ.Result -> IO (V.Vector r) +finishQueryWithV parser conn q result = finishQueryWith' q result $ do + nrows <- PQ.ntuples result + let PQ.Row nrows' = nrows + ncols <- PQ.nfields result + mv <- MV.unsafeNew (fromIntegral nrows') + for_ [ 0 .. nrows-1 ] $ \row -> do + let PQ.Row row' = row + value <- getRowWith parser row ncols conn result + MV.unsafeWrite mv (fromIntegral row') value + V.unsafeFreeze mv + +finishQueryWithVU :: VU.Unbox r => RowParser r -> Connection -> Query -> PQ.Result -> IO (VU.Vector r) +finishQueryWithVU parser conn q result = finishQueryWith' q result $ do + nrows <- PQ.ntuples result + let PQ.Row nrows' = nrows + ncols <- PQ.nfields result + mv <- MVU.unsafeNew (fromIntegral nrows') + for_ [ 0 .. nrows-1 ] $ \row -> do + let PQ.Row row' = row + value <- getRowWith parser row ncols conn result + MVU.unsafeWrite mv (fromIntegral row') value + VU.unsafeFreeze mv + +finishQueryWith' :: Query -> PQ.Result -> IO a -> IO a +finishQueryWith' q result k = do status <- PQ.resultStatus result case status of - PQ.TuplesOk -> do - nrows <- PQ.ntuples result - ncols <- PQ.nfields result - forM' 0 (nrows-1) $ \row -> - getRowWith parser row ncols conn result + PQ.TuplesOk -> k PQ.EmptyQuery -> queryErr "query: Empty query" - PQ.CommandOk -> queryErr "query resulted in a command response" + PQ.CommandOk -> queryErr "query resulted in a command response (did you mean to use `execute` or forget a RETURNING?)" PQ.CopyOut -> queryErr "query: COPY TO is not supported" PQ.CopyIn -> queryErr "query: COPY FROM is not supported" #if MIN_VERSION_postgresql_libpq(0,9,3) diff --git a/src/Database/PostgreSQL/Simple/Ok.hs b/src/Database/PostgreSQL/Simple/Ok.hs index 5030b1ee..0f46c946 100644 --- a/src/Database/PostgreSQL/Simple/Ok.hs +++ b/src/Database/PostgreSQL/Simple/Ok.hs @@ -37,6 +37,8 @@ import Control.Exception import Control.Monad(MonadPlus(..)) import Data.Typeable +import qualified Control.Monad.Fail as Fail + -- FIXME: [SomeException] should probably be something else, maybe -- a difference list (or a tree?) @@ -77,6 +79,11 @@ instance Monad Ok where Errors es >>= _ = Errors es Ok a >>= f = f a +#if !(MIN_VERSION_base(4,13,0)) + fail = Fail.fail +#endif + +instance Fail.MonadFail Ok where fail str = Errors [SomeException (ErrorCall str)] -- | a way to reify a list of exceptions into a single exception diff --git a/src/Database/PostgreSQL/Simple/Range.hs b/src/Database/PostgreSQL/Simple/Range.hs index 07397585..94a31f47 100644 --- a/src/Database/PostgreSQL/Simple/Range.hs +++ b/src/Database/PostgreSQL/Simple/Range.hs @@ -110,19 +110,19 @@ containsBy cmp rng x = PGRange _lb NegInfinity -> False PGRange lb ub -> checkLB lb x && checkUB ub x where - checkLB lb x = + checkLB lb y = case lb of NegInfinity -> True PosInfinity -> False - Inclusive a -> cmp a x /= GT - Exclusive a -> cmp a x == LT + Inclusive a -> cmp a y /= GT + Exclusive a -> cmp a y == LT - checkUB ub x = + checkUB ub y = case ub of NegInfinity -> False PosInfinity -> True - Inclusive z -> cmp x z /= GT - Exclusive z -> cmp x z == LT + Inclusive z -> cmp y z /= GT + Exclusive z -> cmp y z == LT lowerBound :: Parser (a -> RangeBound a) lowerBound = (A.char '(' *> pure Exclusive) <|> (A.char '[' *> pure Inclusive) diff --git a/src/Database/PostgreSQL/Simple/SqlQQ.hs b/src/Database/PostgreSQL/Simple/SqlQQ.hs index b105fb4e..91c9e4a7 100644 --- a/src/Database/PostgreSQL/Simple/SqlQQ.hs +++ b/src/Database/PostgreSQL/Simple/SqlQQ.hs @@ -1,4 +1,9 @@ +{-# LANGUAGE CPP #-} +#if __GLASGOW_HASKELL__ >= 800 +{-# LANGUAGE TemplateHaskellQuotes #-} +#else {-# LANGUAGE TemplateHaskell #-} +#endif ------------------------------------------------------------------------------ -- | -- Module: Database.PostgreSQL.Simple.SqlQQ @@ -52,13 +57,10 @@ import Data.String sql :: QuasiQuoter sql = QuasiQuoter - { quotePat = error "Database.PostgreSQL.Simple.SqlQQ.sql:\ - \ quasiquoter used in pattern context" - , quoteType = error "Database.PostgreSQL.Simple.SqlQQ.sql:\ - \ quasiquoter used in type context" + { quotePat = error "Database.PostgreSQL.Simple.SqlQQ.sql: quasiquoter used in pattern context" + , quoteType = error "Database.PostgreSQL.Simple.SqlQQ.sql: quasiquoter used in type context" , quoteExp = sqlExp - , quoteDec = error "Database.PostgreSQL.Simple.SqlQQ.sql:\ - \ quasiquoter used in declaration context" + , quoteDec = error "Database.PostgreSQL.Simple.SqlQQ.sql: quasiquoter used in declaration context" } sqlExp :: String -> Q Exp @@ -85,5 +87,4 @@ minimizeSpace = drop 1 . reduceSpace instring ('\'':'\'':xs) = '\'':'\'': instring xs instring ('\'':xs) = '\'': insql xs instring (x:xs) = x : instring xs - instring [] = error "Database.PostgreSQL.Simple.SqlQQ.sql:\ - \ string literal not terminated" + instring [] = error "Database.PostgreSQL.Simple.SqlQQ.sql: string literal not terminated" diff --git a/src/Database/PostgreSQL/Simple/ToField.hs b/src/Database/PostgreSQL/Simple/ToField.hs index bc486339..0e50db17 100644 --- a/src/Database/PostgreSQL/Simple/ToField.hs +++ b/src/Database/PostgreSQL/Simple/ToField.hs @@ -319,7 +319,7 @@ inQuotes b = quote `mappend` b `mappend` quote where quote = char8 '\'' interleaveFoldr :: (a -> [b] -> [b]) -> b -> [b] -> [a] -> [b] -interleaveFoldr f b bs as = foldr (\a bs -> b : f a bs) bs as +interleaveFoldr f b bs' as = foldr (\a bs -> b : f a bs) bs' as {-# INLINE interleaveFoldr #-} instance ToRow a => ToField (Values a) where @@ -366,8 +366,8 @@ instance ToRow a => ToField (Values a) where typedRows :: ToRow a => [a] -> [QualifiedIdentifier] -> [Action] -> [Action] typedRows [] _ _ = error funcname - typedRows (val:vals) types rest = - typedRow (toRow val) types (multiRows vals rest) + typedRows (val:vals) typs rest = + typedRow (toRow val) typs (multiRows vals rest) untypedRows :: ToRow a => [a] -> [Action] -> [Action] untypedRows [] _ = error funcname diff --git a/src/Database/PostgreSQL/Simple/ToRow.hs b/src/Database/PostgreSQL/Simple/ToRow.hs index 00fe8fdc..516a384e 100644 --- a/src/Database/PostgreSQL/Simple/ToRow.hs +++ b/src/Database/PostgreSQL/Simple/ToRow.hs @@ -30,6 +30,22 @@ import GHC.Generics -- -- Instances should use the 'toField' method of the 'ToField' class -- to perform conversion of each element of the collection. +-- +-- You can derive 'ToRow' for your data type using GHC generics, like this: +-- +-- @ +-- \{-# LANGUAGE DeriveAnyClass \#-} +-- \{-# LANGUAGE DeriveGeneric \#-} +-- +-- import "GHC.Generics" ('GHC.Generics.Generic') +-- import "Database.PostgreSQL.Simple" ('ToRow') +-- +-- data User = User { name :: String, fileQuota :: Int } +-- deriving ('GHC.Generics.Generic', 'ToRow') +-- @ +-- +-- Note that this only works for product types (e.g. records) and does not +-- support sum types or recursive types. class ToRow a where toRow :: a -> [Action] default toRow :: (Generic a, GToRow (Rep a)) => a -> [Action] diff --git a/src/Database/PostgreSQL/Simple/Transaction.hs b/src/Database/PostgreSQL/Simple/Transaction.hs index c8f34349..7e201dfd 100644 --- a/src/Database/PostgreSQL/Simple/Transaction.hs +++ b/src/Database/PostgreSQL/Simple/Transaction.hs @@ -157,20 +157,19 @@ withTransactionModeRetry :: TransactionMode -> (SqlError -> Bool) -> Connection withTransactionModeRetry mode shouldRetry conn act = mask $ \restore -> retryLoop $ E.try $ do - a <- restore act + a <- restore act `E.onException` rollback_ conn commit conn return a where - retryLoop :: IO (Either E.SomeException a) -> IO a + retryLoop :: IO (Either SqlError a) -> IO a retryLoop act' = do beginMode mode conn r <- act' case r of - Left e -> do - rollback_ conn - case fmap shouldRetry (E.fromException e) of - Just True -> retryLoop act' - _ -> E.throwIO e + Left e -> + case shouldRetry e of + True -> retryLoop act' + False -> E.throwIO e Right a -> return a diff --git a/src/Database/PostgreSQL/Simple/TypeInfo.hs b/src/Database/PostgreSQL/Simple/TypeInfo.hs index 641a09b5..3d6bf3dd 100644 --- a/src/Database/PostgreSQL/Simple/TypeInfo.hs +++ b/src/Database/PostgreSQL/Simple/TypeInfo.hs @@ -50,21 +50,21 @@ import Database.PostgreSQL.Simple.TypeInfo.Static -- in the connections's cache. getTypeInfo :: Connection -> PQ.Oid -> IO TypeInfo -getTypeInfo conn@Connection{..} oid = - case staticTypeInfo oid of - Just name -> return name - Nothing -> modifyMVar connectionObjects $ getTypeInfo' conn oid +getTypeInfo conn@Connection{..} oid' = + case staticTypeInfo oid' of + Just name' -> return name' + Nothing -> modifyMVar connectionObjects $ getTypeInfo' conn oid' getTypeInfo' :: Connection -> PQ.Oid -> TypeInfoCache -> IO (TypeInfoCache, TypeInfo) -getTypeInfo' conn oid oidmap = - case IntMap.lookup (oid2int oid) oidmap of +getTypeInfo' conn oid' oidmap = + case IntMap.lookup (oid2int oid') oidmap of Just typeinfo -> return (oidmap, typeinfo) Nothing -> do names <- query conn "SELECT oid, typcategory, typdelim, typname,\ \ typelem, typrelid\ \ FROM pg_type WHERE oid = ?" - (Only oid) + (Only oid') (oidmap', typeInfo) <- case names of [] -> return $ throw (fatalError "invalid type oid") @@ -78,7 +78,7 @@ getTypeInfo' conn oid oidmap = rngsubtypeOids <- query conn "SELECT rngsubtype\ \ FROM pg_range\ \ WHERE rngtypid = ?" - (Only oid) + (Only oid') case rngsubtypeOids of [Only rngsubtype_] -> do (oidmap', rngsubtype) <- @@ -104,7 +104,7 @@ getTypeInfo' conn oid oidmap = _ -> fail "typename query returned more than one result" -- oid is a primary key, so the query should -- never return more than one result - let !oidmap'' = IntMap.insert (oid2int oid) typeInfo oidmap' + let !oidmap'' = IntMap.insert (oid2int oid') typeInfo oidmap' return $! (oidmap'', typeInfo) getAttInfos :: Connection -> [(B.ByteString, PQ.Oid)] -> TypeInfoCache diff --git a/src/Database/PostgreSQL/Simple/TypeInfo/Macro.hs b/src/Database/PostgreSQL/Simple/TypeInfo/Macro.hs index e7ca250f..30e36781 100644 --- a/src/Database/PostgreSQL/Simple/TypeInfo/Macro.hs +++ b/src/Database/PostgreSQL/Simple/TypeInfo/Macro.hs @@ -1,4 +1,9 @@ +{-# LANGUAGE CPP #-} +#if __GLASGOW_HASKELL__ >= 800 +{-# LANGUAGE TemplateHaskellQuotes #-} +#else {-# LANGUAGE TemplateHaskell #-} +#endif ------------------------------------------------------------------------------ -- | @@ -22,11 +27,12 @@ import Database.PostgreSQL.Simple.TypeInfo.Static import Database.PostgreSQL.Simple.Types (Oid(..)) import Language.Haskell.TH - -- | Returns an expression that has type @'Oid' -> 'Bool'@, true if the -- oid is equal to any one of the 'typoid's of the given 'TypeInfo's. mkCompats :: [TypeInfo] -> ExpQ -mkCompats tys = [| \(Oid x) -> $(caseE [| x |] (map alt tys ++ [catchAll])) |] +mkCompats tys = do + x <- newName "x" + lamE [conP 'Oid [varP x]] $ caseE (varE x) (map alt tys ++ [catchAll]) where alt :: TypeInfo -> MatchQ alt ty = match (inlineTypoidP ty) (normalB [| True |]) [] @@ -38,7 +44,7 @@ mkCompats tys = [| \(Oid x) -> $(caseE [| x |] (map alt tys ++ [catchAll])) |] -- Returns an expression of type 'Oid'. Useful because GHC tends -- not to fold constants. inlineTypoid :: TypeInfo -> ExpQ -inlineTypoid ty = [| Oid $(litE (getTypoid ty)) |] +inlineTypoid ty = conE 'Oid `appE` litE (getTypoid ty) inlineTypoidP :: TypeInfo -> PatQ inlineTypoidP ty = litP (getTypoid ty) diff --git a/src/Database/PostgreSQL/Simple/TypeInfo/Static.hs b/src/Database/PostgreSQL/Simple/TypeInfo/Static.hs index 8db176b3..234206ac 100644 --- a/src/Database/PostgreSQL/Simple/TypeInfo/Static.hs +++ b/src/Database/PostgreSQL/Simple/TypeInfo/Static.hs @@ -19,119 +19,233 @@ module Database.PostgreSQL.Simple.TypeInfo.Static ( TypeInfo(..) , staticTypeInfo , bool + , boolOid , bytea + , byteaOid , char + , charOid , name + , nameOid , int8 + , int8Oid , int2 + , int2Oid , int4 + , int4Oid , regproc + , regprocOid , text + , textOid , oid + , oidOid , tid + , tidOid , xid + , xidOid , cid + , cidOid , xml + , xmlOid , point + , pointOid , lseg + , lsegOid , path + , pathOid , box + , boxOid , polygon + , polygonOid , line + , lineOid , cidr + , cidrOid , float4 + , float4Oid , float8 + , float8Oid , unknown + , unknownOid , circle + , circleOid , money + , moneyOid , macaddr + , macaddrOid , inet + , inetOid , bpchar + , bpcharOid , varchar + , varcharOid , date + , dateOid , time + , timeOid , timestamp + , timestampOid , timestamptz + , timestamptzOid , interval + , intervalOid , timetz + , timetzOid , bit + , bitOid , varbit + , varbitOid , numeric + , numericOid , refcursor + , refcursorOid , record + , recordOid , void + , voidOid , array_record + , array_recordOid , regprocedure + , regprocedureOid , regoper + , regoperOid , regoperator + , regoperatorOid , regclass + , regclassOid , regtype + , regtypeOid , uuid + , uuidOid , json + , jsonOid , jsonb + , jsonbOid , int2vector + , int2vectorOid , oidvector + , oidvectorOid , array_xml + , array_xmlOid , array_json + , array_jsonOid , array_line + , array_lineOid , array_cidr + , array_cidrOid , array_circle + , array_circleOid , array_money + , array_moneyOid , array_bool + , array_boolOid , array_bytea + , array_byteaOid , array_char + , array_charOid , array_name + , array_nameOid , array_int2 + , array_int2Oid , array_int2vector + , array_int2vectorOid , array_int4 + , array_int4Oid , array_regproc + , array_regprocOid , array_text + , array_textOid , array_tid + , array_tidOid , array_xid + , array_xidOid , array_cid + , array_cidOid , array_oidvector + , array_oidvectorOid , array_bpchar + , array_bpcharOid , array_varchar + , array_varcharOid , array_int8 + , array_int8Oid , array_point + , array_pointOid , array_lseg + , array_lsegOid , array_path + , array_pathOid , array_box + , array_boxOid , array_float4 + , array_float4Oid , array_float8 + , array_float8Oid , array_polygon + , array_polygonOid , array_oid + , array_oidOid , array_macaddr + , array_macaddrOid , array_inet + , array_inetOid , array_timestamp + , array_timestampOid , array_date + , array_dateOid , array_time + , array_timeOid , array_timestamptz + , array_timestamptzOid , array_interval + , array_intervalOid , array_numeric + , array_numericOid , array_timetz + , array_timetzOid , array_bit + , array_bitOid , array_varbit + , array_varbitOid , array_refcursor + , array_refcursorOid , array_regprocedure + , array_regprocedureOid , array_regoper + , array_regoperOid , array_regoperator + , array_regoperatorOid , array_regclass + , array_regclassOid , array_regtype + , array_regtypeOid , array_uuid + , array_uuidOid , array_jsonb + , array_jsonbOid , int4range + , int4rangeOid , _int4range + , _int4rangeOid , numrange + , numrangeOid , _numrange + , _numrangeOid , tsrange + , tsrangeOid , _tsrange + , _tsrangeOid , tstzrange + , tstzrangeOid , _tstzrange + , _tstzrangeOid , daterange + , daterangeOid , _daterange + , _daterangeOid , int8range + , int8rangeOid , _int8range + , _int8rangeOid ) where import Database.PostgreSQL.LibPQ (Oid(..)) @@ -257,976 +371,1432 @@ staticTypeInfo (Oid x) = case x of bool :: TypeInfo bool = Basic { - typoid = Oid 16, + typoid = boolOid, typcategory = 'B', typdelim = ',', typname = "bool" } +boolOid :: Oid +boolOid = Oid 16 +{-# INLINE boolOid #-} + bytea :: TypeInfo bytea = Basic { - typoid = Oid 17, + typoid = byteaOid, typcategory = 'U', typdelim = ',', typname = "bytea" } +byteaOid :: Oid +byteaOid = Oid 17 +{-# INLINE byteaOid #-} + char :: TypeInfo char = Basic { - typoid = Oid 18, + typoid = charOid, typcategory = 'S', typdelim = ',', typname = "char" } +charOid :: Oid +charOid = Oid 18 +{-# INLINE charOid #-} + name :: TypeInfo name = Basic { - typoid = Oid 19, + typoid = nameOid, typcategory = 'S', typdelim = ',', typname = "name" } +nameOid :: Oid +nameOid = Oid 19 +{-# INLINE nameOid #-} + int8 :: TypeInfo int8 = Basic { - typoid = Oid 20, + typoid = int8Oid, typcategory = 'N', typdelim = ',', typname = "int8" } +int8Oid :: Oid +int8Oid = Oid 20 +{-# INLINE int8Oid #-} + int2 :: TypeInfo int2 = Basic { - typoid = Oid 21, + typoid = int2Oid, typcategory = 'N', typdelim = ',', typname = "int2" } +int2Oid :: Oid +int2Oid = Oid 21 +{-# INLINE int2Oid #-} + int4 :: TypeInfo int4 = Basic { - typoid = Oid 23, + typoid = int4Oid, typcategory = 'N', typdelim = ',', typname = "int4" } +int4Oid :: Oid +int4Oid = Oid 23 +{-# INLINE int4Oid #-} + regproc :: TypeInfo regproc = Basic { - typoid = Oid 24, + typoid = regprocOid, typcategory = 'N', typdelim = ',', typname = "regproc" } +regprocOid :: Oid +regprocOid = Oid 24 +{-# INLINE regprocOid #-} + text :: TypeInfo text = Basic { - typoid = Oid 25, + typoid = textOid, typcategory = 'S', typdelim = ',', typname = "text" } +textOid :: Oid +textOid = Oid 25 +{-# INLINE textOid #-} + oid :: TypeInfo oid = Basic { - typoid = Oid 26, + typoid = oidOid, typcategory = 'N', typdelim = ',', typname = "oid" } +oidOid :: Oid +oidOid = Oid 26 +{-# INLINE oidOid #-} + tid :: TypeInfo tid = Basic { - typoid = Oid 27, + typoid = tidOid, typcategory = 'U', typdelim = ',', typname = "tid" } +tidOid :: Oid +tidOid = Oid 27 +{-# INLINE tidOid #-} + xid :: TypeInfo xid = Basic { - typoid = Oid 28, + typoid = xidOid, typcategory = 'U', typdelim = ',', typname = "xid" } +xidOid :: Oid +xidOid = Oid 28 +{-# INLINE xidOid #-} + cid :: TypeInfo cid = Basic { - typoid = Oid 29, + typoid = cidOid, typcategory = 'U', typdelim = ',', typname = "cid" } +cidOid :: Oid +cidOid = Oid 29 +{-# INLINE cidOid #-} + xml :: TypeInfo xml = Basic { - typoid = Oid 142, + typoid = xmlOid, typcategory = 'U', typdelim = ',', typname = "xml" } +xmlOid :: Oid +xmlOid = Oid 142 +{-# INLINE xmlOid #-} + point :: TypeInfo point = Basic { - typoid = Oid 600, + typoid = pointOid, typcategory = 'G', typdelim = ',', typname = "point" } +pointOid :: Oid +pointOid = Oid 600 +{-# INLINE pointOid #-} + lseg :: TypeInfo lseg = Basic { - typoid = Oid 601, + typoid = lsegOid, typcategory = 'G', typdelim = ',', typname = "lseg" } +lsegOid :: Oid +lsegOid = Oid 601 +{-# INLINE lsegOid #-} + path :: TypeInfo path = Basic { - typoid = Oid 602, + typoid = pathOid, typcategory = 'G', typdelim = ',', typname = "path" } +pathOid :: Oid +pathOid = Oid 602 +{-# INLINE pathOid #-} + box :: TypeInfo box = Basic { - typoid = Oid 603, + typoid = boxOid, typcategory = 'G', typdelim = ';', typname = "box" } +boxOid :: Oid +boxOid = Oid 603 +{-# INLINE boxOid #-} + polygon :: TypeInfo polygon = Basic { - typoid = Oid 604, + typoid = polygonOid, typcategory = 'G', typdelim = ',', typname = "polygon" } +polygonOid :: Oid +polygonOid = Oid 604 +{-# INLINE polygonOid #-} + line :: TypeInfo line = Basic { - typoid = Oid 628, + typoid = lineOid, typcategory = 'G', typdelim = ',', typname = "line" } +lineOid :: Oid +lineOid = Oid 628 +{-# INLINE lineOid #-} + cidr :: TypeInfo cidr = Basic { - typoid = Oid 650, + typoid = cidrOid, typcategory = 'I', typdelim = ',', typname = "cidr" } +cidrOid :: Oid +cidrOid = Oid 650 +{-# INLINE cidrOid #-} + float4 :: TypeInfo float4 = Basic { - typoid = Oid 700, + typoid = float4Oid, typcategory = 'N', typdelim = ',', typname = "float4" } +float4Oid :: Oid +float4Oid = Oid 700 +{-# INLINE float4Oid #-} + float8 :: TypeInfo float8 = Basic { - typoid = Oid 701, + typoid = float8Oid, typcategory = 'N', typdelim = ',', typname = "float8" } +float8Oid :: Oid +float8Oid = Oid 701 +{-# INLINE float8Oid #-} + unknown :: TypeInfo unknown = Basic { - typoid = Oid 705, + typoid = unknownOid, typcategory = 'X', typdelim = ',', typname = "unknown" } +unknownOid :: Oid +unknownOid = Oid 705 +{-# INLINE unknownOid #-} + circle :: TypeInfo circle = Basic { - typoid = Oid 718, + typoid = circleOid, typcategory = 'G', typdelim = ',', typname = "circle" } +circleOid :: Oid +circleOid = Oid 718 +{-# INLINE circleOid #-} + money :: TypeInfo money = Basic { - typoid = Oid 790, + typoid = moneyOid, typcategory = 'N', typdelim = ',', typname = "money" } +moneyOid :: Oid +moneyOid = Oid 790 +{-# INLINE moneyOid #-} + macaddr :: TypeInfo macaddr = Basic { - typoid = Oid 829, + typoid = macaddrOid, typcategory = 'U', typdelim = ',', typname = "macaddr" } +macaddrOid :: Oid +macaddrOid = Oid 829 +{-# INLINE macaddrOid #-} + inet :: TypeInfo inet = Basic { - typoid = Oid 869, + typoid = inetOid, typcategory = 'I', typdelim = ',', typname = "inet" } +inetOid :: Oid +inetOid = Oid 869 +{-# INLINE inetOid #-} + bpchar :: TypeInfo bpchar = Basic { - typoid = Oid 1042, + typoid = bpcharOid, typcategory = 'S', typdelim = ',', typname = "bpchar" } +bpcharOid :: Oid +bpcharOid = Oid 1042 +{-# INLINE bpcharOid #-} + varchar :: TypeInfo varchar = Basic { - typoid = Oid 1043, + typoid = varcharOid, typcategory = 'S', typdelim = ',', typname = "varchar" } +varcharOid :: Oid +varcharOid = Oid 1043 +{-# INLINE varcharOid #-} + date :: TypeInfo date = Basic { - typoid = Oid 1082, + typoid = dateOid, typcategory = 'D', typdelim = ',', typname = "date" } +dateOid :: Oid +dateOid = Oid 1082 +{-# INLINE dateOid #-} + time :: TypeInfo time = Basic { - typoid = Oid 1083, + typoid = timeOid, typcategory = 'D', typdelim = ',', typname = "time" } +timeOid :: Oid +timeOid = Oid 1083 +{-# INLINE timeOid #-} + timestamp :: TypeInfo timestamp = Basic { - typoid = Oid 1114, + typoid = timestampOid, typcategory = 'D', typdelim = ',', typname = "timestamp" } +timestampOid :: Oid +timestampOid = Oid 1114 +{-# INLINE timestampOid #-} + timestamptz :: TypeInfo timestamptz = Basic { - typoid = Oid 1184, + typoid = timestamptzOid, typcategory = 'D', typdelim = ',', typname = "timestamptz" } +timestamptzOid :: Oid +timestamptzOid = Oid 1184 +{-# INLINE timestamptzOid #-} + interval :: TypeInfo interval = Basic { - typoid = Oid 1186, + typoid = intervalOid, typcategory = 'T', typdelim = ',', typname = "interval" } +intervalOid :: Oid +intervalOid = Oid 1186 +{-# INLINE intervalOid #-} + timetz :: TypeInfo timetz = Basic { - typoid = Oid 1266, + typoid = timetzOid, typcategory = 'D', typdelim = ',', typname = "timetz" } +timetzOid :: Oid +timetzOid = Oid 1266 +{-# INLINE timetzOid #-} + bit :: TypeInfo bit = Basic { - typoid = Oid 1560, + typoid = bitOid, typcategory = 'V', typdelim = ',', typname = "bit" } +bitOid :: Oid +bitOid = Oid 1560 +{-# INLINE bitOid #-} + varbit :: TypeInfo varbit = Basic { - typoid = Oid 1562, + typoid = varbitOid, typcategory = 'V', typdelim = ',', typname = "varbit" } +varbitOid :: Oid +varbitOid = Oid 1562 +{-# INLINE varbitOid #-} + numeric :: TypeInfo numeric = Basic { - typoid = Oid 1700, + typoid = numericOid, typcategory = 'N', typdelim = ',', typname = "numeric" } +numericOid :: Oid +numericOid = Oid 1700 +{-# INLINE numericOid #-} + refcursor :: TypeInfo refcursor = Basic { - typoid = Oid 1790, + typoid = refcursorOid, typcategory = 'U', typdelim = ',', typname = "refcursor" } +refcursorOid :: Oid +refcursorOid = Oid 1790 +{-# INLINE refcursorOid #-} + record :: TypeInfo record = Basic { - typoid = Oid 2249, + typoid = recordOid, typcategory = 'P', typdelim = ',', typname = "record" } +recordOid :: Oid +recordOid = Oid 2249 +{-# INLINE recordOid #-} + void :: TypeInfo void = Basic { - typoid = Oid 2278, + typoid = voidOid, typcategory = 'P', typdelim = ',', typname = "void" } +voidOid :: Oid +voidOid = Oid 2278 +{-# INLINE voidOid #-} + array_record :: TypeInfo array_record = Array { - typoid = Oid 2287, + typoid = array_recordOid, typcategory = 'P', typdelim = ',', typname = "_record", typelem = record } +array_recordOid :: Oid +array_recordOid = Oid 2287 +{-# INLINE array_recordOid #-} + regprocedure :: TypeInfo regprocedure = Basic { - typoid = Oid 2202, + typoid = regprocedureOid, typcategory = 'N', typdelim = ',', typname = "regprocedure" } +regprocedureOid :: Oid +regprocedureOid = Oid 2202 +{-# INLINE regprocedureOid #-} + regoper :: TypeInfo regoper = Basic { - typoid = Oid 2203, + typoid = regoperOid, typcategory = 'N', typdelim = ',', typname = "regoper" } +regoperOid :: Oid +regoperOid = Oid 2203 +{-# INLINE regoperOid #-} + regoperator :: TypeInfo regoperator = Basic { - typoid = Oid 2204, + typoid = regoperatorOid, typcategory = 'N', typdelim = ',', typname = "regoperator" } +regoperatorOid :: Oid +regoperatorOid = Oid 2204 +{-# INLINE regoperatorOid #-} + regclass :: TypeInfo regclass = Basic { - typoid = Oid 2205, + typoid = regclassOid, typcategory = 'N', typdelim = ',', typname = "regclass" } +regclassOid :: Oid +regclassOid = Oid 2205 +{-# INLINE regclassOid #-} + regtype :: TypeInfo regtype = Basic { - typoid = Oid 2206, + typoid = regtypeOid, typcategory = 'N', typdelim = ',', typname = "regtype" } +regtypeOid :: Oid +regtypeOid = Oid 2206 +{-# INLINE regtypeOid #-} + uuid :: TypeInfo uuid = Basic { - typoid = Oid 2950, + typoid = uuidOid, typcategory = 'U', typdelim = ',', typname = "uuid" } +uuidOid :: Oid +uuidOid = Oid 2950 +{-# INLINE uuidOid #-} + json :: TypeInfo json = Basic { - typoid = Oid 114, + typoid = jsonOid, typcategory = 'U', typdelim = ',', typname = "json" } +jsonOid :: Oid +jsonOid = Oid 114 +{-# INLINE jsonOid #-} + jsonb :: TypeInfo jsonb = Basic { - typoid = Oid 3802, + typoid = jsonbOid, typcategory = 'U', typdelim = ',', typname = "jsonb" } +jsonbOid :: Oid +jsonbOid = Oid 3802 +{-# INLINE jsonbOid #-} + int2vector :: TypeInfo int2vector = Array { - typoid = Oid 22, + typoid = int2vectorOid, typcategory = 'A', typdelim = ',', typname = "int2vector", typelem = int2 } +int2vectorOid :: Oid +int2vectorOid = Oid 22 +{-# INLINE int2vectorOid #-} + oidvector :: TypeInfo oidvector = Array { - typoid = Oid 30, + typoid = oidvectorOid, typcategory = 'A', typdelim = ',', typname = "oidvector", typelem = oid } +oidvectorOid :: Oid +oidvectorOid = Oid 30 +{-# INLINE oidvectorOid #-} + array_xml :: TypeInfo array_xml = Array { - typoid = Oid 143, + typoid = array_xmlOid, typcategory = 'A', typdelim = ',', typname = "_xml", typelem = xml } +array_xmlOid :: Oid +array_xmlOid = Oid 143 +{-# INLINE array_xmlOid #-} + array_json :: TypeInfo array_json = Array { - typoid = Oid 199, + typoid = array_jsonOid, typcategory = 'A', typdelim = ',', typname = "_json", typelem = json } +array_jsonOid :: Oid +array_jsonOid = Oid 199 +{-# INLINE array_jsonOid #-} + array_line :: TypeInfo array_line = Array { - typoid = Oid 629, + typoid = array_lineOid, typcategory = 'A', typdelim = ',', typname = "_line", typelem = line } +array_lineOid :: Oid +array_lineOid = Oid 629 +{-# INLINE array_lineOid #-} + array_cidr :: TypeInfo array_cidr = Array { - typoid = Oid 651, + typoid = array_cidrOid, typcategory = 'A', typdelim = ',', typname = "_cidr", typelem = cidr } +array_cidrOid :: Oid +array_cidrOid = Oid 651 +{-# INLINE array_cidrOid #-} + array_circle :: TypeInfo array_circle = Array { - typoid = Oid 719, + typoid = array_circleOid, typcategory = 'A', typdelim = ',', typname = "_circle", typelem = circle } +array_circleOid :: Oid +array_circleOid = Oid 719 +{-# INLINE array_circleOid #-} + array_money :: TypeInfo array_money = Array { - typoid = Oid 791, + typoid = array_moneyOid, typcategory = 'A', typdelim = ',', typname = "_money", typelem = money } +array_moneyOid :: Oid +array_moneyOid = Oid 791 +{-# INLINE array_moneyOid #-} + array_bool :: TypeInfo array_bool = Array { - typoid = Oid 1000, + typoid = array_boolOid, typcategory = 'A', typdelim = ',', typname = "_bool", typelem = bool } +array_boolOid :: Oid +array_boolOid = Oid 1000 +{-# INLINE array_boolOid #-} + array_bytea :: TypeInfo array_bytea = Array { - typoid = Oid 1001, + typoid = array_byteaOid, typcategory = 'A', typdelim = ',', typname = "_bytea", typelem = bytea } +array_byteaOid :: Oid +array_byteaOid = Oid 1001 +{-# INLINE array_byteaOid #-} + array_char :: TypeInfo array_char = Array { - typoid = Oid 1002, + typoid = array_charOid, typcategory = 'A', typdelim = ',', typname = "_char", typelem = char } +array_charOid :: Oid +array_charOid = Oid 1002 +{-# INLINE array_charOid #-} + array_name :: TypeInfo array_name = Array { - typoid = Oid 1003, + typoid = array_nameOid, typcategory = 'A', typdelim = ',', typname = "_name", typelem = name } +array_nameOid :: Oid +array_nameOid = Oid 1003 +{-# INLINE array_nameOid #-} + array_int2 :: TypeInfo array_int2 = Array { - typoid = Oid 1005, + typoid = array_int2Oid, typcategory = 'A', typdelim = ',', typname = "_int2", typelem = int2 } +array_int2Oid :: Oid +array_int2Oid = Oid 1005 +{-# INLINE array_int2Oid #-} + array_int2vector :: TypeInfo array_int2vector = Array { - typoid = Oid 1006, + typoid = array_int2vectorOid, typcategory = 'A', typdelim = ',', typname = "_int2vector", typelem = int2vector } +array_int2vectorOid :: Oid +array_int2vectorOid = Oid 1006 +{-# INLINE array_int2vectorOid #-} + array_int4 :: TypeInfo array_int4 = Array { - typoid = Oid 1007, + typoid = array_int4Oid, typcategory = 'A', typdelim = ',', typname = "_int4", typelem = int4 } +array_int4Oid :: Oid +array_int4Oid = Oid 1007 +{-# INLINE array_int4Oid #-} + array_regproc :: TypeInfo array_regproc = Array { - typoid = Oid 1008, + typoid = array_regprocOid, typcategory = 'A', typdelim = ',', typname = "_regproc", typelem = regproc } +array_regprocOid :: Oid +array_regprocOid = Oid 1008 +{-# INLINE array_regprocOid #-} + array_text :: TypeInfo array_text = Array { - typoid = Oid 1009, + typoid = array_textOid, typcategory = 'A', typdelim = ',', typname = "_text", typelem = text } +array_textOid :: Oid +array_textOid = Oid 1009 +{-# INLINE array_textOid #-} + array_tid :: TypeInfo array_tid = Array { - typoid = Oid 1010, + typoid = array_tidOid, typcategory = 'A', typdelim = ',', typname = "_tid", typelem = tid } +array_tidOid :: Oid +array_tidOid = Oid 1010 +{-# INLINE array_tidOid #-} + array_xid :: TypeInfo array_xid = Array { - typoid = Oid 1011, + typoid = array_xidOid, typcategory = 'A', typdelim = ',', typname = "_xid", typelem = xid } +array_xidOid :: Oid +array_xidOid = Oid 1011 +{-# INLINE array_xidOid #-} + array_cid :: TypeInfo array_cid = Array { - typoid = Oid 1012, + typoid = array_cidOid, typcategory = 'A', typdelim = ',', typname = "_cid", typelem = cid } +array_cidOid :: Oid +array_cidOid = Oid 1012 +{-# INLINE array_cidOid #-} + array_oidvector :: TypeInfo array_oidvector = Array { - typoid = Oid 1013, + typoid = array_oidvectorOid, typcategory = 'A', typdelim = ',', typname = "_oidvector", typelem = oidvector } +array_oidvectorOid :: Oid +array_oidvectorOid = Oid 1013 +{-# INLINE array_oidvectorOid #-} + array_bpchar :: TypeInfo array_bpchar = Array { - typoid = Oid 1014, + typoid = array_bpcharOid, typcategory = 'A', typdelim = ',', typname = "_bpchar", typelem = bpchar } +array_bpcharOid :: Oid +array_bpcharOid = Oid 1014 +{-# INLINE array_bpcharOid #-} + array_varchar :: TypeInfo array_varchar = Array { - typoid = Oid 1015, + typoid = array_varcharOid, typcategory = 'A', typdelim = ',', typname = "_varchar", typelem = varchar } +array_varcharOid :: Oid +array_varcharOid = Oid 1015 +{-# INLINE array_varcharOid #-} + array_int8 :: TypeInfo array_int8 = Array { - typoid = Oid 1016, + typoid = array_int8Oid, typcategory = 'A', typdelim = ',', typname = "_int8", typelem = int8 } +array_int8Oid :: Oid +array_int8Oid = Oid 1016 +{-# INLINE array_int8Oid #-} + array_point :: TypeInfo array_point = Array { - typoid = Oid 1017, + typoid = array_pointOid, typcategory = 'A', typdelim = ',', typname = "_point", typelem = point } +array_pointOid :: Oid +array_pointOid = Oid 1017 +{-# INLINE array_pointOid #-} + array_lseg :: TypeInfo array_lseg = Array { - typoid = Oid 1018, + typoid = array_lsegOid, typcategory = 'A', typdelim = ',', typname = "_lseg", typelem = lseg } +array_lsegOid :: Oid +array_lsegOid = Oid 1018 +{-# INLINE array_lsegOid #-} + array_path :: TypeInfo array_path = Array { - typoid = Oid 1019, + typoid = array_pathOid, typcategory = 'A', typdelim = ',', typname = "_path", typelem = path } +array_pathOid :: Oid +array_pathOid = Oid 1019 +{-# INLINE array_pathOid #-} + array_box :: TypeInfo array_box = Array { - typoid = Oid 1020, + typoid = array_boxOid, typcategory = 'A', typdelim = ';', typname = "_box", typelem = box } +array_boxOid :: Oid +array_boxOid = Oid 1020 +{-# INLINE array_boxOid #-} + array_float4 :: TypeInfo array_float4 = Array { - typoid = Oid 1021, + typoid = array_float4Oid, typcategory = 'A', typdelim = ',', typname = "_float4", typelem = float4 } +array_float4Oid :: Oid +array_float4Oid = Oid 1021 +{-# INLINE array_float4Oid #-} + array_float8 :: TypeInfo array_float8 = Array { - typoid = Oid 1022, + typoid = array_float8Oid, typcategory = 'A', typdelim = ',', typname = "_float8", typelem = float8 } +array_float8Oid :: Oid +array_float8Oid = Oid 1022 +{-# INLINE array_float8Oid #-} + array_polygon :: TypeInfo array_polygon = Array { - typoid = Oid 1027, + typoid = array_polygonOid, typcategory = 'A', typdelim = ',', typname = "_polygon", typelem = polygon } +array_polygonOid :: Oid +array_polygonOid = Oid 1027 +{-# INLINE array_polygonOid #-} + array_oid :: TypeInfo array_oid = Array { - typoid = Oid 1028, + typoid = array_oidOid, typcategory = 'A', typdelim = ',', typname = "_oid", typelem = oid } +array_oidOid :: Oid +array_oidOid = Oid 1028 +{-# INLINE array_oidOid #-} + array_macaddr :: TypeInfo array_macaddr = Array { - typoid = Oid 1040, + typoid = array_macaddrOid, typcategory = 'A', typdelim = ',', typname = "_macaddr", typelem = macaddr } +array_macaddrOid :: Oid +array_macaddrOid = Oid 1040 +{-# INLINE array_macaddrOid #-} + array_inet :: TypeInfo array_inet = Array { - typoid = Oid 1041, + typoid = array_inetOid, typcategory = 'A', typdelim = ',', typname = "_inet", typelem = inet } +array_inetOid :: Oid +array_inetOid = Oid 1041 +{-# INLINE array_inetOid #-} + array_timestamp :: TypeInfo array_timestamp = Array { - typoid = Oid 1115, + typoid = array_timestampOid, typcategory = 'A', typdelim = ',', typname = "_timestamp", typelem = timestamp } +array_timestampOid :: Oid +array_timestampOid = Oid 1115 +{-# INLINE array_timestampOid #-} + array_date :: TypeInfo array_date = Array { - typoid = Oid 1182, + typoid = array_dateOid, typcategory = 'A', typdelim = ',', typname = "_date", typelem = date } +array_dateOid :: Oid +array_dateOid = Oid 1182 +{-# INLINE array_dateOid #-} + array_time :: TypeInfo array_time = Array { - typoid = Oid 1183, + typoid = array_timeOid, typcategory = 'A', typdelim = ',', typname = "_time", typelem = time } +array_timeOid :: Oid +array_timeOid = Oid 1183 +{-# INLINE array_timeOid #-} + array_timestamptz :: TypeInfo array_timestamptz = Array { - typoid = Oid 1185, + typoid = array_timestamptzOid, typcategory = 'A', typdelim = ',', typname = "_timestamptz", typelem = timestamptz } +array_timestamptzOid :: Oid +array_timestamptzOid = Oid 1185 +{-# INLINE array_timestamptzOid #-} + array_interval :: TypeInfo array_interval = Array { - typoid = Oid 1187, + typoid = array_intervalOid, typcategory = 'A', typdelim = ',', typname = "_interval", typelem = interval } +array_intervalOid :: Oid +array_intervalOid = Oid 1187 +{-# INLINE array_intervalOid #-} + array_numeric :: TypeInfo array_numeric = Array { - typoid = Oid 1231, + typoid = array_numericOid, typcategory = 'A', typdelim = ',', typname = "_numeric", typelem = numeric } +array_numericOid :: Oid +array_numericOid = Oid 1231 +{-# INLINE array_numericOid #-} + array_timetz :: TypeInfo array_timetz = Array { - typoid = Oid 1270, + typoid = array_timetzOid, typcategory = 'A', typdelim = ',', typname = "_timetz", typelem = timetz } +array_timetzOid :: Oid +array_timetzOid = Oid 1270 +{-# INLINE array_timetzOid #-} + array_bit :: TypeInfo array_bit = Array { - typoid = Oid 1561, + typoid = array_bitOid, typcategory = 'A', typdelim = ',', typname = "_bit", typelem = bit } +array_bitOid :: Oid +array_bitOid = Oid 1561 +{-# INLINE array_bitOid #-} + array_varbit :: TypeInfo array_varbit = Array { - typoid = Oid 1563, + typoid = array_varbitOid, typcategory = 'A', typdelim = ',', typname = "_varbit", typelem = varbit } +array_varbitOid :: Oid +array_varbitOid = Oid 1563 +{-# INLINE array_varbitOid #-} + array_refcursor :: TypeInfo array_refcursor = Array { - typoid = Oid 2201, + typoid = array_refcursorOid, typcategory = 'A', typdelim = ',', typname = "_refcursor", typelem = refcursor } +array_refcursorOid :: Oid +array_refcursorOid = Oid 2201 +{-# INLINE array_refcursorOid #-} + array_regprocedure :: TypeInfo array_regprocedure = Array { - typoid = Oid 2207, + typoid = array_regprocedureOid, typcategory = 'A', typdelim = ',', typname = "_regprocedure", typelem = regprocedure } +array_regprocedureOid :: Oid +array_regprocedureOid = Oid 2207 +{-# INLINE array_regprocedureOid #-} + array_regoper :: TypeInfo array_regoper = Array { - typoid = Oid 2208, + typoid = array_regoperOid, typcategory = 'A', typdelim = ',', typname = "_regoper", typelem = regoper } +array_regoperOid :: Oid +array_regoperOid = Oid 2208 +{-# INLINE array_regoperOid #-} + array_regoperator :: TypeInfo array_regoperator = Array { - typoid = Oid 2209, + typoid = array_regoperatorOid, typcategory = 'A', typdelim = ',', typname = "_regoperator", typelem = regoperator } +array_regoperatorOid :: Oid +array_regoperatorOid = Oid 2209 +{-# INLINE array_regoperatorOid #-} + array_regclass :: TypeInfo array_regclass = Array { - typoid = Oid 2210, + typoid = array_regclassOid, typcategory = 'A', typdelim = ',', typname = "_regclass", typelem = regclass } +array_regclassOid :: Oid +array_regclassOid = Oid 2210 +{-# INLINE array_regclassOid #-} + array_regtype :: TypeInfo array_regtype = Array { - typoid = Oid 2211, + typoid = array_regtypeOid, typcategory = 'A', typdelim = ',', typname = "_regtype", typelem = regtype } +array_regtypeOid :: Oid +array_regtypeOid = Oid 2211 +{-# INLINE array_regtypeOid #-} + array_uuid :: TypeInfo array_uuid = Array { - typoid = Oid 2951, + typoid = array_uuidOid, typcategory = 'A', typdelim = ',', typname = "_uuid", typelem = uuid } +array_uuidOid :: Oid +array_uuidOid = Oid 2951 +{-# INLINE array_uuidOid #-} + array_jsonb :: TypeInfo array_jsonb = Array { - typoid = Oid 3807, + typoid = array_jsonbOid, typcategory = 'A', typdelim = ',', typname = "_jsonb", typelem = jsonb } +array_jsonbOid :: Oid +array_jsonbOid = Oid 3807 +{-# INLINE array_jsonbOid #-} + int4range :: TypeInfo int4range = Range { - typoid = Oid 3904, + typoid = int4rangeOid, typcategory = 'R', typdelim = ',', typname = "int4range", rngsubtype = int4 } +int4rangeOid :: Oid +int4rangeOid = Oid 3904 +{-# INLINE int4rangeOid #-} + _int4range :: TypeInfo _int4range = Array { - typoid = Oid 3905, + typoid = _int4rangeOid, typcategory = 'A', typdelim = ',', typname = "_int4range", typelem = int4range } +_int4rangeOid :: Oid +_int4rangeOid = Oid 3905 +{-# INLINE _int4rangeOid #-} + numrange :: TypeInfo numrange = Range { - typoid = Oid 3906, + typoid = numrangeOid, typcategory = 'R', typdelim = ',', typname = "numrange", rngsubtype = numeric } +numrangeOid :: Oid +numrangeOid = Oid 3906 +{-# INLINE numrangeOid #-} + _numrange :: TypeInfo _numrange = Array { - typoid = Oid 3907, + typoid = _numrangeOid, typcategory = 'A', typdelim = ',', typname = "_numrange", typelem = numrange } +_numrangeOid :: Oid +_numrangeOid = Oid 3907 +{-# INLINE _numrangeOid #-} + tsrange :: TypeInfo tsrange = Range { - typoid = Oid 3908, + typoid = tsrangeOid, typcategory = 'R', typdelim = ',', typname = "tsrange", rngsubtype = timestamp } +tsrangeOid :: Oid +tsrangeOid = Oid 3908 +{-# INLINE tsrangeOid #-} + _tsrange :: TypeInfo _tsrange = Array { - typoid = Oid 3909, + typoid = _tsrangeOid, typcategory = 'A', typdelim = ',', typname = "_tsrange", typelem = tsrange } +_tsrangeOid :: Oid +_tsrangeOid = Oid 3909 +{-# INLINE _tsrangeOid #-} + tstzrange :: TypeInfo tstzrange = Range { - typoid = Oid 3910, + typoid = tstzrangeOid, typcategory = 'R', typdelim = ',', typname = "tstzrange", rngsubtype = timestamptz } +tstzrangeOid :: Oid +tstzrangeOid = Oid 3910 +{-# INLINE tstzrangeOid #-} + _tstzrange :: TypeInfo _tstzrange = Array { - typoid = Oid 3911, + typoid = _tstzrangeOid, typcategory = 'A', typdelim = ',', typname = "_tstzrange", typelem = tstzrange } +_tstzrangeOid :: Oid +_tstzrangeOid = Oid 3911 +{-# INLINE _tstzrangeOid #-} + daterange :: TypeInfo daterange = Range { - typoid = Oid 3912, + typoid = daterangeOid, typcategory = 'R', typdelim = ',', typname = "daterange", rngsubtype = date } +daterangeOid :: Oid +daterangeOid = Oid 3912 +{-# INLINE daterangeOid #-} + _daterange :: TypeInfo _daterange = Array { - typoid = Oid 3913, + typoid = _daterangeOid, typcategory = 'A', typdelim = ',', typname = "_daterange", typelem = daterange } +_daterangeOid :: Oid +_daterangeOid = Oid 3913 +{-# INLINE _daterangeOid #-} + int8range :: TypeInfo int8range = Range { - typoid = Oid 3926, + typoid = int8rangeOid, typcategory = 'R', typdelim = ',', typname = "int8range", rngsubtype = int8 } +int8rangeOid :: Oid +int8rangeOid = Oid 3926 +{-# INLINE int8rangeOid #-} + _int8range :: TypeInfo _int8range = Array { - typoid = Oid 3927, + typoid = _int8rangeOid, typcategory = 'A', typdelim = ',', typname = "_int8range", typelem = int8range } + +_int8rangeOid :: Oid +_int8rangeOid = Oid 3927 +{-# INLINE _int8rangeOid #-} diff --git a/src/Database/PostgreSQL/Simple/Vector.hs b/src/Database/PostgreSQL/Simple/Vector.hs new file mode 100644 index 00000000..8d17c479 --- /dev/null +++ b/src/Database/PostgreSQL/Simple/Vector.hs @@ -0,0 +1,45 @@ +-- | 'query' variants returning 'V.Vector'. +module Database.PostgreSQL.Simple.Vector where + +import Database.PostgreSQL.Simple (Connection, formatQuery, formatMany) +import Database.PostgreSQL.Simple.FromRow (FromRow(..)) +import Database.PostgreSQL.Simple.ToRow (ToRow(..)) +import Database.PostgreSQL.Simple.Internal (RowParser, exec) +import Database.PostgreSQL.Simple.Internal.PQResultUtils +import Database.PostgreSQL.Simple.Types ( Query (..) ) + +import qualified Data.Vector as V + +-- | Perform a @SELECT@ or other SQL query that is expected to return +-- results. All results are retrieved and converted before this +-- function returns. +query :: (ToRow q, FromRow r) => Connection -> Query -> q -> IO (V.Vector r) +query = queryWith fromRow + +-- | A version of 'query' that does not perform query substitution. +query_ :: (FromRow r) => Connection -> Query -> IO (V.Vector r) +query_ = queryWith_ fromRow + +-- | A version of 'query' taking parser as argument +queryWith :: ToRow q => RowParser r -> Connection -> Query -> q -> IO (V.Vector r) +queryWith parser conn template qs = do + result <- exec conn =<< formatQuery conn template qs + finishQueryWithV parser conn template result + +-- | A version of 'query_' taking parser as argument +queryWith_ :: RowParser r -> Connection -> Query -> IO (V.Vector r) +queryWith_ parser conn q@(Query que) = do + result <- exec conn que + finishQueryWithV parser conn q result + +-- | Execute @INSERT ... RETURNING@, @UPDATE ... RETURNING@, or other SQL +-- query that accepts multi-row input and is expected to return results. +returning :: (ToRow q, FromRow r) => Connection -> Query -> [q] -> IO (V.Vector r) +returning = returningWith fromRow + +-- | A version of 'returning' taking parser as argument +returningWith :: (ToRow q) => RowParser r -> Connection -> Query -> [q] -> IO (V.Vector r) +returningWith _ _ _ [] = return V.empty +returningWith parser conn q qs = do + result <- exec conn =<< formatMany conn q qs + finishQueryWithV parser conn q result diff --git a/src/Database/PostgreSQL/Simple/Vector/Unboxed.hs b/src/Database/PostgreSQL/Simple/Vector/Unboxed.hs new file mode 100644 index 00000000..8e00d7bf --- /dev/null +++ b/src/Database/PostgreSQL/Simple/Vector/Unboxed.hs @@ -0,0 +1,44 @@ +module Database.PostgreSQL.Simple.Vector.Unboxed where + +import Database.PostgreSQL.Simple (Connection, formatQuery, formatMany) +import Database.PostgreSQL.Simple.FromRow (FromRow(..)) +import Database.PostgreSQL.Simple.ToRow (ToRow(..)) +import Database.PostgreSQL.Simple.Internal (RowParser, exec) +import Database.PostgreSQL.Simple.Internal.PQResultUtils +import Database.PostgreSQL.Simple.Types ( Query (..) ) + +import qualified Data.Vector.Unboxed as VU + +-- | Perform a @SELECT@ or other SQL query that is expected to return +-- results. All results are retrieved and converted before this +-- function returns. +query :: (ToRow q, FromRow r, VU.Unbox r) => Connection -> Query -> q -> IO (VU.Vector r) +query = queryWith fromRow + +-- | A version of 'query' that does not perform query substitution. +query_ :: (FromRow r, VU.Unbox r) => Connection -> Query -> IO (VU.Vector r) +query_ = queryWith_ fromRow + +-- | A version of 'query' taking parser as argument +queryWith :: (ToRow q, VU.Unbox r) => RowParser r -> Connection -> Query -> q -> IO (VU.Vector r) +queryWith parser conn template qs = do + result <- exec conn =<< formatQuery conn template qs + finishQueryWithVU parser conn template result + +-- | A version of 'query_' taking parser as argument +queryWith_ :: VU.Unbox r => RowParser r -> Connection -> Query -> IO (VU.Vector r) +queryWith_ parser conn q@(Query que) = do + result <- exec conn que + finishQueryWithVU parser conn q result + +-- | Execute @INSERT ... RETURNING@, @UPDATE ... RETURNING@, or other SQL +-- query that accepts multi-row input and is expected to return results. +returning :: (ToRow q, FromRow r, VU.Unbox r) => Connection -> Query -> [q] -> IO (VU.Vector r) +returning = returningWith fromRow + +-- | A version of 'returning' taking parser as argument +returningWith :: (ToRow q, VU.Unbox r) => RowParser r -> Connection -> Query -> [q] -> IO (VU.Vector r) +returningWith _ _ _ [] = return VU.empty +returningWith parser conn q qs = do + result <- exec conn =<< formatMany conn q qs + finishQueryWithVU parser conn q result diff --git a/test/Inspection.hs b/test/Inspection.hs new file mode 100644 index 00000000..fa42ac64 --- /dev/null +++ b/test/Inspection.hs @@ -0,0 +1,79 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE TemplateHaskell #-} +{-# OPTIONS_GHC -dsuppress-idinfo -dsuppress-coercions -dsuppress-type-applications -dsuppress-module-prefixes -dsuppress-type-signatures #-} +-- {-# OPTIONS_GHC -dsuppress-uniques #-} +{-# OPTIONS_GHC -fplugin=Test.Inspection.Plugin #-} +module Main where + +import Test.Inspection +import Test.Tasty +import Test.Tasty.HUnit + +import qualified Database.PostgreSQL.LibPQ as PQ +import Database.PostgreSQL.Simple.FromField +import Database.PostgreSQL.Simple.TypeInfo as TI +import Database.PostgreSQL.Simple.TypeInfo.Macro +import qualified Database.PostgreSQL.Simple.TypeInfo.Static as TI + +------------------------------------------------------------------------------- +-- Inspection tests +------------------------------------------------------------------------------- + +-- # doesn't work :( +#define TH_MKCOMPATS3(a,b,c) $(mkCompats [TI.a,TI.b,TI.c]) +#define IN_MKCOMPATS3(a,b,c) (eq TI.a \/ eq TI.b \/ eq TI.c) + +#define TH_INLINETYPOID(n) eq $(inlineTypoid TI.n) +#define IN_INLINETYPOID(n) eq TI.n + +-- eta-expansion is required +lhs01, rhs01 :: PQ.Oid -> Bool +lhs01 = TH_MKCOMPATS3(name,text,char) +rhs01 = IN_MKCOMPATS3(nameOid,textOid,charOid) + +lhs02, rhs02 :: PQ.Oid -> Bool +lhs02 = TH_INLINETYPOID(name) +rhs02 = IN_INLINETYPOID(nameOid) + +eq :: PQ.Oid -> PQ.Oid -> Bool +eq = (==) +{-# INLINE eq #-} + +infixr 2 \/ +(\/) :: (PQ.Oid -> Bool) + -> (PQ.Oid -> Bool) + -> (PQ.Oid -> Bool) +f \/ g = \x -> f x || g x +{-# INLINE (\/) #-} + +inspectionTests :: TestTree +inspectionTests = testGroup "inspection" + [ testCase "mkCompats" $ + assertSuccess $(inspectTest $ 'lhs01 === 'rhs01) + + -- byteaOid isn't inlined? + , testCase "inlineTypoid" $ +#if __GLASGOW_HASKELL__ >= 808 + assertSuccess +#else + assertFailure' +#endif + $(inspectTest $ 'lhs02 ==- 'rhs02) + ] + +assertSuccess :: Result -> IO () +assertSuccess (Success _) = return () +assertSuccess (Failure err) = assertFailure err + +assertFailure' :: Result -> IO () +assertFailure' (Success err) = assertFailure err +assertFailure' (Failure _) = return () + +------------------------------------------------------------------------------- +-- Main +------------------------------------------------------------------------------- + +main :: IO () +main = defaultMain $ testGroup "tests" + [ inspectionTests + ] diff --git a/test/Main.hs b/test/Main.hs index 963ca785..bc43e0f1 100644 --- a/test/Main.hs +++ b/test/Main.hs @@ -4,18 +4,21 @@ {-# LANGUAGE DoAndIfThenElse #-} {-# LANGUAGE ScopedTypeVariables #-} import Common +import Database.PostgreSQL.Simple.Copy import Database.PostgreSQL.Simple.FromField (FromField) -import Database.PostgreSQL.Simple.Types(Query(..),Values(..)) import Database.PostgreSQL.Simple.HStore -import Database.PostgreSQL.Simple.Copy +import Database.PostgreSQL.Simple.Internal (breakOnSingleQuestionMark) +import Database.PostgreSQL.Simple.Types(Query(..),Values(..), PGArray(..)) import qualified Database.PostgreSQL.Simple.Transaction as ST import Control.Applicative import Control.Exception as E import Control.Monad import Data.Char -import Data.List (sort) +import Data.List (concat, sort) import Data.IORef +import Data.Monoid ((<>)) +import Data.String (fromString) import Data.Typeable import GHC.Generics (Generic) @@ -44,26 +47,27 @@ tests :: TestEnv -> TestTree tests env = testGroup "tests" $ map ($ env) [ testBytea - , testCase "ExecuteMany" . testExecuteMany - , testCase "Fold" . testFold - , testCase "Notify" . testNotify - , testCase "Serializable" . testSerializable - , testCase "Time" . testTime - , testCase "Array" . testArray - , testCase "Array of nullables" . testNullableArray - , testCase "HStore" . testHStore - , testCase "citext" . testCIText - , testCase "JSON" . testJSON - , testCase "Savepoint" . testSavepoint - , testCase "Unicode" . testUnicode - , testCase "Values" . testValues - , testCase "Copy" . testCopy + , testCase "ExecuteMany" . testExecuteMany + , testCase "Fold" . testFold + , testCase "Notify" . testNotify + , testCase "Serializable" . testSerializable + , testCase "Time" . testTime + , testCase "Array" . testArray + , testCase "Array of nullables" . testNullableArray + , testCase "HStore" . testHStore + , testCase "citext" . testCIText + , testCase "JSON" . testJSON + , testCase "Question mark escape" . testQM + , testCase "Savepoint" . testSavepoint + , testCase "Unicode" . testUnicode + , testCase "Values" . testValues + , testCase "Copy" . testCopy , testCopyFailures - , testCase "Double" . testDouble - , testCase "1-ary generic" . testGeneric1 - , testCase "2-ary generic" . testGeneric2 - , testCase "3-ary generic" . testGeneric3 - , testCase "Timeout" . testTimeout + , testCase "Double" . testDouble + , testCase "1-ary generic" . testGeneric1 + , testCase "2-ary generic" . testGeneric2 + , testCase "3-ary generic" . testGeneric3 + , testCase "Timeout" . testTimeout ] testBytea :: TestEnv -> TestTree @@ -234,6 +238,56 @@ testJSON TestEnv{..} = do js' <- query conn "SELECT ?::json" js [js] @?= js' +testQM :: TestEnv -> Assertion +testQM TestEnv{..} = do + -- Just test on a single string + let testQuery' b = "testing for ?" <> b <> " and making sure " + testQueryDoubleQM = testQuery' "?" + testQueryRest = "? is substituted" + testQuery = fromString $ testQueryDoubleQM <> testQueryRest + -- expect the entire first part with double QMs replaced with literal '?' + expected = (fromString $ testQuery' "", fromString testQueryRest) + tried = breakOnSingleQuestionMark testQuery + errMsg = concat + [ "Failed to break on single question mark exclusively:\n" + , "expected: ", show expected + , "result: ", show tried + ] + assertBool errMsg $ tried == expected + + -- Let's also test the question mark operators in action + -- ? -> Does the string exist as a top-level key within the JSON value? + positiveQuery "SELECT ?::jsonb ?? ?" (testObj, "foo" :: Text) + negativeQuery "SELECT ?::jsonb ?? ?" (testObj, "baz" :: Text) + negativeQuery "SELECT ?::jsonb ?? ?" (toJSON numArray, "1" :: Text) + -- ?| -> Do any of these array strings exist as top-level keys? + positiveQuery "SELECT ?::jsonb ??| ?" (testObj, PGArray ["nope","bar","6" :: Text]) + negativeQuery "SELECT ?::jsonb ??| ?" (testObj, PGArray ["nope","6" :: Text]) + negativeQuery "SELECT ?::jsonb ??| ?" (toJSON numArray, PGArray ["1","2","6" :: Text]) + -- ?& -> Do all of these array strings exist as top-level keys? + positiveQuery "SELECT ?::jsonb ??& ?" (testObj, PGArray ["foo","bar","quux" :: Text]) + positiveQuery "SELECT ?::jsonb ??& ?" (testObj, PGArray ["foo","bar" :: Text]) + negativeQuery "SELECT ?::jsonb ??& ?" (testObj, PGArray ["foo","bar","baz" :: Text]) + negativeQuery "SELECT ?::jsonb ??& ?" (toJSON numArray, PGArray ["1","2","3","4","5" :: Text]) + -- Format error for 2 question marks, not 4 + True <- expectError (isFormatError 2) $ + (query conn "SELECT ?::jsonb ?? ?" $ Only testObj :: IO [Only Bool]) + return () + where positiveQuery :: ToRow a => Query -> a -> Assertion + positiveQuery = boolQuery True + negativeQuery :: ToRow a => Query -> a -> Assertion + negativeQuery = boolQuery False + numArray :: [Int] + numArray = [1,2,3,4,5] + boolQuery :: ToRow a => Bool -> Query -> a -> Assertion + boolQuery b t x = do + a <- query conn t x + [Only b] @?= a + testObj = toJSON (Map.fromList [("foo",toJSON (1 :: Int)) + ,("bar",String "baz") + ,("quux",toJSON [1 :: Int,2,3,4,5])] :: Map Text Value + ) + testSavepoint :: TestEnv -> Assertion testSavepoint TestEnv{..} = do True <- expectError ST.isNoActiveTransactionError $ @@ -478,6 +532,14 @@ isUniqueViolation SqlError{..} = sqlState == "23505" isSyntaxError :: SqlError -> Bool isSyntaxError SqlError{..} = sqlState == "42601" +isFormatError :: Int -> FormatError -> Bool +isFormatError i FormatError{..} + | null fmtMessage = False + | otherwise = fmtMessage == concat [ show i + , " single '?' characters, but " + , show (length fmtParams) + , " parameters" + ] ------------------------------------------------------------------------ -- | Action for connecting to the database that will be used for testing. diff --git a/tools/GenTypeInfo.hs b/tools/GenTypeInfo.hs index 9af4c05f..5164478e 100644 --- a/tools/GenTypeInfo.hs +++ b/tools/GenTypeInfo.hs @@ -21,6 +21,9 @@ -- know about. It then constructs a TypeInfo record and stores it in -- a per-connection cache for later use. -- +-- @ +-- runghc -itools tools/GenTypeInfo.hs +-- @ ------------------------------------------------------------------------------ {-# LANGUAGE OverloadedStrings, QuasiQuotes, ViewPatterns, RecordWildCards #-} @@ -38,12 +41,10 @@ import Database.PostgreSQL.Simple.FromRow import Database.PostgreSQL.Simple.Ok import Database.PostgreSQL.Simple.Types(Oid(..)) import Database.PostgreSQL.Simple.SqlQQ -import qualified Data.ByteString.Char8 as B +import qualified Data.ByteString.Char8 as B8 import Data.ByteString(ByteString) import qualified Data.ByteString.Lazy as L -import qualified Blaze.ByteString.Builder as Blaze -import qualified Blaze.ByteString.Builder.ByteString as Blaze -import qualified Blaze.ByteString.Builder.Char8 as Blaze +import qualified Data.ByteString.Builder as Blaze import Data.String import Data.List ( sort, intersperse ) import qualified Data.Map as Map @@ -69,11 +70,11 @@ data TypeInfo = TypeInfo instance FromRow TypeInfo where fromRow = TypeInfo <$> field <*> field <*> field <*> field <*> field <*> field -type NameMap = Map.Map B.ByteString TypeInfo +type NameMap = Map.Map B8.ByteString TypeInfo type OidMap = Map.Map Oid TypeInfo -type TypeName = (B.ByteString, B.ByteString) +type TypeName = (B8.ByteString, B8.ByteString) type TypeNames = [TypeName] @@ -197,9 +198,6 @@ int8range _int8range |] -instance IsString Blaze.Builder where - fromString = Blaze.fromByteString . fromString - connectionString = "dbname=postgres" withPostgreSQL = bracket (connectPostgreSQL connectionString) close @@ -237,7 +235,7 @@ getTypeInfos typnames = withPostgreSQL $ \conn -> do main = do (oidmap, namemap) <- getTypeInfos typeNames - L.writeFile "../src/Database/PostgreSQL/Simple/TypeInfo/Static.hs" + L.writeFile "src/Database/PostgreSQL/Simple/TypeInfo/Static.hs" (Blaze.toLazyByteString (renderFile oidmap namemap typeNames)) @@ -246,7 +244,7 @@ showOid (Oid n) = show n renderOid :: NameMap -> TypeName -> Blaze.Builder renderOid byName name = case Map.lookup (pg name) byName of - Nothing -> error (B.unpack (pg name)) + Nothing -> error (B8.unpack (pg name)) Just (showOid . typoid -> n) -> fromString n ++ fromString (replicate (4 - length n) ' ') @@ -264,20 +262,25 @@ renderTypeInfo byOid info name typelem_hs_name = case lookup (typname typelem_info) typeNames of Nothing -> error ( "type not found: " - ++ B.unpack( typname typelem_info) - ++ " (typelem of " ++ B.unpack (typname info) + ++ B8.unpack( typname typelem_info) + ++ " (typelem of " ++ B8.unpack (typname info) ++ ")") Just x -> x in concat [ "\n" , bs (hs name), " :: TypeInfo\n" , bs (hs name), " = Array {\n" - , " typoid = ", fromString (show (typoid info)), ",\n" - , " typcategory = '", Blaze.fromChar (typcategory info), "',\n" - , " typdelim = '", Blaze.fromChar (typdelim info), "',\n" + , " typoid = ", bs (hs name), "Oid,\n" + , " typcategory = '", bs $ B8.singleton (typcategory info), "',\n" + , " typdelim = '", bs $ B8.singleton (typdelim info), "',\n" , " typname = \"", bs (typname info), "\",\n" , " typelem = ", bs typelem_hs_name, "\n" , " }\n" + , "\n" + , bs (hs name), "Oid :: Oid\n" + , bs (hs name), "Oid = ", fromString (show (typoid info)), "\n" + , "{-# INLINE ", bs (hs name), "Oid #-}" + , "\n" ] | typcategory info == 'R' = let (Just rngsubtype_oid) = rngsubtype info @@ -285,31 +288,41 @@ renderTypeInfo byOid info name rngsubtype_hs_name = case lookup (typname rngsubtype_info) typeNames of Nothing -> error ( "type not found: " - ++ B.unpack (typname rngsubtype_info) + ++ B8.unpack (typname rngsubtype_info) ++ " (rngsubtype of " - ++ B.unpack (typname info) ++ ")") + ++ B8.unpack (typname info) ++ ")") Just x -> x in concat [ "\n" , bs (hs name), " :: TypeInfo\n" , bs (hs name), " = Range {\n" - , " typoid = ", fromString (show (typoid info)), ",\n" - , " typcategory = '", Blaze.fromChar (typcategory info), "',\n" - , " typdelim = '", Blaze.fromChar (typdelim info), "',\n" + , " typoid = ", bs (hs name), "Oid,\n" + , " typcategory = '", bs $ B8.singleton (typcategory info), "',\n" + , " typdelim = '", bs $ B8.singleton (typdelim info), "',\n" , " typname = \"", bs (typname info), "\",\n" , " rngsubtype = ", bs rngsubtype_hs_name, "\n" , " }\n" + , "\n" + , bs (hs name), "Oid :: Oid\n" + , bs (hs name), "Oid = ", fromString (show (typoid info)), "\n" + , "{-# INLINE ", bs (hs name), "Oid #-}" + , "\n" ] | otherwise = concat [ "\n" , bs (hs name), " :: TypeInfo\n" , bs (hs name), " = Basic {\n" - , " typoid = ", fromString (show (typoid info)), ",\n" - , " typcategory = '", Blaze.fromChar (typcategory info), "',\n" - , " typdelim = '", Blaze.fromChar (typdelim info), "',\n" + , " typoid = ", bs (hs name), "Oid,\n" + , " typcategory = '", bs $ B8.singleton (typcategory info), "',\n" + , " typdelim = '", bs $ B8.singleton (typdelim info), "',\n" , " typname = \"", bs (typname info), "\"\n" , " }\n" + , "\n" + , bs (hs name), "Oid :: Oid\n" + , bs (hs name), "Oid = ", fromString (show (typoid info)), "\n" + , "{-# INLINE ", bs (hs name), "Oid #-}" + , "\n" ] -- FIXME: add in any names that we need that we didn't specify, (i.e. @@ -318,7 +331,7 @@ renderTypeInfo byOid info name getNames :: NameMap -> TypeNames -> TypeNames getNames _ x = x -bs = Blaze.fromByteString +bs = Blaze.byteString pg = fst @@ -347,6 +360,7 @@ module Database.PostgreSQL.Simple.TypeInfo.Static ( TypeInfo(..) , staticTypeInfo |] ++ concat [ " , " ++ bs (hs name) ++ "\n" + ++ " , " ++ bs (hs name) ++ "Oid\n" | name <- names ] ++ [longstring| ) where